@agentikos/omega-os 0.2.0 → 0.19.6
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.
- package/README.md +33 -3
- package/bootstrap/lib/__pycache__/claude-code-settings.cpython-313.pyc +0 -0
- package/bootstrap/lib/__pycache__/llm-clis.cpython-313.pyc +0 -0
- package/bootstrap/lib/__pycache__/manifest-helpers.cpython-313.pyc +0 -0
- package/bootstrap/lib/claude-code-settings.py +176 -0
- package/bootstrap/lib/common.sh +457 -1
- package/bootstrap/lib/llm-clis.py +341 -0
- package/bootstrap/lib/manifest-helpers.py +384 -0
- package/bootstrap/lib/steps.sh +790 -21
- package/bootstrap/manifest.example.yaml +87 -1
- package/bootstrap/templates/aisb/CLAUDE.md +305 -0
- package/bootstrap/templates/aisb/architect.md +204 -0
- package/bootstrap/templates/aisb/checkers/CLAUDE.md +9 -0
- package/bootstrap/templates/aisb/checkers/checker-architect.md +151 -0
- package/bootstrap/templates/aisb/checkers/checker-common.md +171 -0
- package/bootstrap/templates/aisb/checkers/checker-construct.md +129 -0
- package/bootstrap/templates/aisb/checkers/checker-keymaker.md +204 -0
- package/bootstrap/templates/aisb/checkers/checker-link.md +205 -0
- package/bootstrap/templates/aisb/checkers/checker-merovingian.md +219 -0
- package/bootstrap/templates/aisb/checkers/checker-morpheus.md +211 -0
- package/bootstrap/templates/aisb/checkers/checker-neo.md +177 -0
- package/bootstrap/templates/aisb/checkers/checker-niobe.md +156 -0
- package/bootstrap/templates/aisb/checkers/checker-oracle.md +164 -0
- package/bootstrap/templates/aisb/checkers/checker-seraph.md +187 -0
- package/bootstrap/templates/aisb/checkers/checker-smith.md +195 -0
- package/bootstrap/templates/aisb/checkers/checker-zion.md +113 -0
- package/bootstrap/templates/aisb/construct.md +135 -0
- package/bootstrap/templates/aisb/keymaker.md +227 -0
- package/bootstrap/templates/aisb/link.md +170 -0
- package/bootstrap/templates/aisb/lmc-protocol.md +57 -0
- package/bootstrap/templates/aisb/merovingian.md +159 -0
- package/bootstrap/templates/aisb/morpheus.md +243 -0
- package/bootstrap/templates/aisb/neo.md +147 -0
- package/bootstrap/templates/aisb/niobe.md +197 -0
- package/bootstrap/templates/aisb/oracle.md +244 -0
- package/bootstrap/templates/aisb/protocols/handoff-templates.md +204 -0
- package/bootstrap/templates/aisb/protocols/shared-protocol.md +248 -0
- package/bootstrap/templates/aisb/pythia.md +153 -0
- package/bootstrap/templates/aisb/seraph.md +315 -0
- package/bootstrap/templates/aisb/smith.md +202 -0
- package/bootstrap/templates/aisb/zion.md +172 -0
- package/bootstrap/templates/autonomous/audit-patrol.yaml +41 -0
- package/bootstrap/templates/autonomous/smith-reflect.yaml +43 -0
- package/bootstrap/templates/autonomous/ssh-key-rotate.yaml +46 -0
- package/bootstrap/templates/autonomous/support-agent.yaml +38 -0
- package/docs/AUDITS.md +85 -0
- package/docs/GAP-ANALYSIS.md +214 -0
- package/docs/INSTALL.md +47 -9
- package/docs/MCP-AND-PLUGINS.md +31 -4
- package/docs/SIMULATION.md +171 -0
- package/docs/simulate.sh +211 -0
- package/install.sh +164 -17
- package/omega/Agentik_Engine/README.md +4 -2
- package/omega/Agentik_Engine/omega_engine/__init__.py +147 -1
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/account.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/agent_messages.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/aisb_chat.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_arsenal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_diff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_gate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/auto_update.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/backup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/barrier.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/bus.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cadence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/classifier.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cleanup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/completions.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/costs.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/done_signal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/envelope.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/events.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/executor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/handoff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_bootstrap.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes_desktop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/learning.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/managed_agent.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/memory.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/menu.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/mission.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/plan.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/progress.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/project.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/prompts.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/prune.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/pursue.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/reducer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/report.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/router.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/skill_routing.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/smoke.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/store.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/supervisor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/sync.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/task.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/telegram.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/telegram_history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tools.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/understand_anything.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/updater.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/validate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/vault.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/webhooks.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/worker.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/account.py +28 -31
- package/omega/Agentik_Engine/omega_engine/agent_messages.py +167 -0
- package/omega/Agentik_Engine/omega_engine/aisb_chat.py +128 -0
- package/omega/Agentik_Engine/omega_engine/audit_diff.py +99 -0
- package/omega/Agentik_Engine/omega_engine/audit_gate.py +149 -0
- package/omega/Agentik_Engine/omega_engine/audits/__init__.py +60 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/batcher.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/dispatcher.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/generator.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/__pycache__/pipeline.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audits/batcher.py +218 -0
- package/omega/Agentik_Engine/omega_engine/audits/dispatcher.py +92 -0
- package/omega/Agentik_Engine/omega_engine/audits/generator.py +234 -0
- package/omega/Agentik_Engine/omega_engine/audits/history.py +168 -0
- package/omega/Agentik_Engine/omega_engine/audits/pipeline.py +198 -0
- package/omega/Agentik_Engine/omega_engine/auto_update.py +339 -0
- package/omega/Agentik_Engine/omega_engine/backup.py +215 -0
- package/omega/Agentik_Engine/omega_engine/cadence.py +158 -0
- package/omega/Agentik_Engine/omega_engine/classifier.py +215 -0
- package/omega/Agentik_Engine/omega_engine/cleanup.py +673 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +4156 -86
- package/omega/Agentik_Engine/omega_engine/completions.py +260 -0
- package/omega/Agentik_Engine/omega_engine/costs.py +100 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/engine.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/__pycache__/telegram.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/daemons/engine.py +53 -4
- package/omega/Agentik_Engine/omega_engine/daemons/telegram.py +101 -17
- package/omega/Agentik_Engine/omega_engine/done_signal.py +154 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/artifact.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/automation.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/base.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/claudecode.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/connection.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/coworker.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/loop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/prompt.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/educators/__pycache__/skill.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/envelope.py +219 -0
- package/omega/Agentik_Engine/omega_engine/executor.py +149 -10
- package/omega/Agentik_Engine/omega_engine/genesis/__init__.py +134 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/phases.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/stack.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/__pycache__/state.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/genesis/orchestrator.py +262 -0
- package/omega/Agentik_Engine/omega_engine/genesis/phases.py +950 -0
- package/omega/Agentik_Engine/omega_engine/genesis/stack.py +324 -0
- package/omega/Agentik_Engine/omega_engine/genesis/state.py +353 -0
- package/omega/Agentik_Engine/omega_engine/handoff.py +459 -0
- package/omega/Agentik_Engine/omega_engine/hermes.py +426 -0
- package/omega/Agentik_Engine/omega_engine/hermes_bootstrap.py +382 -0
- package/omega/Agentik_Engine/omega_engine/hermes_desktop.py +469 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__init__.py +30 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/integrations/__pycache__/graphify.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/integrations/graphify.py +234 -0
- package/omega/Agentik_Engine/omega_engine/learning.py +268 -0
- package/omega/Agentik_Engine/omega_engine/managed_agent.py +467 -0
- package/omega/Agentik_Engine/omega_engine/memory.py +271 -0
- package/omega/Agentik_Engine/omega_engine/menu.py +1065 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__init__.py +144 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/migrations/__pycache__/v0_14_0.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/migrations/v0_14_0.py +29 -0
- package/omega/Agentik_Engine/omega_engine/mission.py +16 -13
- package/omega/Agentik_Engine/omega_engine/plan.py +846 -0
- package/omega/Agentik_Engine/omega_engine/prompts.py +158 -0
- package/omega/Agentik_Engine/omega_engine/provider.py +161 -12
- package/omega/Agentik_Engine/omega_engine/prune.py +151 -0
- package/omega/Agentik_Engine/omega_engine/pursue.py +205 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/agentic.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/base.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/corrective.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/graph.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/hybrid.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/multimodal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/rag/__pycache__/router.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/router.py +28 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__init__.py +48 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/auditor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/finder.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/installer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/__pycache__/marketplaces.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/auditor.py +232 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/finder.py +94 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/installer.py +129 -0
- package/omega/Agentik_Engine/omega_engine/skill_discovery/marketplaces.py +80 -0
- package/omega/Agentik_Engine/omega_engine/skill_routing.py +388 -0
- package/omega/Agentik_Engine/omega_engine/smoke.py +81 -0
- package/omega/Agentik_Engine/omega_engine/store.py +88 -41
- package/omega/Agentik_Engine/omega_engine/sync.py +142 -1
- package/omega/Agentik_Engine/omega_engine/telegram_history.py +260 -0
- package/omega/Agentik_Engine/omega_engine/tmux.py +526 -0
- package/omega/Agentik_Engine/omega_engine/understand_anything.py +275 -0
- package/omega/Agentik_Engine/omega_engine/updater.py +70 -0
- package/omega/Agentik_Engine/omega_engine/validate.py +186 -0
- package/omega/Agentik_Engine/omega_engine/vault.py +342 -0
- package/omega/Agentik_Engine/omega_engine/webhooks.py +262 -0
- package/omega/Agentik_Engine/omega_engine/worker.py +526 -0
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_account.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_adversarial.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_agents_envelope.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audits_pipeline.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_auto_update_and_migrations.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_autonomous.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_educators.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_genesis_and_plan.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_graphify.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_handoff.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_hermes_bootstrap_and_desktop.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_intelligence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_llm_clis_and_uninstall.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_managed_agent.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_max_provider_and_menu.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_menu_coverage.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_pursue_cadence.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_rag.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_role_aliases_and_ssot.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_discovery_and_gate.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_power.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_skill_routing.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_snapshot_partial.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_telegram_history.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tools_and_sync.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v06_features.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_vault.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_webhooks_and_readiness.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_worker_and_cleanup.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_account.py +8 -3
- package/omega/Agentik_Engine/tests/test_adversarial.py +351 -0
- package/omega/Agentik_Engine/tests/test_agents_envelope.py +274 -0
- package/omega/Agentik_Engine/tests/test_audits_pipeline.py +348 -0
- package/omega/Agentik_Engine/tests/test_auto_update_and_migrations.py +394 -0
- package/omega/Agentik_Engine/tests/test_genesis_and_plan.py +573 -0
- package/omega/Agentik_Engine/tests/test_graphify.py +190 -0
- package/omega/Agentik_Engine/tests/test_handoff.py +311 -0
- package/omega/Agentik_Engine/tests/test_hermes_and_ua.py +387 -0
- package/omega/Agentik_Engine/tests/test_hermes_bootstrap_and_desktop.py +358 -0
- package/omega/Agentik_Engine/tests/test_install_steps.py +359 -0
- package/omega/Agentik_Engine/tests/test_install_ux.py +151 -0
- package/omega/Agentik_Engine/tests/test_installer_wiring.py +496 -0
- package/omega/Agentik_Engine/tests/test_intelligence.py +285 -0
- package/omega/Agentik_Engine/tests/test_llm_clis_and_uninstall.py +228 -0
- package/omega/Agentik_Engine/tests/test_managed_agent.py +363 -0
- package/omega/Agentik_Engine/tests/test_max_provider_and_menu.py +231 -0
- package/omega/Agentik_Engine/tests/test_menu_coverage.py +72 -0
- package/omega/Agentik_Engine/tests/test_pursue_cadence.py +217 -0
- package/omega/Agentik_Engine/tests/test_role_aliases_and_ssot.py +207 -0
- package/omega/Agentik_Engine/tests/test_skill_discovery_and_gate.py +337 -0
- package/omega/Agentik_Engine/tests/test_skill_power.py +259 -0
- package/omega/Agentik_Engine/tests/test_skill_routing.py +189 -0
- package/omega/Agentik_Engine/tests/test_telegram_history.py +209 -0
- package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +223 -0
- package/omega/Agentik_Engine/tests/test_v06_features.py +370 -0
- package/omega/Agentik_Engine/tests/test_vault.py +173 -0
- package/omega/Agentik_Engine/tests/test_webhooks_and_readiness.py +277 -0
- package/omega/Agentik_Engine/tests/test_worker_and_cleanup.py +541 -0
- package/omega/Agentik_Extra/etc/secrets/.vault-key +3 -0
- package/omega/Agentik_Extra/etc/secrets/.vault-pub +1 -0
- package/omega/Agentik_Runtime/audits.db +0 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/claude-plugins/claude-plugins.yaml +100 -0
- package/omega/Agentik_SSOT/docs/LAYERS.md +90 -0
- package/omega/Agentik_SSOT/docs/USER-JOURNEY.md +283 -0
- package/omega/Agentik_SSOT/docs/quality-arsenal/ARSENAL-INTERCONNECTIONS.md +283 -0
- package/omega/Agentik_SSOT/docs/quality-arsenal/ARSENAL-ORCHESTRATION-PLAYBOOK.md +364 -0
- package/omega/Agentik_SSOT/docs/quality-arsenal/AUDIT-VERIFICATION-CONTRACT.md +272 -0
- package/omega/Agentik_SSOT/docs/quality-arsenal/QUALITY-ARSENAL-PREAMBLE.md +462 -0
- package/omega/Agentik_SSOT/marketplaces/design-discipline.yaml +86 -0
- package/omega/Agentik_SSOT/skills/a11yaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/apiaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/audit-orchestrator.md +212 -0
- package/omega/Agentik_SSOT/skills/audit-pilot.md +466 -0
- package/omega/Agentik_SSOT/skills/audit-tracker.md +147 -0
- package/omega/Agentik_SSOT/skills/automationaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/cadence/SKILL.md +76 -0
- package/omega/Agentik_SSOT/skills/codeaudit/SKILL.md +153 -0
- package/omega/Agentik_SSOT/skills/copyaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/dataaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/debugaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/dispatch/SKILL.md +79 -0
- package/omega/Agentik_SSOT/skills/dxaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/featureaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/flowaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/genesis/SKILL.md +116 -0
- package/omega/Agentik_SSOT/skills/handoff/SKILL.md +117 -0
- package/omega/Agentik_SSOT/skills/logicaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/motionaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/newcmd.md +300 -0
- package/omega/Agentik_SSOT/skills/perfaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/plan/SKILL.md +127 -0
- package/omega/Agentik_SSOT/skills/pursue/SKILL.md +68 -0
- package/omega/Agentik_SSOT/skills/quality-arsenal.md +180 -0
- package/omega/Agentik_SSOT/skills/rag-route.md +9 -0
- package/omega/Agentik_SSOT/skills/refontaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/retentionaudit/SKILL.md +165 -0
- package/omega/Agentik_SSOT/skills/secaudit/SKILL.md +157 -0
- package/omega/Agentik_SSOT/skills/seoaudit/SKILL.md +161 -0
- package/omega/Agentik_SSOT/skills/skill-auditor/SKILL.md +83 -0
- package/omega/Agentik_SSOT/skills/skill-finder/SKILL.md +116 -0
- package/omega/Agentik_SSOT/skills/uiuxaudit/SKILL.md +165 -0
- package/package.json +2 -2
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""Encrypted secrets vault — age if available, chmod 600 plaintext fallback.
|
|
2
|
+
|
|
3
|
+
The vault is the single source for *how* a secret reaches disk. It exists so
|
|
4
|
+
that the rest of the engine never duplicates the "if age then encrypt else
|
|
5
|
+
chmod 600" logic.
|
|
6
|
+
|
|
7
|
+
Backends
|
|
8
|
+
--------
|
|
9
|
+
* **age** (preferred) — uses the system `age` and `age-keygen` binaries.
|
|
10
|
+
At first write, we generate an age identity at
|
|
11
|
+
`Agentik_Extra/etc/secrets/.vault-key` (mode 600) plus its public recipient
|
|
12
|
+
at `.vault-pub` (mode 644). Every secret is written as `<ref>.age` next to
|
|
13
|
+
the key file.
|
|
14
|
+
* **plain** (fallback) — when age is not installed, secrets are still stored
|
|
15
|
+
with mode 600 under `Agentik_Extra/etc/secrets/<ref>`. The doctor flags
|
|
16
|
+
this as "unencrypted" so the operator can install age and re-write.
|
|
17
|
+
|
|
18
|
+
API
|
|
19
|
+
---
|
|
20
|
+
* ``vault_init(home)`` — idempotent. Ensures the secrets dir exists, mode 700.
|
|
21
|
+
If age is installed, ensures the keypair exists. Returns a status dict.
|
|
22
|
+
* ``vault_write(home, ref, value)`` — write a secret. Returns the path.
|
|
23
|
+
* ``vault_read(home, ref) -> str | None`` — read a secret, or None if absent.
|
|
24
|
+
* ``vault_status(home)`` — summary for ``omega doctor``.
|
|
25
|
+
|
|
26
|
+
The vault is intentionally narrow: one value per ref, plain string. Tokens
|
|
27
|
+
are strings; structured secrets (mcp-<id>.env) keep their dotenv layout for
|
|
28
|
+
now and can migrate later.
|
|
29
|
+
"""
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import shutil
|
|
34
|
+
import stat
|
|
35
|
+
import subprocess
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _safe_ref(ref: str) -> str:
|
|
41
|
+
"""Refuse path-traversal — `ref` is a flat filename only."""
|
|
42
|
+
if not ref or "/" in ref or "\\" in ref or ".." in ref:
|
|
43
|
+
raise ValueError(f"invalid secret ref: {ref!r}")
|
|
44
|
+
if ref.startswith("."):
|
|
45
|
+
raise ValueError(f"secret ref must not start with '.': {ref!r}")
|
|
46
|
+
return ref
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _safe_project_slug(project: str) -> str:
|
|
50
|
+
"""A project slug is a flat name — no traversal."""
|
|
51
|
+
if not project or "/" in project or "\\" in project or ".." in project:
|
|
52
|
+
raise ValueError(f"invalid project slug: {project!r}")
|
|
53
|
+
if project.startswith("."):
|
|
54
|
+
raise ValueError(f"project slug must not start with '.': {project!r}")
|
|
55
|
+
return project
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _secrets_dir(home: Path, project: str | None = None) -> Path:
|
|
59
|
+
"""Return the vault directory, creating it with mode 700.
|
|
60
|
+
|
|
61
|
+
When ``project`` is set, the directory is
|
|
62
|
+
``Agentik_Coding/projects/<slug>/.secrets/`` — a per-project vault that
|
|
63
|
+
sits next to the project's code. Otherwise the global vault at
|
|
64
|
+
``Agentik_Extra/etc/secrets/``.
|
|
65
|
+
"""
|
|
66
|
+
if project is not None:
|
|
67
|
+
slug = _safe_project_slug(project)
|
|
68
|
+
d = (Path(home) / "Agentik_Coding" / "projects" / slug / ".secrets")
|
|
69
|
+
else:
|
|
70
|
+
d = Path(home) / "Agentik_Extra" / "etc" / "secrets"
|
|
71
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
try:
|
|
73
|
+
os.chmod(d, 0o700)
|
|
74
|
+
except OSError:
|
|
75
|
+
pass
|
|
76
|
+
return d
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _global_key_dir(home: Path) -> Path:
|
|
80
|
+
"""Where the age keypair lives. Always the global secrets dir — every
|
|
81
|
+
per-project vault encrypts to the same recipient so the engine can
|
|
82
|
+
decrypt anything with one key."""
|
|
83
|
+
return _secrets_dir(home, project=None)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _age_available() -> bool:
|
|
87
|
+
return shutil.which("age") is not None and shutil.which("age-keygen") is not None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def vault_init(home: str | Path) -> dict[str, Any]:
|
|
91
|
+
"""Idempotent vault setup. Generates the age keypair on first call when
|
|
92
|
+
age is installed; no-op afterwards. Always safe to call.
|
|
93
|
+
|
|
94
|
+
Returns: ``{ "backend": "age" | "plain", "key_path"?, "pub_path"?, ... }``
|
|
95
|
+
"""
|
|
96
|
+
home = Path(home)
|
|
97
|
+
sec = _global_key_dir(home)
|
|
98
|
+
if not _age_available():
|
|
99
|
+
return {"backend": "plain", "age_installed": False, "secrets_dir": str(sec)}
|
|
100
|
+
|
|
101
|
+
key_path = sec / ".vault-key"
|
|
102
|
+
pub_path = sec / ".vault-pub"
|
|
103
|
+
if key_path.exists() and pub_path.exists():
|
|
104
|
+
return {
|
|
105
|
+
"backend": "age", "age_installed": True,
|
|
106
|
+
"key_path": str(key_path), "pub_path": str(pub_path),
|
|
107
|
+
"secrets_dir": str(sec),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Generate a fresh keypair.
|
|
111
|
+
proc = subprocess.run(
|
|
112
|
+
["age-keygen", "-o", str(key_path)],
|
|
113
|
+
check=False, capture_output=True, text=True, timeout=30,
|
|
114
|
+
)
|
|
115
|
+
if proc.returncode != 0:
|
|
116
|
+
return {
|
|
117
|
+
"backend": "plain", "age_installed": True,
|
|
118
|
+
"secrets_dir": str(sec),
|
|
119
|
+
"warning": f"age-keygen failed: {proc.stderr.strip()[:200]}",
|
|
120
|
+
}
|
|
121
|
+
try:
|
|
122
|
+
os.chmod(key_path, 0o600)
|
|
123
|
+
except OSError:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
# age-keygen prints "Public key: age1xyz..." to stderr.
|
|
127
|
+
pub: str | None = None
|
|
128
|
+
for line in (proc.stderr or "").splitlines():
|
|
129
|
+
s = line.strip()
|
|
130
|
+
if s.startswith("Public key:"):
|
|
131
|
+
pub = s.split(":", 1)[1].strip()
|
|
132
|
+
break
|
|
133
|
+
if not pub:
|
|
134
|
+
# Some age-keygen versions write the public key as a comment in the
|
|
135
|
+
# private-key file. Try to extract it from there.
|
|
136
|
+
for line in key_path.read_text().splitlines():
|
|
137
|
+
s = line.strip()
|
|
138
|
+
if s.lower().startswith("# public key:"):
|
|
139
|
+
pub = s.split(":", 1)[1].strip()
|
|
140
|
+
break
|
|
141
|
+
if not pub:
|
|
142
|
+
return {
|
|
143
|
+
"backend": "plain", "age_installed": True,
|
|
144
|
+
"secrets_dir": str(sec),
|
|
145
|
+
"warning": "could not extract public key from age-keygen output",
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
pub_path.write_text(pub + "\n")
|
|
149
|
+
try:
|
|
150
|
+
os.chmod(pub_path, 0o644)
|
|
151
|
+
except OSError:
|
|
152
|
+
pass
|
|
153
|
+
return {
|
|
154
|
+
"backend": "age", "age_installed": True,
|
|
155
|
+
"key_path": str(key_path), "pub_path": str(pub_path),
|
|
156
|
+
"secrets_dir": str(sec),
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def vault_write(
|
|
161
|
+
home: str | Path,
|
|
162
|
+
ref: str,
|
|
163
|
+
value: str,
|
|
164
|
+
*,
|
|
165
|
+
project: str | None = None,
|
|
166
|
+
) -> Path:
|
|
167
|
+
"""Write a secret. age-encrypted when available, mode-600 plaintext otherwise.
|
|
168
|
+
|
|
169
|
+
With ``project=<slug>``, the secret is scoped to that project's vault at
|
|
170
|
+
``Agentik_Coding/projects/<slug>/.secrets/`` (encrypted to the SAME age
|
|
171
|
+
recipient as the global vault, so one key decrypts everything). Without
|
|
172
|
+
``project``, the global vault is used.
|
|
173
|
+
|
|
174
|
+
Returns the on-disk path written. Idempotent for a given (ref, value):
|
|
175
|
+
re-writing rotates the secret in place.
|
|
176
|
+
"""
|
|
177
|
+
ref = _safe_ref(ref)
|
|
178
|
+
if not isinstance(value, str) or not value:
|
|
179
|
+
raise ValueError("secret value must be a non-empty string")
|
|
180
|
+
|
|
181
|
+
home = Path(home)
|
|
182
|
+
# vault_init always operates on the global dir so the keypair is
|
|
183
|
+
# discoverable from any project.
|
|
184
|
+
info = vault_init(home)
|
|
185
|
+
sec = _secrets_dir(home, project=project)
|
|
186
|
+
|
|
187
|
+
if info["backend"] == "age":
|
|
188
|
+
pub = Path(info["pub_path"]).read_text().strip()
|
|
189
|
+
target = sec / f"{ref}.age"
|
|
190
|
+
proc = subprocess.run(
|
|
191
|
+
["age", "-r", pub, "-o", str(target)],
|
|
192
|
+
input=value, text=True, check=False, capture_output=True, timeout=30,
|
|
193
|
+
)
|
|
194
|
+
if proc.returncode != 0:
|
|
195
|
+
raise RuntimeError(
|
|
196
|
+
f"age encrypt failed for ref={ref}: {proc.stderr.strip()[:200]}"
|
|
197
|
+
)
|
|
198
|
+
try:
|
|
199
|
+
os.chmod(target, 0o600)
|
|
200
|
+
except OSError:
|
|
201
|
+
pass
|
|
202
|
+
# Best-effort cleanup of a previous plaintext for the same ref.
|
|
203
|
+
plain_old = sec / ref
|
|
204
|
+
if plain_old.exists():
|
|
205
|
+
try:
|
|
206
|
+
plain_old.unlink()
|
|
207
|
+
except OSError:
|
|
208
|
+
pass
|
|
209
|
+
return target
|
|
210
|
+
|
|
211
|
+
# Plain fallback.
|
|
212
|
+
target = sec / ref
|
|
213
|
+
fd = os.open(str(target), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
214
|
+
try:
|
|
215
|
+
with os.fdopen(fd, "w") as fh:
|
|
216
|
+
fh.write(value)
|
|
217
|
+
finally:
|
|
218
|
+
try:
|
|
219
|
+
os.close(fd)
|
|
220
|
+
except OSError:
|
|
221
|
+
pass
|
|
222
|
+
try:
|
|
223
|
+
os.chmod(target, stat.S_IRUSR | stat.S_IWUSR) # 0o600
|
|
224
|
+
except OSError:
|
|
225
|
+
pass
|
|
226
|
+
return target
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def vault_read(
|
|
230
|
+
home: str | Path,
|
|
231
|
+
ref: str,
|
|
232
|
+
*,
|
|
233
|
+
project: str | None = None,
|
|
234
|
+
) -> str | None:
|
|
235
|
+
"""Read a secret. Tries age then plain; returns None if neither exists.
|
|
236
|
+
|
|
237
|
+
``project=<slug>`` reads from the per-project vault.
|
|
238
|
+
|
|
239
|
+
Raises RuntimeError if an encrypted file exists but decryption fails
|
|
240
|
+
(so callers don't silently treat a broken vault as missing).
|
|
241
|
+
"""
|
|
242
|
+
ref = _safe_ref(ref)
|
|
243
|
+
home = Path(home)
|
|
244
|
+
sec = _secrets_dir(home, project=project)
|
|
245
|
+
age_file = sec / f"{ref}.age"
|
|
246
|
+
plain_file = sec / ref
|
|
247
|
+
|
|
248
|
+
if age_file.exists():
|
|
249
|
+
# The age key always lives in the global dir.
|
|
250
|
+
key = _global_key_dir(home) / ".vault-key"
|
|
251
|
+
if not key.exists():
|
|
252
|
+
raise RuntimeError(
|
|
253
|
+
f"encrypted secret {age_file} exists but vault key {key} is missing"
|
|
254
|
+
)
|
|
255
|
+
if not _age_available():
|
|
256
|
+
raise RuntimeError(
|
|
257
|
+
f"encrypted secret {age_file} exists but `age` is not installed"
|
|
258
|
+
)
|
|
259
|
+
proc = subprocess.run(
|
|
260
|
+
["age", "-d", "-i", str(key), str(age_file)],
|
|
261
|
+
check=False, capture_output=True, text=True, timeout=30,
|
|
262
|
+
)
|
|
263
|
+
if proc.returncode != 0:
|
|
264
|
+
raise RuntimeError(
|
|
265
|
+
f"age decrypt failed for ref={ref}: {proc.stderr.strip()[:200]}"
|
|
266
|
+
)
|
|
267
|
+
return proc.stdout
|
|
268
|
+
if plain_file.exists():
|
|
269
|
+
return plain_file.read_text()
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _file_has_real_secret(path: Path) -> bool:
|
|
274
|
+
"""True iff the file contains at least one non-comment KEY=VALUE line.
|
|
275
|
+
|
|
276
|
+
A file with only commented placeholders like
|
|
277
|
+
# COMPOSIO_API_KEY= # set this when ready
|
|
278
|
+
has nothing to leak and is not counted as a plaintext secret.
|
|
279
|
+
"""
|
|
280
|
+
try:
|
|
281
|
+
text = path.read_text(errors="replace")
|
|
282
|
+
except OSError:
|
|
283
|
+
return False
|
|
284
|
+
if not text.strip():
|
|
285
|
+
return False
|
|
286
|
+
for raw in text.splitlines():
|
|
287
|
+
ln = raw.strip()
|
|
288
|
+
if not ln or ln.startswith("#"):
|
|
289
|
+
continue
|
|
290
|
+
if "=" not in ln:
|
|
291
|
+
# A bare opaque value (e.g. an OAuth token written as the whole
|
|
292
|
+
# file body) still counts as a real secret.
|
|
293
|
+
return True
|
|
294
|
+
k, _, v = ln.partition("=")
|
|
295
|
+
if v.strip().strip('"').strip("'"):
|
|
296
|
+
return True
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def vault_status(home: str | Path, *, project: str | None = None) -> dict[str, Any]:
|
|
301
|
+
"""Summary of the vault for `omega doctor`.
|
|
302
|
+
|
|
303
|
+
With ``project=<slug>``, summarises the per-project vault only.
|
|
304
|
+
"""
|
|
305
|
+
home = Path(home)
|
|
306
|
+
sec = _secrets_dir(home, project=project)
|
|
307
|
+
info = vault_init(home)
|
|
308
|
+
files = [p for p in sec.iterdir() if p.is_file() and not p.name.startswith(".")]
|
|
309
|
+
encrypted = [p.name for p in files if p.suffix == ".age"]
|
|
310
|
+
plain_files = [p for p in files if p.suffix != ".age"]
|
|
311
|
+
# Discriminate real plaintext secrets from placeholder dotenv files.
|
|
312
|
+
real_plain = [p.name for p in plain_files if _file_has_real_secret(p)]
|
|
313
|
+
placeholders = [p.name for p in plain_files if p.name not in real_plain]
|
|
314
|
+
return {
|
|
315
|
+
"backend": info["backend"],
|
|
316
|
+
"age_installed": info.get("age_installed", False),
|
|
317
|
+
"encrypted_count": len(encrypted),
|
|
318
|
+
"plaintext_count": len(real_plain),
|
|
319
|
+
"placeholder_count": len(placeholders),
|
|
320
|
+
"secrets_dir": str(sec),
|
|
321
|
+
"project": project,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def list_project_vaults(home: str | Path) -> list[dict[str, Any]]:
|
|
326
|
+
"""Enumerate every per-project vault under
|
|
327
|
+
``Agentik_Coding/projects/<slug>/.secrets/``. Returns a list of vault
|
|
328
|
+
statuses, one per project that has any vault (encrypted or plain).
|
|
329
|
+
"""
|
|
330
|
+
home = Path(home)
|
|
331
|
+
projects_root = home / "Agentik_Coding" / "projects"
|
|
332
|
+
out: list[dict[str, Any]] = []
|
|
333
|
+
if not projects_root.is_dir():
|
|
334
|
+
return out
|
|
335
|
+
for proj_dir in sorted(projects_root.iterdir()):
|
|
336
|
+
if not proj_dir.is_dir():
|
|
337
|
+
continue
|
|
338
|
+
sec = proj_dir / ".secrets"
|
|
339
|
+
if not sec.is_dir():
|
|
340
|
+
continue
|
|
341
|
+
out.append(vault_status(home, project=proj_dir.name))
|
|
342
|
+
return out
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Inbound webhooks — GitHub / Linear / generic HMAC-SHA256.
|
|
2
|
+
|
|
3
|
+
The autonomous supervisor already has ``wake_webhook(path, payload)``. The
|
|
4
|
+
gap was the *HTTP front door*: nothing was listening on a port, nothing
|
|
5
|
+
verified signatures. This module is that front door.
|
|
6
|
+
|
|
7
|
+
Specs live in ``Agentik_SSOT/webhooks/webhooks.yaml`` (one path = one
|
|
8
|
+
spec). The engine daemon's HTTP server handles ``POST /webhook/<id>``:
|
|
9
|
+
it locates the spec, reads the source-specific signature header, looks
|
|
10
|
+
up the HMAC secret in the vault, verifies, and calls
|
|
11
|
+
``supervisor.wake_webhook(path, payload)``. On a valid signature the
|
|
12
|
+
caller gets ``202 Accepted`` immediately — the mission runs async.
|
|
13
|
+
|
|
14
|
+
We never accept an unsigned webhook. The default is ``source: generic``
|
|
15
|
+
which still requires HMAC-SHA256 in a configurable header.
|
|
16
|
+
|
|
17
|
+
Why we ship this small (no fancy framework):
|
|
18
|
+
|
|
19
|
+
* The engine daemon is already on a loopback port. The operator's
|
|
20
|
+
reverse proxy (nginx, Cloudflare Tunnel) is responsible for the
|
|
21
|
+
public face. We do auth, not networking.
|
|
22
|
+
* Signature verification is the only thing that matters — without it,
|
|
23
|
+
a webhook endpoint is a remote code execution waiting to happen.
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import hashlib
|
|
28
|
+
import hmac
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
from dataclasses import dataclass, field
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Any
|
|
34
|
+
|
|
35
|
+
import yaml
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Source-specific header names. The signature value's wire format is
|
|
39
|
+
# different per source; see _verify_source for the parsing.
|
|
40
|
+
_SIGNATURE_HEADER = {
|
|
41
|
+
"github": "X-Hub-Signature-256",
|
|
42
|
+
"linear": "Linear-Signature",
|
|
43
|
+
"generic": "X-Omega-Signature",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class WebhookSpec:
|
|
49
|
+
"""One inbound webhook endpoint."""
|
|
50
|
+
id: str
|
|
51
|
+
path: str # "/webhook/github-prs"
|
|
52
|
+
source: str = "generic" # "github" | "linear" | "generic"
|
|
53
|
+
secret_ref: str = "" # vault ref for the HMAC secret
|
|
54
|
+
description: str = ""
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def from_dict(d: dict[str, Any]) -> "WebhookSpec":
|
|
58
|
+
return WebhookSpec(
|
|
59
|
+
id=str(d["id"]),
|
|
60
|
+
path=str(d.get("path") or f"/webhook/{d['id']}"),
|
|
61
|
+
source=str(d.get("source", "generic")),
|
|
62
|
+
secret_ref=str(d.get("secret_ref",
|
|
63
|
+
f"WEBHOOK_{d['id'].upper().replace('-', '_')}")),
|
|
64
|
+
description=str(d.get("description", "")),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class WebhookRegistry:
|
|
70
|
+
"""All webhook specs known to the engine."""
|
|
71
|
+
specs: list[WebhookSpec] = field(default_factory=list)
|
|
72
|
+
|
|
73
|
+
def by_path(self, path: str) -> WebhookSpec | None:
|
|
74
|
+
for s in self.specs:
|
|
75
|
+
if s.path == path:
|
|
76
|
+
return s
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
def by_id(self, id: str) -> WebhookSpec | None:
|
|
80
|
+
for s in self.specs:
|
|
81
|
+
if s.id == id:
|
|
82
|
+
return s
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def load_registry(omega_home: str | Path) -> WebhookRegistry:
|
|
87
|
+
"""Read ``Agentik_SSOT/webhooks/webhooks.yaml``. Empty if missing."""
|
|
88
|
+
path = (Path(omega_home) / "Agentik_SSOT" / "webhooks" / "webhooks.yaml")
|
|
89
|
+
if not path.exists():
|
|
90
|
+
return WebhookRegistry()
|
|
91
|
+
data = yaml.safe_load(path.read_text()) or {}
|
|
92
|
+
specs = [
|
|
93
|
+
WebhookSpec.from_dict(d)
|
|
94
|
+
for d in (data.get("webhooks") or [])
|
|
95
|
+
if isinstance(d, dict) and d.get("id")
|
|
96
|
+
]
|
|
97
|
+
return WebhookRegistry(specs=specs)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def signature_header_for(source: str) -> str:
|
|
101
|
+
"""Return the header name the signature is expected in."""
|
|
102
|
+
return _SIGNATURE_HEADER.get(source.lower(), _SIGNATURE_HEADER["generic"])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def verify_signature(
|
|
106
|
+
source: str,
|
|
107
|
+
body: bytes,
|
|
108
|
+
signature_header_value: str | None,
|
|
109
|
+
secret: str,
|
|
110
|
+
*,
|
|
111
|
+
timestamp_header_value: str | None = None,
|
|
112
|
+
replay_window_s: int = 300,
|
|
113
|
+
now_s: float | None = None,
|
|
114
|
+
) -> tuple[bool, str]:
|
|
115
|
+
"""Return (ok, reason) for a webhook signature.
|
|
116
|
+
|
|
117
|
+
Hard rule: missing signature → False. Missing secret → False. We
|
|
118
|
+
NEVER let an unsigned webhook through.
|
|
119
|
+
|
|
120
|
+
Replay protection
|
|
121
|
+
-----------------
|
|
122
|
+
When ``timestamp_header_value`` is provided, we require the timestamp
|
|
123
|
+
to be within ``replay_window_s`` seconds of ``now_s``. An attacker who
|
|
124
|
+
captured a valid signed body can otherwise replay it forever. Callers
|
|
125
|
+
SHOULD pass the timestamp header (X-Omega-Timestamp / X-Hub-Timestamp /
|
|
126
|
+
Linear-Delivery-Timestamp); when absent we accept the request (HMAC
|
|
127
|
+
alone is the v0.5 contract; timestamp is additive).
|
|
128
|
+
|
|
129
|
+
Source-specific notes:
|
|
130
|
+
|
|
131
|
+
* ``github`` ships ``X-Hub-Signature-256: sha256=<hex>``.
|
|
132
|
+
* ``linear`` ships ``Linear-Signature: <hex>`` (no prefix).
|
|
133
|
+
* ``generic`` expects ``X-Omega-Signature: sha256=<hex>``.
|
|
134
|
+
"""
|
|
135
|
+
if not signature_header_value:
|
|
136
|
+
return False, "missing signature header"
|
|
137
|
+
if not secret:
|
|
138
|
+
return False, "secret not set in vault"
|
|
139
|
+
|
|
140
|
+
# Replay window check (additive, off when no timestamp supplied).
|
|
141
|
+
if timestamp_header_value is not None:
|
|
142
|
+
try:
|
|
143
|
+
ts = float(timestamp_header_value)
|
|
144
|
+
except (TypeError, ValueError):
|
|
145
|
+
return False, "timestamp header is not numeric"
|
|
146
|
+
import time as _t
|
|
147
|
+
wall = now_s if now_s is not None else _t.time()
|
|
148
|
+
if abs(wall - ts) > replay_window_s:
|
|
149
|
+
return False, (
|
|
150
|
+
f"timestamp outside ±{replay_window_s}s replay window "
|
|
151
|
+
f"(now={wall:.0f} hdr={ts:.0f})"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
src = source.lower()
|
|
155
|
+
raw_sig = signature_header_value.strip()
|
|
156
|
+
if src in ("github", "generic"):
|
|
157
|
+
# `sha256=<hex>` form
|
|
158
|
+
if not raw_sig.startswith("sha256="):
|
|
159
|
+
return False, "signature missing sha256= prefix"
|
|
160
|
+
provided = raw_sig.split("=", 1)[1]
|
|
161
|
+
elif src == "linear":
|
|
162
|
+
# raw hex
|
|
163
|
+
provided = raw_sig
|
|
164
|
+
else:
|
|
165
|
+
return False, f"unknown source {source!r}"
|
|
166
|
+
|
|
167
|
+
mac = hmac.new(
|
|
168
|
+
secret.encode("utf-8"), body, hashlib.sha256,
|
|
169
|
+
).hexdigest()
|
|
170
|
+
if not hmac.compare_digest(mac, provided):
|
|
171
|
+
return False, "signature mismatch"
|
|
172
|
+
return True, "ok"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _load_secret(omega_home: str | Path, secret_ref: str) -> str:
|
|
176
|
+
"""Best-effort vault read."""
|
|
177
|
+
if not secret_ref:
|
|
178
|
+
return ""
|
|
179
|
+
try:
|
|
180
|
+
from omega_engine.vault import vault_read
|
|
181
|
+
v = vault_read(omega_home, secret_ref)
|
|
182
|
+
return v or ""
|
|
183
|
+
except Exception: # noqa: BLE001
|
|
184
|
+
return ""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def handle_webhook(
|
|
188
|
+
omega_home: str | Path,
|
|
189
|
+
*,
|
|
190
|
+
path: str,
|
|
191
|
+
headers: dict[str, str],
|
|
192
|
+
body: bytes,
|
|
193
|
+
supervisor=None,
|
|
194
|
+
) -> dict[str, Any]:
|
|
195
|
+
"""Run the gate for one inbound webhook.
|
|
196
|
+
|
|
197
|
+
Returns ``{status: int, ok: bool, reason: str, fired: [charter_ids]}``.
|
|
198
|
+
Status codes: 200 (already-known but no charter fired), 202 (charters
|
|
199
|
+
fired), 401 (bad signature), 404 (unknown path), 500 (internal).
|
|
200
|
+
|
|
201
|
+
``supervisor`` is optional — pass an ``AutonomousSupervisor`` to
|
|
202
|
+
actually fire charters. Without one, we still verify but don't fire
|
|
203
|
+
(useful for ``omega webhook test``).
|
|
204
|
+
"""
|
|
205
|
+
home = Path(omega_home)
|
|
206
|
+
registry = load_registry(home)
|
|
207
|
+
spec = registry.by_path(path)
|
|
208
|
+
if spec is None:
|
|
209
|
+
return {"status": 404, "ok": False, "reason": "no spec for path",
|
|
210
|
+
"fired": []}
|
|
211
|
+
|
|
212
|
+
secret = _load_secret(home, spec.secret_ref)
|
|
213
|
+
sig_header_name = signature_header_for(spec.source)
|
|
214
|
+
sig_value = None
|
|
215
|
+
# Headers come from a stdlib BaseHTTPRequestHandler which lower-cases
|
|
216
|
+
# access via dict-style only when wrapped; here we normalise both sides.
|
|
217
|
+
for k, v in headers.items():
|
|
218
|
+
if k.lower() == sig_header_name.lower():
|
|
219
|
+
sig_value = v
|
|
220
|
+
break
|
|
221
|
+
ok, reason = verify_signature(spec.source, body, sig_value, secret)
|
|
222
|
+
if not ok:
|
|
223
|
+
return {"status": 401, "ok": False, "reason": reason, "fired": []}
|
|
224
|
+
|
|
225
|
+
# Parse payload best-effort; webhooks SHOULD be JSON.
|
|
226
|
+
try:
|
|
227
|
+
payload = json.loads(body.decode("utf-8") or "{}")
|
|
228
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
229
|
+
payload = {"_raw": body.decode("utf-8", errors="replace")[:1000]}
|
|
230
|
+
|
|
231
|
+
fired: list[str] = []
|
|
232
|
+
if supervisor is not None:
|
|
233
|
+
try:
|
|
234
|
+
fired = supervisor.wake_webhook(path, payload) or []
|
|
235
|
+
except Exception as exc: # noqa: BLE001
|
|
236
|
+
return {
|
|
237
|
+
"status": 500, "ok": False,
|
|
238
|
+
"reason": f"supervisor raised: {exc}"[:300],
|
|
239
|
+
"fired": [],
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
"status": 202 if fired else 200,
|
|
244
|
+
"ok": True,
|
|
245
|
+
"reason": "ok",
|
|
246
|
+
"fired": fired,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def generate_test_signature(source: str, body: bytes, secret: str) -> str:
|
|
251
|
+
"""Build a valid signature header value for the given source.
|
|
252
|
+
|
|
253
|
+
Used by ``omega webhook test`` and by the integration tests so we can
|
|
254
|
+
POST a synthetic webhook to the engine and confirm the gate works.
|
|
255
|
+
"""
|
|
256
|
+
mac = hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
|
|
257
|
+
src = source.lower()
|
|
258
|
+
if src in ("github", "generic"):
|
|
259
|
+
return f"sha256={mac}"
|
|
260
|
+
if src == "linear":
|
|
261
|
+
return mac
|
|
262
|
+
raise ValueError(f"unknown source: {source}")
|