@agentikos/omega-os 0.1.0 → 0.19.5
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 +56 -14
- 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 +1000 -26
- package/bootstrap/manifest.example.yaml +93 -2
- 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/COMPLETION-PLAN.md +48 -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 +27 -10
- package/omega/Agentik_Engine/omega_engine/__init__.py +212 -2
- 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_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__/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__/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__/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__/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__/sync.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 +502 -0
- 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/autonomous.py +538 -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 +4564 -56
- 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/__init__.py +14 -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/autonomous.py +56 -0
- package/omega/Agentik_Engine/omega_engine/daemons/engine.py +236 -0
- package/omega/Agentik_Engine/omega_engine/daemons/telegram.py +315 -0
- package/omega/Agentik_Engine/omega_engine/done_signal.py +154 -0
- package/omega/Agentik_Engine/omega_engine/educators/__init__.py +51 -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/educators/artifact.py +65 -0
- package/omega/Agentik_Engine/omega_engine/educators/automation.py +76 -0
- package/omega/Agentik_Engine/omega_engine/educators/base.py +327 -0
- package/omega/Agentik_Engine/omega_engine/educators/claudecode.py +71 -0
- package/omega/Agentik_Engine/omega_engine/educators/connection.py +75 -0
- package/omega/Agentik_Engine/omega_engine/educators/coworker.py +68 -0
- package/omega/Agentik_Engine/omega_engine/educators/loop.py +82 -0
- package/omega/Agentik_Engine/omega_engine/educators/prompt.py +68 -0
- package/omega/Agentik_Engine/omega_engine/educators/skill.py +69 -0
- package/omega/Agentik_Engine/omega_engine/envelope.py +219 -0
- package/omega/Agentik_Engine/omega_engine/executor.py +195 -16
- 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 +29 -14
- 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 +408 -13
- 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/__init__.py +21 -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/rag/agentic.py +83 -0
- package/omega/Agentik_Engine/omega_engine/rag/base.py +42 -0
- package/omega/Agentik_Engine/omega_engine/rag/corrective.py +119 -0
- package/omega/Agentik_Engine/omega_engine/rag/graph.py +169 -0
- package/omega/Agentik_Engine/omega_engine/rag/hybrid.py +205 -0
- package/omega/Agentik_Engine/omega_engine/rag/multimodal.py +136 -0
- package/omega/Agentik_Engine/omega_engine/rag/router.py +110 -0
- package/omega/Agentik_Engine/omega_engine/reducer.py +21 -3
- 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 +132 -25
- package/omega/Agentik_Engine/omega_engine/sync.py +445 -0
- 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/tools.py +272 -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_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_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_progress.cpython-313-pytest-8.4.2.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_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_report.cpython-313-pytest-8.4.2.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 +338 -0
- 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_autonomous.py +361 -0
- package/omega/Agentik_Engine/tests/test_educators.py +233 -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_rag.py +287 -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_snapshot_partial.py +172 -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_tools_and_sync.py +312 -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/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/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/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/rag-route.md +82 -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,186 @@
|
|
|
1
|
+
"""Validate provider credentials at install time + on demand.
|
|
2
|
+
|
|
3
|
+
The install used to write provider config + credentials to the vault and
|
|
4
|
+
then say "ok". The first mission would discover the key was wrong/expired
|
|
5
|
+
and fail. This module closes the gap: ``omega validate providers`` does a
|
|
6
|
+
1-token round-trip against each configured provider's real API.
|
|
7
|
+
|
|
8
|
+
For each provider we know how to validate:
|
|
9
|
+
|
|
10
|
+
* **claude** ``/v1/messages`` with max_tokens=1
|
|
11
|
+
* **glm** ``/api/paas/v4/chat/completions`` with max_tokens=1
|
|
12
|
+
* **openai** ``/v1/chat/completions`` with max_tokens=1
|
|
13
|
+
* **deepseek** ``/v1/chat/completions`` with max_tokens=1
|
|
14
|
+
|
|
15
|
+
Each provider's adapter already knows its endpoint + auth shape — we
|
|
16
|
+
reuse it. A 200 OK is a pass; anything else is a fail with the API's
|
|
17
|
+
error string surfaced.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import yaml
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ValidationResult:
|
|
31
|
+
provider: str
|
|
32
|
+
ok: bool
|
|
33
|
+
detail: str
|
|
34
|
+
elapsed_ms: int = 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _read_router_config(omega_home: str | Path) -> list[dict[str, Any]]:
|
|
38
|
+
p = (Path(omega_home) / "Agentik_SSOT" / "providers" / "router.yaml")
|
|
39
|
+
if not p.exists():
|
|
40
|
+
return []
|
|
41
|
+
data = yaml.safe_load(p.read_text()) or {}
|
|
42
|
+
return list(data.get("providers") or [])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _read_secret(omega_home: str | Path, secret_ref: str) -> str:
|
|
46
|
+
if not secret_ref:
|
|
47
|
+
return ""
|
|
48
|
+
if env := os.environ.get(secret_ref):
|
|
49
|
+
return env
|
|
50
|
+
try:
|
|
51
|
+
from omega_engine.vault import vault_read
|
|
52
|
+
return vault_read(omega_home, secret_ref) or ""
|
|
53
|
+
except Exception: # noqa: BLE001
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _validate_claude(api_key: str) -> ValidationResult:
|
|
58
|
+
"""1-token call against Anthropic /v1/messages."""
|
|
59
|
+
if not api_key:
|
|
60
|
+
return ValidationResult("claude", False, "no API key in env or vault")
|
|
61
|
+
import json
|
|
62
|
+
import time
|
|
63
|
+
import urllib.error
|
|
64
|
+
import urllib.request
|
|
65
|
+
start = time.time()
|
|
66
|
+
body = json.dumps({
|
|
67
|
+
"model": "claude-haiku-4-5",
|
|
68
|
+
"max_tokens": 1,
|
|
69
|
+
"messages": [{"role": "user", "content": "x"}],
|
|
70
|
+
}).encode()
|
|
71
|
+
req = urllib.request.Request(
|
|
72
|
+
"https://api.anthropic.com/v1/messages",
|
|
73
|
+
data=body, method="POST",
|
|
74
|
+
headers={
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
"x-api-key": api_key,
|
|
77
|
+
"anthropic-version": "2023-06-01",
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
try:
|
|
81
|
+
with urllib.request.urlopen(req, timeout=15) as resp: # noqa: S310
|
|
82
|
+
resp.read()
|
|
83
|
+
return ValidationResult(
|
|
84
|
+
"claude", True, "ok", int((time.time() - start) * 1000),
|
|
85
|
+
)
|
|
86
|
+
except urllib.error.HTTPError as exc:
|
|
87
|
+
detail = exc.read().decode("utf-8", errors="replace")[:300] if exc.fp else ""
|
|
88
|
+
return ValidationResult(
|
|
89
|
+
"claude", False, f"HTTP {exc.code}: {detail or exc.reason}",
|
|
90
|
+
int((time.time() - start) * 1000),
|
|
91
|
+
)
|
|
92
|
+
except Exception as exc: # noqa: BLE001
|
|
93
|
+
return ValidationResult(
|
|
94
|
+
"claude", False, f"network error: {exc}"[:200],
|
|
95
|
+
int((time.time() - start) * 1000),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _validate_openai_compat(
|
|
100
|
+
provider: str, api_key: str, base_url: str, model: str,
|
|
101
|
+
) -> ValidationResult:
|
|
102
|
+
"""1-token call against any OpenAI-compatible /chat/completions."""
|
|
103
|
+
if not api_key:
|
|
104
|
+
return ValidationResult(provider, False, "no API key in env or vault")
|
|
105
|
+
import json
|
|
106
|
+
import time
|
|
107
|
+
import urllib.error
|
|
108
|
+
import urllib.request
|
|
109
|
+
start = time.time()
|
|
110
|
+
body = json.dumps({
|
|
111
|
+
"model": model,
|
|
112
|
+
"max_tokens": 1,
|
|
113
|
+
"messages": [{"role": "user", "content": "x"}],
|
|
114
|
+
}).encode()
|
|
115
|
+
req = urllib.request.Request(
|
|
116
|
+
f"{base_url.rstrip('/')}/chat/completions",
|
|
117
|
+
data=body, method="POST",
|
|
118
|
+
headers={
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"Authorization": f"Bearer {api_key}",
|
|
121
|
+
},
|
|
122
|
+
)
|
|
123
|
+
try:
|
|
124
|
+
with urllib.request.urlopen(req, timeout=15) as resp: # noqa: S310
|
|
125
|
+
resp.read()
|
|
126
|
+
return ValidationResult(
|
|
127
|
+
provider, True, "ok", int((time.time() - start) * 1000),
|
|
128
|
+
)
|
|
129
|
+
except urllib.error.HTTPError as exc:
|
|
130
|
+
detail = exc.read().decode("utf-8", errors="replace")[:300] if exc.fp else ""
|
|
131
|
+
return ValidationResult(
|
|
132
|
+
provider, False, f"HTTP {exc.code}: {detail or exc.reason}",
|
|
133
|
+
int((time.time() - start) * 1000),
|
|
134
|
+
)
|
|
135
|
+
except Exception as exc: # noqa: BLE001
|
|
136
|
+
return ValidationResult(
|
|
137
|
+
provider, False, f"network error: {exc}"[:200],
|
|
138
|
+
int((time.time() - start) * 1000),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def validate_one(
|
|
143
|
+
omega_home: str | Path, provider_id: str,
|
|
144
|
+
*, secret_ref: str | None = None,
|
|
145
|
+
) -> ValidationResult:
|
|
146
|
+
"""Validate one provider by id. Reads secret from env or vault."""
|
|
147
|
+
secret_ref = (
|
|
148
|
+
secret_ref or f"{provider_id.upper()}_API_KEY"
|
|
149
|
+
)
|
|
150
|
+
key = _read_secret(omega_home, secret_ref)
|
|
151
|
+
pid = provider_id.lower()
|
|
152
|
+
if pid == "claude":
|
|
153
|
+
return _validate_claude(key)
|
|
154
|
+
if pid == "glm":
|
|
155
|
+
return _validate_openai_compat(
|
|
156
|
+
"glm", key,
|
|
157
|
+
"https://open.bigmodel.cn/api/paas/v4",
|
|
158
|
+
"glm-4-flash",
|
|
159
|
+
)
|
|
160
|
+
if pid == "openai":
|
|
161
|
+
return _validate_openai_compat(
|
|
162
|
+
"openai", key, "https://api.openai.com/v1", "gpt-4o-mini",
|
|
163
|
+
)
|
|
164
|
+
if pid == "deepseek":
|
|
165
|
+
return _validate_openai_compat(
|
|
166
|
+
"deepseek", key, "https://api.deepseek.com/v1", "deepseek-chat",
|
|
167
|
+
)
|
|
168
|
+
return ValidationResult(
|
|
169
|
+
provider_id, False,
|
|
170
|
+
f"no validator known for provider {provider_id!r}",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def validate_all(omega_home: str | Path) -> list[ValidationResult]:
|
|
175
|
+
"""Validate every provider in ``router.yaml``."""
|
|
176
|
+
out: list[ValidationResult] = []
|
|
177
|
+
for cfg in _read_router_config(omega_home):
|
|
178
|
+
if not bool(cfg.get("enabled", True)):
|
|
179
|
+
continue
|
|
180
|
+
pid = str(cfg.get("id", "")).strip()
|
|
181
|
+
if not pid:
|
|
182
|
+
continue
|
|
183
|
+
out.append(validate_one(
|
|
184
|
+
omega_home, pid, secret_ref=cfg.get("secret_ref"),
|
|
185
|
+
))
|
|
186
|
+
return out
|
|
@@ -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
|