marvisx-cli 0.2.0__tar.gz → 0.2.2__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.
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/PKG-INFO +6 -4
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/rbac.py +8 -2
- marvisx_cli-0.2.2/core/api/tests/test_require_scope_empty_deny.py +65 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_safety_bridge.py +57 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_feedback.py +2 -2
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_init.py +12 -5
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/safety_bridge.py +11 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/client.py +1 -1
- marvisx_cli-0.2.2/core/wizard/byok_vault.py +384 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/PKG-INFO +6 -4
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/SOURCES.txt +3 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/requires.txt +3 -0
- marvisx_cli-0.2.2/migrations/145_audit_log_immutable.sql +23 -0
- marvisx_cli-0.2.2/migrations/145_audit_log_immutable_down.sql +4 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/pyproject.toml +9 -4
- marvisx_cli-0.2.0/core/wizard/byok_vault.py +0 -147
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/LICENSE +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/MANIFEST.in +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/README.md +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/agents/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/agents/session_health.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/agents/session_manager.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/bin/marvisx-state-hook.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/config.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/db.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/dependencies/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/dependencies/tenant.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/main.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/_adapter.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/server.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/brain.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/graph.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/handoffs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/ingest.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/learnings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/projects.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/pull_requests.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/safety.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/mcp/tools/tasks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/middleware/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/middleware/tool_call_audit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/auth.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/brain.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/common.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/costs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/graph.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/graph_cosmo.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/graph_pr_impact.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/graph_ux.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/inbox.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/ingest_keys.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/kg.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/llm_config.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/monitoring.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/projects.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/sessions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/tasks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/teams.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/models/users.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/observability/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/observability/tracing.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/paths.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/rate_limit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/_adapter.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/admin_pr_impact.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/admin_settings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/agent.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/agent_tokens.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/app_settings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/audit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/auth.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/bench.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/brain.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/brain_directions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/ci_checks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/comments.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/costs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/docs_coverage.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/docs_governance.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/documents.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/files.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/finder.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/graph.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/handoffs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/inbox.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/ingest_api_keys.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/ingest_triage.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/judge.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/kg.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/learnings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/llm_config.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/monitoring.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/notifications.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/pr_impact.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/projects.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/pull_requests.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/push.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/raci.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/sessions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/settings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/share_repo.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/status_updates.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/tags.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/tasks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/teams.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/terminal.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/users.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/routers/webhooks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/runtime_settings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/security.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/audit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/auto_approval.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/baseline.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/capabilities.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/cascade_rollup.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/compound_bridge.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/cycle.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/cycle_snapshot.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/digest_collector.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/direction.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/drift.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/drift_router.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/edge_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/events_reader.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/findings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/findings_reader.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/jobs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/journal.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/knowledge_forms.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/_runner.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/base.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/cache.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/constants.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/direction_alignment.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/factory.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/finding_reasoning.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/finding_summary.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/grounding.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/journal_polish.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/local_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/parsers.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/llm/router_glue.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/memory_ops.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/models.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/owner_hint.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/recap.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/_signals.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr1_activity_without_status.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr2_decision_without_adr.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr3_stale_open_loop.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr4_docs_governance_drift.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr5_playbook_changed.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr6_external_update_unpropagated.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr7_claimed_decision_gap.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/rules/dr8_direction_misalignment.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/runs_reader.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/scope.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/base.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/git_kg.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/handoffs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/ingestor.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/learnings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/sources/pir_tasks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/watermarks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/brain/ws_emitter.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/cc_tasks_reader.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ci_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/claude_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/codex_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/conversation_reader.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/cost_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/crypto.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/confidence.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/config.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/enrichment.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/frontmatter_validator.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/hard_gates.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/docs_governance/triage_orchestrator.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/embedding_internal.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/embedding_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/event_dispatcher.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/events.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/git_ops.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/graph_cosmo_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/graph_ranker.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/graph_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_digest.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_digest_deep_research.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_digest_jobs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_gmail_sync.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_llm_classifier.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_source_identity.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_sources.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_taxonomy.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_tldr.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/inbox_triage.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/api_key_auth.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/auto_approve.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/classifier.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/confidence.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/dispatch.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/embedding_router.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/events.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/ignore_patterns.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/image_probe.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/ingress.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/insert_saga.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/anthropic_haiku.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/base.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/byok_provider.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/classification_context.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/config_store.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/factory.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/kg_enricher.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/local_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/local_vllm.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/llm/openai_nano.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/lock_advisory.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parser_router.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/docling_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/docparse_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/docx_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/folder_unpacker.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/gateway_aux.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/image_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/internal_markdown.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/ocr_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/ocr_pdf_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/pdf_types.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/transcript_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/vision_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/xlsx_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/parsers/zip_unpacker.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/preflight.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/retry_voyage.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/routing_policy.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/serializers/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/serializers/xlsx_to_markdown.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/skip_log.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/ingest/watcher.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/audit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/hybrid_search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/lens.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/pr_impact.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/queries.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/ranking.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg/rrf.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/kg_watcher_control.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/local_llm/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/local_llm/async_client.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/local_llm/client.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/local_llm/url_validator.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/metrics_collector.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/metrics_providers.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/model_registry.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/model_router.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/n8n_client.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/newsletter_llm_gateway.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/notification_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/openai_responses.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/opencode_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/opencode_sessions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pii_redactor.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/differ.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/dispatcher.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/gc.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/languages.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_impact_pipeline/writer.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/pr_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/project_paths.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/project_status_updates.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/providers.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/push_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/reminder_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/runas.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/salience_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/security_collector.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/session_catalog.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/session_metrics_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/session_ops.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/session_state.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/share_links.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/task_transitions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/terminal_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/terminal_metrics_dump.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/tmux.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/webhook_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/services/workspace_sync.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/templates/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/templates/markdown_share.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/terminal.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_agent_facing_auth_dependencies.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_audit_permissions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_backfill_session_conversations.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_backfill_working_seconds_msg.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_claude_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_codex_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_finder_paths.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_git_ops_merge.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_learnings_check_search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_metrics_providers.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_migration_087.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_migration_088.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_migration_089.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_openai_responses.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_opencode_metrics.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_opencode_sessions.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_pr_workflow_e2e.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_projects_handoffs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_providers.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_session_catalog.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_session_conversations.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_session_metrics_service.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_session_resume_paths.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_session_theme_mode_migration.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_sessions_rbac.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_share_edit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_share_repo.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_terminal_session_manager.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_terminal_upload.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_tmux.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_workspace_sync.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/tests/test_ws_ticket_in_memory.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/_context.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/_errors.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/_roles.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/audit.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/brain.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/costs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/graph.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/handoffs.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/ingest_triage.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/learnings.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/projects.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/pull_requests.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/search.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/use_cases/tasks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/api/visibility.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/README.md +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/_index_source.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/_runtime_ctx.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/_transmute.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_account.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_doctor.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_governance.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_hooks.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_mcp.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_runtime.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/cli/marvis_telemetry.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/_drift_check.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/_frontmatter.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/_graph_writer.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/ast_parser.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/_config.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/block-dangerous-bash.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/block-db-direct-write.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/block-push-no-task.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/block-staging-to-prod.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/block-subtree-push.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/config.json +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/enforce-no-merge-main.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/enforce-worktree.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/quality-gate.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/safety_bridge.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/install_hooks/secret-scan.sh +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/migrate_spike_node_ids.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_artifacts.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_cross_project.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_inbox_nodes.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_pr_impact.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_project_nodes.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/populate_touch_counter.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/scripts/reparse_failed.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/entitlements.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/rollup.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/schema.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/telemetry/sender.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/wizard/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/wizard/defaults.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/wizard/state.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/wizard/steps.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/core/wizard/validation.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/dependency_links.txt +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/entry_points.txt +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/marvisx_cli.egg-info/top_level.txt +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/001_initial.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/002_tasks.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/003_session_management.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/004_projects_comments.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/005_session_intelligence.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/006_settings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/007_task_scoring.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/008_cost_tracking.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/009_session_card_metrics.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/010_monitoring.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/012_agent_api.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/013_session_complete.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/015_pull_requests.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/015_pull_requests_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/016_users_raci.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/017_task_cost_entries.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/018_agents.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/018_agents_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/019_review_feedback.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/020_pr_commit_sha.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/021_webhook_events.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/022_devx_agent_managed.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/022_devx_agent_managed_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/023_devx_p1_gate.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/023_devx_p1_gate_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/024_chat_messages.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/024_pr_conversation_id.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/024_task_indexes.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/024_task_indexes_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/025_audit_log.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/026_agent_tokens.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/027_teams_auth_phase_b.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/028_learnings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/029_team_roles.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/030_finder_pins.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/031_pr_deploy_status.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/032_task_reminders.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/033_events_retry_count.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/033_session_owner.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/034_notifications.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/035_shared_links.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/036_session_index_upgrade.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/037_pr_approval.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/038_pr_submitted_by.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/039_push_subscriptions.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/040_semantic_search.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/041_workspaces.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/042_oidc_providers.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/043_ci_checks.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/044_agent_metrics.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/045_documents_doc_type.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/046_salience.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/047_seed_missing_agents.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/048_fix_agent_paths_roles.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/049_agent_role_and_learnings_schema.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/050_session_provider.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/051_session_launch_profile.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/052_session_theme_mode.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/052_task_kind.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/053_inbox_items.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/054_inbox_triage_contract.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/055_inbox_topic_treatment.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/056_inbox_treatment_read_save.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/057_session_theme_mode_backfill.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/058_inbox_item_status_lifecycle.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/059_inbox_tldr_and_source_scores.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/060_newsletter.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/061_inbox_redesign.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/062_fix_inbox_sources_backfill.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/063_task_completion_mode.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/064_judge_mode_setting.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/065_knowledge_graph_spike.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/066_digest_ranking_inputs.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/066_kg_artifact_nodes.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/067_inbox_digest_selections.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/067_kg_temporal.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/068_inbox_digest_app_settings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/068_kg_touch_counter.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/069_kg_doc_types.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/070_digest_ranking_inputs_recovery.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/071_inbox_digest_selections_recovery.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/072_inbox_digest_app_settings_recovery.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/073_kg_cross_project.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/073_kg_cross_project_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/074_kg_infra_types.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/074_kg_infra_types_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/075_kg_file_state_recovery.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/075_kg_file_state_recovery_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/076_kg_watcher_state.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/076_kg_watcher_state_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/077_kg_doc_types_extend.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/077_kg_doc_types_extend_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/078_kg_fts5.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/078_kg_fts5_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/079_kg_missing_indexes.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/079_kg_missing_indexes_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/080_kg_fts5_extended.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/080_kg_fts5_extended_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/081_kg_lens_indexes.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/081_kg_lens_indexes_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/082_kg_pins.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/082_kg_pins_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/083_kg_graph_nodes_degree.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/083_kg_graph_nodes_degree_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/084_drop_legacy_scheduler_tables.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/084_drop_legacy_scheduler_tables_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/085_kg_edge_resolves_to.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/085_kg_edge_resolves_to_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/086_project_status_updates_feed.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/086_project_status_updates_feed_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/087_session_metrics_dual.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/087_session_metrics_dual_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/088_rename_context_pct_legacy.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/088_rename_context_pct_legacy_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/089_session_metrics_equivalent_cost.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/089_session_metrics_equivalent_cost_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/090_kg_inbox_node_type.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/090_kg_inbox_node_type_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/091_kg_inbox_node_type_check.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/091_kg_inbox_node_type_check_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/092_sessions_activity_state_ts.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/092_sessions_activity_state_ts_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/093_sessions_activity_state_column.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/093_sessions_activity_state_column_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/094_ingest_pending.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/094_ingest_pending_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/095_kg_intent_first.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/095_kg_intent_first_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/096_kg_xlsx_artifact_prefix.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/096_kg_xlsx_artifact_prefix_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/097_ingest_change_history.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/097_ingest_change_history_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/098_kg_node_type_business.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/098_kg_node_type_business_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/099_kg_edges_restore_weight.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/099_kg_edges_restore_weight_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/100_kg_enriched_at.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/100_kg_enriched_at_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/101_local_llm_shadow_comparisons.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/101_local_llm_shadow_comparisons_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/102_promote_llm_costs.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/102_promote_llm_costs_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/103_ingest_skipped_log.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/103_ingest_skipped_log_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/120_docs_governance.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/120_docs_governance_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/121_notification_event_fk_cleanup.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/121_notification_event_fk_cleanup_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/122_docs_drift_history.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/122_docs_drift_history_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/123_ingest_parser_waiting_status.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/123_ingest_parser_waiting_status_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/124_heypocket_recordings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/124_heypocket_recordings_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/125_kg_node_type_record.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/125_kg_node_type_record_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/126_ingest_terminal_upload_source_kind.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/126_ingest_terminal_upload_source_kind_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/127_brain_v1_substrate.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/127_brain_v1_substrate_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/128_brain_drift_signals.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/128_brain_drift_signals_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/129_brain_memory_operations.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/129_brain_memory_operations_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/130_brain_findings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/130_brain_findings_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/132_kg_pr_modifies.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/132_kg_pr_modifies_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/133_brain_v1_2_direction_schema.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/133_brain_v1_2_direction_schema_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/134_brain_journal_narrative_polished.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/134_brain_journal_narrative_polished_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/135_kg_edges_provider.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/136_documents_fts.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/137_promote_llm_costs.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/137_promote_llm_costs_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/138_ingest_api_keys.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/138_ingest_api_keys_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/139_ingest_pending_ingress.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/139_ingest_pending_ingress_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/140_ingest_idempotency_quota.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/140_ingest_idempotency_quota_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/141_ingest_pending_metadata.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/141_ingest_pending_metadata_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/142_llm_function_config.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/142_llm_function_config_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/143_kg_code_embeddings.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/143_kg_code_embeddings_down.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/144_notifications_pending_sync_index.sql +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/migrations/__init__.py +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/projects/_template/project.yaml +0 -0
- {marvisx_cli-0.2.0 → marvisx_cli-0.2.2}/setup.cfg +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marvisx-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: MarvisX OSS — agent-native company-brain CLI runtime (init, status, brief, triage)
|
|
5
5
|
Author: MarvisX maintainers
|
|
6
6
|
License: BSL-1.1
|
|
7
7
|
Project-URL: Homepage, https://justaskmarvis.com
|
|
8
8
|
Project-URL: Documentation, https://justaskmarvis.com/docs/getting-started
|
|
9
|
-
Project-URL: Source, https://github.com/
|
|
10
|
-
Project-URL: Issues, https://github.com/
|
|
11
|
-
Project-URL: Changelog, https://github.com/
|
|
9
|
+
Project-URL: Source, https://github.com/emiliomartucci/marvisx-oss
|
|
10
|
+
Project-URL: Issues, https://github.com/emiliomartucci/marvisx-oss/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/emiliomartucci/marvisx-oss/blob/main/CHANGELOG.md
|
|
12
12
|
Keywords: marvisx,cli,bootstrap,wizard,oss,agent,company-brain
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
@@ -72,6 +72,8 @@ Requires-Dist: sqlite-vec>=0.1.6
|
|
|
72
72
|
Provides-Extra: dev
|
|
73
73
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
74
74
|
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
75
|
+
Provides-Extra: keyring
|
|
76
|
+
Requires-Dist: keyring>=24; extra == "keyring"
|
|
75
77
|
Dynamic: license-file
|
|
76
78
|
|
|
77
79
|
# marvis init
|
|
@@ -80,9 +80,15 @@ def require_scope(*required_scopes: str) -> Callable[..., Awaitable[UserInfo]]:
|
|
|
80
80
|
if not user.username.startswith("agent:") and user.username not in agent_usernames:
|
|
81
81
|
return user
|
|
82
82
|
|
|
83
|
-
# Agent with no scopes
|
|
83
|
+
# Agent with no scopes (empty/null) = NO permission for scope-gated ops.
|
|
84
|
+
# Empty MUST deny, not allow-all: an unscoped agent token previously
|
|
85
|
+
# passed every scope check (authz bypass — empty list satisfied
|
|
86
|
+
# `not user.scopes`). Scope-gated endpoints require an explicit grant.
|
|
84
87
|
if not user.scopes:
|
|
85
|
-
|
|
88
|
+
raise HTTPException(
|
|
89
|
+
status_code=403,
|
|
90
|
+
detail=f"Token missing required scopes: {list(required_scopes)}",
|
|
91
|
+
)
|
|
86
92
|
|
|
87
93
|
# Check that all required scopes are present
|
|
88
94
|
missing = [s for s in required_scopes if s not in user.scopes]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Regression: require_scope must DENY agent tokens with empty/null scopes.
|
|
2
|
+
|
|
3
|
+
Audit S4: an agent token with scopes=[] previously satisfied `not user.scopes`
|
|
4
|
+
and was treated as unrestricted (authz bypass — a scope-gated PATCH succeeded
|
|
5
|
+
with an empty-scope token). Empty scopes must grant NO permission.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from fastapi import HTTPException
|
|
11
|
+
|
|
12
|
+
from core.api.models.auth import UserInfo
|
|
13
|
+
from core.api.rbac import require_scope
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _agent(scopes: list[str]) -> UserInfo:
|
|
17
|
+
# "agent:" prefix forces the agent path (not the human cookie bypass).
|
|
18
|
+
return UserInfo(
|
|
19
|
+
username="agent:tester",
|
|
20
|
+
user_id="tok-1",
|
|
21
|
+
system_role="operator",
|
|
22
|
+
user_type="agent",
|
|
23
|
+
scopes=scopes,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _human(scopes: list[str]) -> UserInfo:
|
|
28
|
+
return UserInfo(
|
|
29
|
+
username="emilio",
|
|
30
|
+
user_id="user-1",
|
|
31
|
+
system_role="operator",
|
|
32
|
+
user_type="human",
|
|
33
|
+
scopes=scopes,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.asyncio
|
|
38
|
+
async def test_empty_scopes_agent_denied():
|
|
39
|
+
check = require_scope("write")
|
|
40
|
+
with pytest.raises(HTTPException) as exc:
|
|
41
|
+
await check(user=_agent([]))
|
|
42
|
+
assert exc.value.status_code == 403
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.asyncio
|
|
46
|
+
async def test_matching_scope_agent_allowed():
|
|
47
|
+
check = require_scope("write")
|
|
48
|
+
user = await check(user=_agent(["read", "write"]))
|
|
49
|
+
assert user.username == "agent:tester"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.asyncio
|
|
53
|
+
async def test_missing_scope_agent_denied():
|
|
54
|
+
check = require_scope("write")
|
|
55
|
+
with pytest.raises(HTTPException) as exc:
|
|
56
|
+
await check(user=_agent(["read"]))
|
|
57
|
+
assert exc.value.status_code == 403
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.asyncio
|
|
61
|
+
async def test_human_bypasses_scope_check_even_with_empty_scopes():
|
|
62
|
+
# Cookie/JWT humans are gated by role, not scopes — empty scopes is fine.
|
|
63
|
+
check = require_scope("write")
|
|
64
|
+
user = await check(user=_human([]))
|
|
65
|
+
assert user.username == "emilio"
|
|
@@ -345,3 +345,60 @@ def test_bash_action_returns_first_matching_rule(monkeypatch):
|
|
|
345
345
|
|
|
346
346
|
assert decision.allowed is False
|
|
347
347
|
assert decision.rule == "bash-merge"
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def test_secret_scan_runs_outside_git_repo(monkeypatch):
|
|
351
|
+
"""S5a: secret scan must NOT fail open when cwd is not a git repo.
|
|
352
|
+
|
|
353
|
+
Previously resolve_repo_root() returning None silently allowed, so a
|
|
354
|
+
`git commit` of an API_KEY=sk-live-... outside a repo slipped through.
|
|
355
|
+
"""
|
|
356
|
+
bridge = _load_bridge_module()
|
|
357
|
+
monkeypatch.setattr(
|
|
358
|
+
bridge, "resolve_repo_root", lambda file_path, cwd=None: None
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
decision = bridge.check_secret_scan(
|
|
362
|
+
"git commit -m 'add config' && export API_KEY=sk-live-abcd1234efgh5678ijkl",
|
|
363
|
+
"/tmp/not-a-repo",
|
|
364
|
+
_config(),
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
assert decision.allowed is False
|
|
368
|
+
assert decision.rule == "secret-scan"
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def test_secret_scan_outside_repo_allows_clean_command(monkeypatch):
|
|
372
|
+
"""Outside a git repo, a clean commit command (no secret) is still allowed."""
|
|
373
|
+
bridge = _load_bridge_module()
|
|
374
|
+
monkeypatch.setattr(
|
|
375
|
+
bridge, "resolve_repo_root", lambda file_path, cwd=None: None
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
decision = bridge.check_secret_scan(
|
|
379
|
+
"git commit -m 'docs: tidy readme'",
|
|
380
|
+
"/tmp/not-a-repo",
|
|
381
|
+
_config(),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
assert decision.allowed is True
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def test_secret_scan_blocks_secret_in_staged_diff(monkeypatch):
|
|
388
|
+
"""In-repo path (unchanged behaviour): secret in staged diff is blocked."""
|
|
389
|
+
bridge = _load_bridge_module()
|
|
390
|
+
monkeypatch.setattr(
|
|
391
|
+
bridge, "resolve_repo_root", lambda file_path, cwd=None: "/repo"
|
|
392
|
+
)
|
|
393
|
+
monkeypatch.setattr(
|
|
394
|
+
bridge,
|
|
395
|
+
"_git_output",
|
|
396
|
+
lambda args, cwd: "+API_KEY=sk-live-abcd1234efgh5678ijkl",
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
decision = bridge.check_secret_scan(
|
|
400
|
+
"git commit -m 'add config'", "/repo", _config()
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
assert decision.allowed is False
|
|
404
|
+
assert decision.rule == "secret-scan"
|
|
@@ -58,8 +58,8 @@ from core.cli._runtime_ctx import console, err_console
|
|
|
58
58
|
_PANEL_FEEDBACK = "Support"
|
|
59
59
|
|
|
60
60
|
# Target OSS repo. Kept consistent with the project URLs in pyproject.toml
|
|
61
|
-
# ([project.urls] Source/Issues = https://github.com/
|
|
62
|
-
MARVISX_OSS_REPO = "
|
|
61
|
+
# ([project.urls] Source/Issues = https://github.com/emiliomartucci/marvisx-oss).
|
|
62
|
+
MARVISX_OSS_REPO = "emiliomartucci/marvisx-oss"
|
|
63
63
|
|
|
64
64
|
# The label that turns the issue stream into a product-signal feed.
|
|
65
65
|
FEEDBACK_LABEL = "source:cli"
|
|
@@ -7,7 +7,9 @@ answers.
|
|
|
7
7
|
|
|
8
8
|
Outputs (default ~/.marvis):
|
|
9
9
|
- settings.yaml → workspace + storage + llm choice (no secrets)
|
|
10
|
-
- master.key
|
|
10
|
+
- master.key.enc + master.key.salt → Fernet key wrapped with a passphrase-derived
|
|
11
|
+
KEK, written when MARVIS_MASTER_PASSPHRASE (or an OS keyring / TTY prompt) is
|
|
12
|
+
available; otherwise a cleartext master.key (chmod 600) + a one-time warning.
|
|
11
13
|
- byok.vault → encrypted API key store
|
|
12
14
|
- <projects_root>/<slug>/project.yaml → first project seed
|
|
13
15
|
"""
|
|
@@ -60,7 +62,7 @@ from core.wizard.defaults import (
|
|
|
60
62
|
)
|
|
61
63
|
|
|
62
64
|
DEFAULT_SETTINGS_FILENAME = "settings.yaml"
|
|
63
|
-
CLI_VERSION = "marvis-init 0.2.
|
|
65
|
+
CLI_VERSION = "marvis-init 0.2.2"
|
|
64
66
|
|
|
65
67
|
|
|
66
68
|
def _default_vault_dir() -> Path:
|
|
@@ -458,14 +460,19 @@ def _bootstrap_schema(state: WizardState) -> str | None:
|
|
|
458
460
|
os.environ["PIR_PASSWORD"] = prev_pwd
|
|
459
461
|
|
|
460
462
|
|
|
461
|
-
def _store_byok(
|
|
463
|
+
def _store_byok(
|
|
464
|
+
state: WizardState, vault_dir: Path, *, allow_prompt: bool = False
|
|
465
|
+
) -> Path | None:
|
|
462
466
|
if (
|
|
463
467
|
not state.llm_provider
|
|
464
468
|
or not state.llm_provider.provider
|
|
465
469
|
or not state.llm_provider.api_key
|
|
466
470
|
):
|
|
467
471
|
return None
|
|
468
|
-
|
|
472
|
+
# When a passphrase source is available (env / keyring / TTY prompt), the
|
|
473
|
+
# master key is written encrypted from the start; otherwise cleartext + a
|
|
474
|
+
# one-time warning. Init never blocks on this.
|
|
475
|
+
ensure_master_key(vault_dir, create=True, allow_prompt=allow_prompt)
|
|
469
476
|
store_provider_key(
|
|
470
477
|
state.llm_provider.provider.value,
|
|
471
478
|
state.llm_provider.api_key,
|
|
@@ -699,7 +706,7 @@ def init(
|
|
|
699
706
|
raise typer.Exit(code=0)
|
|
700
707
|
|
|
701
708
|
_write_settings(settings_destination, settings_payload)
|
|
702
|
-
vault_file = _store_byok(state, vault_dir)
|
|
709
|
+
vault_file = _store_byok(state, vault_dir, allow_prompt=interactive)
|
|
703
710
|
project_yaml = _seed_project(state)
|
|
704
711
|
db_file = _bootstrap_schema(state)
|
|
705
712
|
|
|
@@ -552,6 +552,17 @@ def check_secret_scan(command: str | None, cwd: str | None, _config: dict[str, A
|
|
|
552
552
|
|
|
553
553
|
repo_root = resolve_repo_root(None, cwd)
|
|
554
554
|
if not repo_root:
|
|
555
|
+
# Outside a git repo there is no staged diff to inspect, but the scan must
|
|
556
|
+
# NOT fail open (previously it silently allowed, so `git commit` of an
|
|
557
|
+
# `API_KEY=sk-live-...` outside a repo slipped through). Scan the command
|
|
558
|
+
# text itself so the secret-scan rule runs regardless of git context.
|
|
559
|
+
if command and SECRET_ASSIGNMENT_RE.search(command):
|
|
560
|
+
return _deny(
|
|
561
|
+
"Blocked: possible secret in commit command. "
|
|
562
|
+
"Reason: secret-scan safety rule protects credentials in every governance profile. "
|
|
563
|
+
"Fix: remove the secret from the command and use the BYOK vault or environment configuration instead.",
|
|
564
|
+
"secret-scan",
|
|
565
|
+
)
|
|
555
566
|
return _allow()
|
|
556
567
|
|
|
557
568
|
diff = _git_output(["diff", "--cached", "--unified=0", "--no-ext-diff"], repo_root)
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""BYOK vault — Fernet AES-128-CBC + HMAC-SHA256 file store.
|
|
2
|
+
|
|
3
|
+
OSS single-user. The vault file (`~/.marvis/byok.vault`) is encrypted with a
|
|
4
|
+
Fernet "master key". That master key is itself protected at rest:
|
|
5
|
+
|
|
6
|
+
- **Encrypted (preferred)** — when a passphrase source is available the master
|
|
7
|
+
key is wrapped with a KEK (key-encryption key) derived from the passphrase via
|
|
8
|
+
scrypt (memory-hard KDF) and stored as `master.key.enc` next to a random
|
|
9
|
+
`master.key.salt`. The raw Fernet key never touches disk.
|
|
10
|
+
- **Cleartext (legacy / headless fallback)** — older installs (and headless
|
|
11
|
+
servers with no passphrase source) keep a cleartext `master.key`. It still
|
|
12
|
+
works; we just emit a one-time WARNING so the operator can opt into wrapping by
|
|
13
|
+
setting ``MARVIS_MASTER_PASSPHRASE``.
|
|
14
|
+
|
|
15
|
+
Passphrase source precedence: ``MARVIS_MASTER_PASSPHRASE`` env → OS keyring
|
|
16
|
+
(optional, soft dependency) → interactive prompt (only on a TTY). No source +
|
|
17
|
+
no TTY => cleartext + warning, never a lockout.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import base64
|
|
23
|
+
import getpass
|
|
24
|
+
import json
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
from datetime import datetime, timezone
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
from cryptography.fernet import Fernet, InvalidToken
|
|
33
|
+
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
DEFAULT_VAULT_DIR = Path.home() / ".marvis"
|
|
38
|
+
MASTER_KEY_FILENAME = "master.key"
|
|
39
|
+
MASTER_KEY_ENC_FILENAME = "master.key.enc"
|
|
40
|
+
MASTER_KEY_SALT_FILENAME = "master.key.salt"
|
|
41
|
+
VAULT_FILENAME = "byok.vault"
|
|
42
|
+
VAULT_VERSION = 1
|
|
43
|
+
|
|
44
|
+
# Passphrase source for the KEK that wraps the master key.
|
|
45
|
+
PASSPHRASE_ENV = "MARVIS_MASTER_PASSPHRASE"
|
|
46
|
+
# OS keyring coordinates (only used when the optional `keyring` backend exists).
|
|
47
|
+
_KEYRING_SERVICE = "marvisx-byok"
|
|
48
|
+
_KEYRING_USERNAME = "master-passphrase"
|
|
49
|
+
|
|
50
|
+
# scrypt parameters (memory-hard KDF). n=2**15 ~= 32 MiB working set: strong for a
|
|
51
|
+
# local single-user secret, light enough to derive once per process on a small box.
|
|
52
|
+
_SCRYPT_N = 2**15
|
|
53
|
+
_SCRYPT_R = 8
|
|
54
|
+
_SCRYPT_P = 1
|
|
55
|
+
_SCRYPT_KEYLEN = 32
|
|
56
|
+
_SALT_BYTES = 16
|
|
57
|
+
|
|
58
|
+
_KNOWN_PROVIDERS = ("anthropic", "openai", "mac_gateway", "bedrock")
|
|
59
|
+
|
|
60
|
+
# Module-level latch so the "your master.key is unencrypted" notice fires once per
|
|
61
|
+
# process instead of on every vault read/write.
|
|
62
|
+
_cleartext_warning_emitted = False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class VaultError(Exception):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class MasterKeyMissing(VaultError):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class WrongPassphrase(VaultError):
|
|
74
|
+
"""Raised when an encrypted master.key exists but the passphrase is wrong."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _master_key_path(vault_dir: Path) -> Path:
|
|
78
|
+
return vault_dir / MASTER_KEY_FILENAME
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _master_key_enc_path(vault_dir: Path) -> Path:
|
|
82
|
+
return vault_dir / MASTER_KEY_ENC_FILENAME
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _master_key_salt_path(vault_dir: Path) -> Path:
|
|
86
|
+
return vault_dir / MASTER_KEY_SALT_FILENAME
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _vault_path(vault_dir: Path) -> Path:
|
|
90
|
+
return vault_dir / VAULT_FILENAME
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _resolve_dir(vault_dir: Path | None) -> Path:
|
|
94
|
+
base = vault_dir or DEFAULT_VAULT_DIR
|
|
95
|
+
return Path(base).expanduser()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# --------------------------------------------------------------------------- #
|
|
99
|
+
# Passphrase resolution + KEK derivation
|
|
100
|
+
# --------------------------------------------------------------------------- #
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _keyring_passphrase() -> str | None:
|
|
104
|
+
"""Read the passphrase from the OS keyring.
|
|
105
|
+
|
|
106
|
+
`keyring` is an OPTIONAL soft dependency: a headless server has no backend, so
|
|
107
|
+
we import inside the function and swallow any import/backend error. Never a
|
|
108
|
+
hard runtime requirement.
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
import keyring # type: ignore[import-not-found]
|
|
112
|
+
except Exception: # noqa: BLE001 — no keyring installed at all
|
|
113
|
+
return None
|
|
114
|
+
try:
|
|
115
|
+
value = keyring.get_password(_KEYRING_SERVICE, _KEYRING_USERNAME)
|
|
116
|
+
except Exception: # noqa: BLE001 — no usable backend (headless), locked, etc.
|
|
117
|
+
return None
|
|
118
|
+
return value or None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _is_interactive() -> bool:
|
|
122
|
+
try:
|
|
123
|
+
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
124
|
+
except Exception: # noqa: BLE001 — stdin/stdout replaced in some hosts
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def resolve_passphrase(*, allow_prompt: bool = False) -> str | None:
|
|
129
|
+
"""Resolve the master passphrase, or None when no source is available.
|
|
130
|
+
|
|
131
|
+
Precedence: ``MARVIS_MASTER_PASSPHRASE`` env → OS keyring (optional) →
|
|
132
|
+
interactive prompt (only when ``allow_prompt`` and a TTY is present).
|
|
133
|
+
"""
|
|
134
|
+
env = os.environ.get(PASSPHRASE_ENV)
|
|
135
|
+
if env:
|
|
136
|
+
return env
|
|
137
|
+
|
|
138
|
+
from_keyring = _keyring_passphrase()
|
|
139
|
+
if from_keyring:
|
|
140
|
+
return from_keyring
|
|
141
|
+
|
|
142
|
+
if allow_prompt and _is_interactive():
|
|
143
|
+
try:
|
|
144
|
+
entered = getpass.getpass(
|
|
145
|
+
"Master passphrase (protects your BYOK API keys): "
|
|
146
|
+
)
|
|
147
|
+
except Exception: # noqa: BLE001 — non-interactive surprise, EOF, etc.
|
|
148
|
+
return None
|
|
149
|
+
return entered or None
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _derive_kek(passphrase: str, salt: bytes) -> bytes:
|
|
155
|
+
"""Derive a 32-byte Fernet-compatible KEK from the passphrase via scrypt."""
|
|
156
|
+
kdf = Scrypt(
|
|
157
|
+
salt=salt,
|
|
158
|
+
length=_SCRYPT_KEYLEN,
|
|
159
|
+
n=_SCRYPT_N,
|
|
160
|
+
r=_SCRYPT_R,
|
|
161
|
+
p=_SCRYPT_P,
|
|
162
|
+
)
|
|
163
|
+
raw = kdf.derive(passphrase.encode("utf-8"))
|
|
164
|
+
return base64.urlsafe_b64encode(raw)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _chmod_600(path: Path) -> None:
|
|
168
|
+
try:
|
|
169
|
+
os.chmod(path, 0o600)
|
|
170
|
+
except OSError:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# --------------------------------------------------------------------------- #
|
|
175
|
+
# Wrap / unwrap the master key
|
|
176
|
+
# --------------------------------------------------------------------------- #
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _write_encrypted_master_key(
|
|
180
|
+
vault_dir: Path, fernet_key: bytes, passphrase: str
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Wrap `fernet_key` with a passphrase-derived KEK and write the .enc + .salt."""
|
|
183
|
+
salt = os.urandom(_SALT_BYTES)
|
|
184
|
+
kek = _derive_kek(passphrase, salt)
|
|
185
|
+
wrapped = Fernet(kek).encrypt(fernet_key)
|
|
186
|
+
|
|
187
|
+
salt_file = _master_key_salt_path(vault_dir)
|
|
188
|
+
salt_tmp = salt_file.with_suffix(salt_file.suffix + ".tmp")
|
|
189
|
+
salt_tmp.write_bytes(salt)
|
|
190
|
+
_chmod_600(salt_tmp)
|
|
191
|
+
salt_tmp.replace(salt_file)
|
|
192
|
+
|
|
193
|
+
enc_file = _master_key_enc_path(vault_dir)
|
|
194
|
+
enc_tmp = enc_file.with_suffix(enc_file.suffix + ".tmp")
|
|
195
|
+
enc_tmp.write_bytes(wrapped)
|
|
196
|
+
_chmod_600(enc_tmp)
|
|
197
|
+
enc_tmp.replace(enc_file)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _read_encrypted_master_key(vault_dir: Path, passphrase: str) -> bytes:
|
|
201
|
+
"""Unwrap and return the raw Fernet key from the .enc + .salt files."""
|
|
202
|
+
salt = _master_key_salt_path(vault_dir).read_bytes()
|
|
203
|
+
wrapped = _master_key_enc_path(vault_dir).read_bytes().strip()
|
|
204
|
+
if not salt or not wrapped:
|
|
205
|
+
raise VaultError("Encrypted master key files are empty/corrupt")
|
|
206
|
+
kek = _derive_kek(passphrase, salt)
|
|
207
|
+
try:
|
|
208
|
+
return Fernet(kek).decrypt(wrapped)
|
|
209
|
+
except InvalidToken as exc:
|
|
210
|
+
raise WrongPassphrase(
|
|
211
|
+
f"wrong {PASSPHRASE_ENV} (cannot unwrap the master key)"
|
|
212
|
+
) from exc
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _warn_cleartext_once(key_file: Path) -> None:
|
|
216
|
+
global _cleartext_warning_emitted
|
|
217
|
+
if _cleartext_warning_emitted:
|
|
218
|
+
return
|
|
219
|
+
_cleartext_warning_emitted = True
|
|
220
|
+
logger.warning(
|
|
221
|
+
"%s is unencrypted; set %s to protect your BYOK API keys at rest "
|
|
222
|
+
"(it will be migrated to an encrypted master.key.enc automatically).",
|
|
223
|
+
key_file,
|
|
224
|
+
PASSPHRASE_ENV,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# --------------------------------------------------------------------------- #
|
|
229
|
+
# Public master-key entrypoint
|
|
230
|
+
# --------------------------------------------------------------------------- #
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def ensure_master_key(
|
|
234
|
+
vault_dir: Path | None = None,
|
|
235
|
+
*,
|
|
236
|
+
create: bool = True,
|
|
237
|
+
allow_prompt: bool = False,
|
|
238
|
+
) -> bytes:
|
|
239
|
+
"""Read or create the master key. Returns the raw Fernet key bytes.
|
|
240
|
+
|
|
241
|
+
Resolution order (no lockout, ever):
|
|
242
|
+
|
|
243
|
+
1. **Encrypted on disk** (`master.key.enc` + `master.key.salt`) → unwrap with
|
|
244
|
+
the resolved passphrase. Missing passphrase → ``MasterKeyMissing``; wrong
|
|
245
|
+
passphrase → ``WrongPassphrase`` (clean error, no corruption).
|
|
246
|
+
2. **Cleartext on disk** (`master.key`, legacy) → use it. If a passphrase is
|
|
247
|
+
available, transparently MIGRATE it (write .enc/.salt, remove cleartext).
|
|
248
|
+
If not, keep using cleartext + emit a one-time WARNING.
|
|
249
|
+
3. **Nothing on disk** + ``create`` → generate a fresh key. If a passphrase is
|
|
250
|
+
available, write it encrypted from the start (never cleartext). Otherwise
|
|
251
|
+
write cleartext + warning. ``create=False`` → ``MasterKeyMissing``.
|
|
252
|
+
"""
|
|
253
|
+
resolved = _resolve_dir(vault_dir)
|
|
254
|
+
resolved.mkdir(parents=True, exist_ok=True)
|
|
255
|
+
try:
|
|
256
|
+
os.chmod(resolved, 0o700)
|
|
257
|
+
except OSError:
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
enc_file = _master_key_enc_path(resolved)
|
|
261
|
+
salt_file = _master_key_salt_path(resolved)
|
|
262
|
+
key_file = _master_key_path(resolved)
|
|
263
|
+
|
|
264
|
+
# 1. Encrypted form present → it is the source of truth.
|
|
265
|
+
if enc_file.exists() and salt_file.exists():
|
|
266
|
+
passphrase = resolve_passphrase(allow_prompt=allow_prompt)
|
|
267
|
+
if not passphrase:
|
|
268
|
+
raise MasterKeyMissing(
|
|
269
|
+
f"Encrypted master key found at {enc_file} but no passphrase "
|
|
270
|
+
f"available; set {PASSPHRASE_ENV} to unlock the BYOK vault."
|
|
271
|
+
)
|
|
272
|
+
return _read_encrypted_master_key(resolved, passphrase)
|
|
273
|
+
|
|
274
|
+
# 2. Cleartext form present (legacy / headless) → keep working, migrate if able.
|
|
275
|
+
if key_file.exists():
|
|
276
|
+
key = key_file.read_bytes().strip()
|
|
277
|
+
if not key:
|
|
278
|
+
raise VaultError(f"Master key file empty: {key_file}")
|
|
279
|
+
passphrase = resolve_passphrase(allow_prompt=allow_prompt)
|
|
280
|
+
if passphrase:
|
|
281
|
+
# Transparent migration: wrap, then drop the cleartext copy.
|
|
282
|
+
_write_encrypted_master_key(resolved, key, passphrase)
|
|
283
|
+
try:
|
|
284
|
+
key_file.unlink()
|
|
285
|
+
except OSError:
|
|
286
|
+
logger.warning(
|
|
287
|
+
"Migrated master key to %s but could not remove the "
|
|
288
|
+
"cleartext %s; delete it manually.",
|
|
289
|
+
enc_file,
|
|
290
|
+
key_file,
|
|
291
|
+
)
|
|
292
|
+
logger.info(
|
|
293
|
+
"Migrated %s to an encrypted master.key.enc (protected by %s).",
|
|
294
|
+
key_file,
|
|
295
|
+
PASSPHRASE_ENV,
|
|
296
|
+
)
|
|
297
|
+
else:
|
|
298
|
+
_warn_cleartext_once(key_file)
|
|
299
|
+
return key
|
|
300
|
+
|
|
301
|
+
# 3. Nothing on disk.
|
|
302
|
+
if not create:
|
|
303
|
+
raise MasterKeyMissing(f"Master key not found at {key_file}")
|
|
304
|
+
|
|
305
|
+
key = Fernet.generate_key()
|
|
306
|
+
passphrase = resolve_passphrase(allow_prompt=allow_prompt)
|
|
307
|
+
if passphrase:
|
|
308
|
+
_write_encrypted_master_key(resolved, key, passphrase)
|
|
309
|
+
else:
|
|
310
|
+
key_file.write_bytes(key)
|
|
311
|
+
_chmod_600(key_file)
|
|
312
|
+
_warn_cleartext_once(key_file)
|
|
313
|
+
return key
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _empty_vault() -> dict[str, Any]:
|
|
317
|
+
return {
|
|
318
|
+
"version": VAULT_VERSION,
|
|
319
|
+
"providers": {provider: None for provider in _KNOWN_PROVIDERS},
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def load_vault(vault_dir: Path | None = None) -> dict[str, Any]:
|
|
324
|
+
"""Decrypt + return the vault. Returns an empty vault if file missing."""
|
|
325
|
+
resolved = _resolve_dir(vault_dir)
|
|
326
|
+
vault_file = _vault_path(resolved)
|
|
327
|
+
if not vault_file.exists():
|
|
328
|
+
return _empty_vault()
|
|
329
|
+
|
|
330
|
+
master = ensure_master_key(resolved, create=False)
|
|
331
|
+
fernet = Fernet(master)
|
|
332
|
+
token = vault_file.read_bytes()
|
|
333
|
+
try:
|
|
334
|
+
plaintext = fernet.decrypt(token)
|
|
335
|
+
except InvalidToken as exc:
|
|
336
|
+
raise VaultError("Vault corrupted or master key mismatch") from exc
|
|
337
|
+
return json.loads(plaintext.decode("utf-8"))
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def save_vault(
|
|
341
|
+
vault: dict[str, Any], vault_dir: Path | None = None
|
|
342
|
+
) -> Path:
|
|
343
|
+
"""Encrypt + atomically write the vault. Returns the absolute path."""
|
|
344
|
+
resolved = _resolve_dir(vault_dir)
|
|
345
|
+
master = ensure_master_key(resolved, create=True)
|
|
346
|
+
fernet = Fernet(master)
|
|
347
|
+
payload = json.dumps(vault, sort_keys=True).encode("utf-8")
|
|
348
|
+
token = fernet.encrypt(payload)
|
|
349
|
+
|
|
350
|
+
vault_file = _vault_path(resolved)
|
|
351
|
+
tmp_file = vault_file.with_suffix(".tmp")
|
|
352
|
+
tmp_file.write_bytes(token)
|
|
353
|
+
_chmod_600(tmp_file)
|
|
354
|
+
tmp_file.replace(vault_file)
|
|
355
|
+
return vault_file
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def store_provider_key(
|
|
359
|
+
provider: str,
|
|
360
|
+
api_key: str,
|
|
361
|
+
*,
|
|
362
|
+
base_url: str | None = None,
|
|
363
|
+
vault_dir: Path | None = None,
|
|
364
|
+
) -> None:
|
|
365
|
+
if provider not in _KNOWN_PROVIDERS:
|
|
366
|
+
raise VaultError(f"Unknown provider: {provider}")
|
|
367
|
+
vault = load_vault(vault_dir)
|
|
368
|
+
entry: dict[str, Any] = {
|
|
369
|
+
"api_key": api_key,
|
|
370
|
+
"added_at": datetime.now(timezone.utc).isoformat(),
|
|
371
|
+
}
|
|
372
|
+
if base_url is not None:
|
|
373
|
+
entry["base_url"] = base_url
|
|
374
|
+
vault["providers"][provider] = entry
|
|
375
|
+
save_vault(vault, vault_dir)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def mask_api_key(api_key: str | None) -> str:
|
|
379
|
+
"""First 6 + last 4. Used for recap display + audit log lines."""
|
|
380
|
+
if not api_key:
|
|
381
|
+
return ""
|
|
382
|
+
if len(api_key) <= 10:
|
|
383
|
+
return "***"
|
|
384
|
+
return f"{api_key[:6]}***{api_key[-4:]}"
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marvisx-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: MarvisX OSS — agent-native company-brain CLI runtime (init, status, brief, triage)
|
|
5
5
|
Author: MarvisX maintainers
|
|
6
6
|
License: BSL-1.1
|
|
7
7
|
Project-URL: Homepage, https://justaskmarvis.com
|
|
8
8
|
Project-URL: Documentation, https://justaskmarvis.com/docs/getting-started
|
|
9
|
-
Project-URL: Source, https://github.com/
|
|
10
|
-
Project-URL: Issues, https://github.com/
|
|
11
|
-
Project-URL: Changelog, https://github.com/
|
|
9
|
+
Project-URL: Source, https://github.com/emiliomartucci/marvisx-oss
|
|
10
|
+
Project-URL: Issues, https://github.com/emiliomartucci/marvisx-oss/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/emiliomartucci/marvisx-oss/blob/main/CHANGELOG.md
|
|
12
12
|
Keywords: marvisx,cli,bootstrap,wizard,oss,agent,company-brain
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
@@ -72,6 +72,8 @@ Requires-Dist: sqlite-vec>=0.1.6
|
|
|
72
72
|
Provides-Extra: dev
|
|
73
73
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
74
74
|
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
75
|
+
Provides-Extra: keyring
|
|
76
|
+
Requires-Dist: keyring>=24; extra == "keyring"
|
|
75
77
|
Dynamic: license-file
|
|
76
78
|
|
|
77
79
|
# marvis init
|