@trac3er/oh-my-god 2.1.0 → 2.1.4
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/.agents/skills/omg/AGENTS.fragment.md +2 -2
- package/.agents/skills/omg/algorithms/SKILL.md +1 -1
- package/.agents/skills/omg/algorithms/openai.yaml +1 -1
- package/.agents/skills/omg/api-twin/SKILL.md +1 -1
- package/.agents/skills/omg/api-twin/openai.yaml +1 -1
- package/.agents/skills/omg/claim-judge/SKILL.md +1 -1
- package/.agents/skills/omg/claim-judge/openai.yaml +1 -1
- package/.agents/skills/omg/codex-rules.md +1 -1
- package/.agents/skills/omg/control-plane/SKILL.md +1 -1
- package/.agents/skills/omg/control-plane/openai.yaml +1 -1
- package/.agents/skills/omg/data-lineage/SKILL.md +1 -1
- package/.agents/skills/omg/data-lineage/openai.yaml +1 -1
- package/.agents/skills/omg/delta-classifier/SKILL.md +1 -1
- package/.agents/skills/omg/delta-classifier/openai.yaml +1 -1
- package/.agents/skills/omg/eval-gate/SKILL.md +1 -1
- package/.agents/skills/omg/eval-gate/openai.yaml +1 -1
- package/.agents/skills/omg/health/SKILL.md +1 -1
- package/.agents/skills/omg/health/openai.yaml +1 -1
- package/.agents/skills/omg/hook-governor/SKILL.md +1 -1
- package/.agents/skills/omg/hook-governor/openai.yaml +1 -1
- package/.agents/skills/omg/incident-replay/SKILL.md +1 -1
- package/.agents/skills/omg/incident-replay/openai.yaml +1 -1
- package/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
- package/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
- package/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
- package/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
- package/.agents/skills/omg/plan-council/SKILL.md +1 -1
- package/.agents/skills/omg/plan-council/openai.yaml +1 -1
- package/.agents/skills/omg/preflight/SKILL.md +1 -1
- package/.agents/skills/omg/preflight/openai.yaml +1 -1
- package/.agents/skills/omg/proof-gate/SKILL.md +1 -1
- package/.agents/skills/omg/proof-gate/openai.yaml +1 -1
- package/.agents/skills/omg/remote-supervisor/SKILL.md +1 -1
- package/.agents/skills/omg/remote-supervisor/openai.yaml +1 -1
- package/.agents/skills/omg/robotics/SKILL.md +1 -1
- package/.agents/skills/omg/robotics/openai.yaml +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
- package/.agents/skills/omg/security-check/SKILL.md +1 -1
- package/.agents/skills/omg/security-check/openai.yaml +1 -1
- package/.agents/skills/omg/test-intent-lock/SKILL.md +1 -1
- package/.agents/skills/omg/test-intent-lock/openai.yaml +1 -1
- package/.agents/skills/omg/tracebank/SKILL.md +1 -1
- package/.agents/skills/omg/tracebank/openai.yaml +1 -1
- package/.agents/skills/omg/vision/SKILL.md +1 -1
- package/.agents/skills/omg/vision/openai.yaml +1 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +16 -0
- package/CLI-ADAPTER-MAP.md +3 -3
- package/OMG-setup.sh +114 -1
- package/OMG_COMPAT_CONTRACT.md +1 -1
- package/README.md +2 -1
- package/artifacts/release/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/settings.json +3 -3
- package/artifacts/release/dist/enterprise/manifest.json +30 -40
- package/artifacts/release/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/public/bundle/settings.json +3 -3
- package/artifacts/release/dist/public/manifest.json +30 -40
- package/artifacts/release/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/health.yaml +1 -1
- package/artifacts/release/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/settings.json +3 -3
- package/build/lib/commands/OMG:profile-review.md +43 -0
- package/build/lib/commands/OMG:validate.md +59 -0
- package/build/lib/control_plane/service.py +40 -1
- package/build/lib/hooks/_agent_registry.py +2 -0
- package/build/lib/hooks/firewall.py +263 -4
- package/build/lib/hooks/intentgate-keyword-detector.py +15 -3
- package/build/lib/hooks/policy_engine.py +77 -0
- package/build/lib/hooks/prompt-enhancer.py +146 -0
- package/build/lib/hooks/session-end-capture.py +374 -6
- package/build/lib/hooks/session-start.py +13 -35
- package/build/lib/hooks/setup_wizard.py +333 -1
- package/build/lib/hooks/shadow_manager.py +111 -2
- package/build/lib/hooks/stop_dispatcher.py +108 -4
- package/build/lib/plugins/advanced/plugin.json +26 -9
- package/build/lib/plugins/core/plugin.json +44 -11
- package/build/lib/registry/bundles/algorithms.yaml +1 -1
- package/build/lib/registry/bundles/api-twin.yaml +1 -1
- package/build/lib/registry/bundles/claim-judge.yaml +1 -1
- package/build/lib/registry/bundles/control-plane.yaml +1 -1
- package/build/lib/registry/bundles/data-lineage.yaml +1 -1
- package/build/lib/registry/bundles/delta-classifier.yaml +1 -1
- package/build/lib/registry/bundles/eval-gate.yaml +1 -1
- package/build/lib/registry/bundles/health.yaml +1 -1
- package/build/lib/registry/bundles/hook-governor.yaml +1 -1
- package/build/lib/registry/bundles/incident-replay.yaml +1 -1
- package/build/lib/registry/bundles/lsp-pack.yaml +1 -1
- package/build/lib/registry/bundles/mcp-fabric.yaml +1 -1
- package/build/lib/registry/bundles/plan-council.yaml +1 -1
- package/build/lib/registry/bundles/preflight.yaml +1 -1
- package/build/lib/registry/bundles/proof-gate.yaml +1 -1
- package/build/lib/registry/bundles/remote-supervisor.yaml +1 -1
- package/build/lib/registry/bundles/robotics.yaml +1 -1
- package/build/lib/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/build/lib/registry/bundles/security-check.yaml +1 -1
- package/build/lib/registry/bundles/test-intent-lock.yaml +1 -1
- package/build/lib/registry/bundles/tracebank.yaml +1 -1
- package/build/lib/registry/bundles/vision.yaml +1 -1
- package/build/lib/registry/omg-capability.schema.json +1 -1
- package/build/lib/runtime/adoption.py +1 -1
- package/build/lib/runtime/background_verification.py +62 -0
- package/build/lib/runtime/claim_judge.py +165 -5
- package/build/lib/runtime/compat.py +15 -1
- package/build/lib/runtime/context_engine.py +261 -4
- package/build/lib/runtime/contract_compiler.py +370 -93
- package/build/lib/runtime/defense_state.py +79 -8
- package/build/lib/runtime/delta_classifier.py +80 -2
- package/build/lib/runtime/evidence_query.py +56 -4
- package/build/lib/runtime/evidence_registry.py +16 -0
- package/build/lib/runtime/evidence_requirements.py +47 -0
- package/build/lib/runtime/forge_agents.py +179 -9
- package/build/lib/runtime/forge_contracts.py +208 -2
- package/build/lib/runtime/interaction_journal.py +318 -42
- package/build/lib/runtime/memory_store.py +134 -1
- package/build/lib/runtime/mutation_gate.py +57 -11
- package/build/lib/runtime/omg_compat_contract_snapshot.json +2 -2
- package/build/lib/runtime/omg_mcp_server.py +14 -4
- package/build/lib/runtime/preflight.py +14 -0
- package/build/lib/runtime/profile_io.py +294 -0
- package/build/lib/runtime/proof_gate.py +122 -7
- package/build/lib/runtime/release_run_coordinator.py +339 -0
- package/build/lib/runtime/release_surfaces.py +243 -0
- package/build/lib/runtime/rollback_manifest.py +136 -0
- package/build/lib/runtime/router_critics.py +50 -0
- package/build/lib/runtime/runtime_contracts.py +137 -0
- package/build/lib/runtime/security_check.py +16 -0
- package/build/lib/runtime/session_health.py +60 -16
- package/build/lib/runtime/team_router.py +156 -13
- package/build/lib/runtime/test_intent_lock.py +102 -0
- package/build/lib/runtime/tool_plan_gate.py +249 -24
- package/build/lib/runtime/validate.py +194 -0
- package/build/lib/runtime/verification_controller.py +52 -2
- package/commands/OMG:diagnose-plugins.md +33 -0
- package/commands/OMG:profile-review.md +43 -0
- package/commands/OMG:setup.md +4 -1
- package/commands/OMG:validate.md +59 -0
- package/control_plane/service.py +40 -1
- package/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/enterprise/bundle/settings.json +3 -3
- package/dist/enterprise/manifest.json +30 -40
- package/dist/oh_my_god-2.1.4-py3-none-any.whl +0 -0
- package/dist/oh_my_god-2.1.4.tar.gz +0 -0
- package/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/public/bundle/settings.json +3 -3
- package/dist/public/manifest.json +30 -40
- package/docs/install/opencode.md +30 -0
- package/docs/proof.md +11 -0
- package/docs/release-checklist.md +14 -1
- package/hooks/_agent_registry.py +2 -0
- package/hooks/firewall.py +263 -4
- package/hooks/intentgate-keyword-detector.py +15 -3
- package/hooks/policy_engine.py +77 -0
- package/hooks/prompt-enhancer.py +146 -0
- package/hooks/session-end-capture.py +374 -6
- package/hooks/session-start.py +13 -35
- package/hooks/setup_wizard.py +342 -2
- package/hooks/shadow_manager.py +111 -2
- package/hooks/stop_dispatcher.py +108 -4
- package/hud/omg-hud.mjs +30 -6
- package/lab/pipeline.py +197 -26
- package/package.json +1 -1
- package/plugins/advanced/plugin.json +26 -9
- package/plugins/core/plugin.json +51 -12
- package/pyproject.toml +10 -2
- package/registry/bundles/algorithms.yaml +1 -1
- package/registry/bundles/api-twin.yaml +1 -1
- package/registry/bundles/claim-judge.yaml +1 -1
- package/registry/bundles/control-plane.yaml +1 -1
- package/registry/bundles/data-lineage.yaml +1 -1
- package/registry/bundles/delta-classifier.yaml +1 -1
- package/registry/bundles/eval-gate.yaml +1 -1
- package/registry/bundles/health.yaml +1 -1
- package/registry/bundles/hook-governor.yaml +1 -1
- package/registry/bundles/incident-replay.yaml +1 -1
- package/registry/bundles/lsp-pack.yaml +1 -1
- package/registry/bundles/mcp-fabric.yaml +1 -1
- package/registry/bundles/plan-council.yaml +1 -1
- package/registry/bundles/preflight.yaml +1 -1
- package/registry/bundles/proof-gate.yaml +1 -1
- package/registry/bundles/remote-supervisor.yaml +1 -1
- package/registry/bundles/robotics.yaml +1 -1
- package/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/registry/bundles/security-check.yaml +1 -1
- package/registry/bundles/test-intent-lock.yaml +1 -1
- package/registry/bundles/tracebank.yaml +1 -1
- package/registry/bundles/vision.yaml +1 -1
- package/registry/omg-capability.schema.json +1 -1
- package/runtime/adoption.py +7 -5
- package/runtime/background_verification.py +62 -0
- package/runtime/claim_judge.py +165 -5
- package/runtime/compat.py +48 -1
- package/runtime/context_engine.py +261 -4
- package/runtime/contract_compiler.py +370 -93
- package/runtime/defense_state.py +79 -8
- package/runtime/delta_classifier.py +80 -2
- package/runtime/evidence_query.py +56 -4
- package/runtime/evidence_registry.py +16 -0
- package/runtime/evidence_requirements.py +47 -0
- package/runtime/forge_agents.py +179 -9
- package/runtime/forge_contracts.py +208 -2
- package/runtime/hook_governor.py +19 -1
- package/runtime/interaction_journal.py +318 -42
- package/runtime/memory_store.py +134 -1
- package/runtime/mutation_gate.py +57 -11
- package/runtime/omg_compat_contract_snapshot.json +2 -2
- package/runtime/omg_mcp_server.py +14 -4
- package/runtime/plugin_diagnostics.py +248 -0
- package/runtime/plugin_interop.py +1007 -0
- package/runtime/preflight.py +14 -0
- package/runtime/profile_io.py +294 -0
- package/runtime/proof_gate.py +122 -7
- package/runtime/providers/opencode_provider.py +94 -0
- package/runtime/release_run_coordinator.py +339 -0
- package/runtime/release_surfaces.py +243 -0
- package/runtime/rollback_manifest.py +136 -0
- package/runtime/router_critics.py +50 -0
- package/runtime/runtime_contracts.py +137 -0
- package/runtime/security_check.py +16 -0
- package/runtime/session_health.py +60 -16
- package/runtime/team_router.py +156 -13
- package/runtime/test_intent_lock.py +102 -0
- package/runtime/tool_plan_gate.py +218 -19
- package/runtime/validate.py +289 -0
- package/runtime/verification_controller.py +52 -2
- package/scripts/omg.py +306 -3
- package/scripts/prepare-release-proof-fixtures.py +191 -7
- package/scripts/print-canonical-version.py +80 -0
- package/scripts/sync-release-identity.py +476 -0
- package/scripts/validate-release-identity.py +246 -0
- package/scripts/verify-standalone.sh +3 -0
- package/settings.json +3 -3
- package/templates/plugins-allowlist.yaml +19 -0
- package/artifacts/release/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/artifacts/release/dist/public/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/public/bundle/.kimi/mcp.json +0 -11
- package/build/lib/yaml.py +0 -321
- package/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/dist/public/bundle/.gemini/settings.json +0 -11
- package/dist/public/bundle/.kimi/mcp.json +0 -11
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read-only release identity validator.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 scripts/validate-release-identity.py --scope all --forbid-version 2.1.1
|
|
6
|
+
python3 scripts/validate-release-identity.py --scope authored
|
|
7
|
+
python3 scripts/validate-release-identity.py --scope derived
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import ast
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
20
|
+
if str(_REPO_ROOT) not in sys.path:
|
|
21
|
+
sys.path.insert(0, str(_REPO_ROOT))
|
|
22
|
+
|
|
23
|
+
from runtime.release_surfaces import AUTHORED_SURFACES, SCOPED_RESIDUE_TARGETS
|
|
24
|
+
|
|
25
|
+
import importlib.util
|
|
26
|
+
|
|
27
|
+
_SYNC_SCRIPT = _REPO_ROOT / "scripts" / "sync-release-identity.py"
|
|
28
|
+
_sync_spec = importlib.util.spec_from_file_location("sync_release_identity", _SYNC_SCRIPT)
|
|
29
|
+
assert _sync_spec is not None and _sync_spec.loader is not None
|
|
30
|
+
_sync_mod = importlib.util.module_from_spec(_sync_spec)
|
|
31
|
+
_sync_spec.loader.exec_module(_sync_mod)
|
|
32
|
+
|
|
33
|
+
check_surface = _sync_mod.check_surface
|
|
34
|
+
extract_canonical_version = _sync_mod.extract_canonical_version
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_DERIVED_JSON_SURFACES: list[tuple[str, list[str]]] = [
|
|
38
|
+
("dist/public/manifest.json", ["contract_version"]),
|
|
39
|
+
("dist/enterprise/manifest.json", ["contract_version"]),
|
|
40
|
+
("artifacts/release/dist/public/manifest.json", ["contract_version"]),
|
|
41
|
+
("artifacts/release/dist/enterprise/manifest.json", ["contract_version"]),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
_DERIVED_AST_SURFACE = "build/lib/runtime/adoption.py"
|
|
45
|
+
|
|
46
|
+
_CHANGELOG_HISTORICAL_RE = re.compile(r"^## \[?\d+\.\d+\.\d+\]?")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def validate_authored(repo_root: Path, canonical: str) -> dict[str, Any]:
|
|
50
|
+
blockers: list[dict[str, str]] = []
|
|
51
|
+
for surface in AUTHORED_SURFACES:
|
|
52
|
+
drifts = check_surface(repo_root, surface, canonical)
|
|
53
|
+
for label, found in drifts:
|
|
54
|
+
blockers.append({
|
|
55
|
+
"surface": label,
|
|
56
|
+
"found": found if found is not None else "<not found>",
|
|
57
|
+
"expected": canonical,
|
|
58
|
+
})
|
|
59
|
+
status = "fail" if blockers else "ok"
|
|
60
|
+
return {"status": status, "blockers": blockers}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def validate_derived(repo_root: Path, canonical: str) -> dict[str, Any]:
|
|
64
|
+
blockers: list[dict[str, str]] = []
|
|
65
|
+
|
|
66
|
+
for rel_path, key_path in _DERIVED_JSON_SURFACES:
|
|
67
|
+
full_path = repo_root / rel_path
|
|
68
|
+
if not full_path.exists():
|
|
69
|
+
continue
|
|
70
|
+
try:
|
|
71
|
+
data = json.loads(full_path.read_text(encoding="utf-8"))
|
|
72
|
+
except (OSError, json.JSONDecodeError):
|
|
73
|
+
continue
|
|
74
|
+
current: Any = data
|
|
75
|
+
for key in key_path:
|
|
76
|
+
if isinstance(current, dict):
|
|
77
|
+
current = current.get(key)
|
|
78
|
+
else:
|
|
79
|
+
current = None
|
|
80
|
+
break
|
|
81
|
+
if current is not None and current != canonical:
|
|
82
|
+
blockers.append({
|
|
83
|
+
"surface": rel_path,
|
|
84
|
+
"found": str(current),
|
|
85
|
+
"expected": canonical,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
ast_path = repo_root / _DERIVED_AST_SURFACE
|
|
89
|
+
if ast_path.exists():
|
|
90
|
+
try:
|
|
91
|
+
tree = ast.parse(ast_path.read_text(encoding="utf-8"))
|
|
92
|
+
for node in ast.walk(tree):
|
|
93
|
+
if isinstance(node, ast.Assign):
|
|
94
|
+
for target in node.targets:
|
|
95
|
+
if isinstance(target, ast.Name) and target.id == "CANONICAL_VERSION":
|
|
96
|
+
if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str):
|
|
97
|
+
if node.value.value != canonical:
|
|
98
|
+
blockers.append({
|
|
99
|
+
"surface": _DERIVED_AST_SURFACE,
|
|
100
|
+
"found": node.value.value,
|
|
101
|
+
"expected": canonical,
|
|
102
|
+
})
|
|
103
|
+
except (OSError, SyntaxError):
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
status = "fail" if blockers else "ok"
|
|
107
|
+
return {"status": status, "blockers": blockers}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _is_changelog_historical(line: str, forbid_version: str) -> bool:
|
|
111
|
+
return (
|
|
112
|
+
_CHANGELOG_HISTORICAL_RE.match(line.strip()) is not None
|
|
113
|
+
and forbid_version in line
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _scan_file_for_residue(
|
|
118
|
+
file_path: Path, rel_path: str, forbid_version: str,
|
|
119
|
+
) -> list[dict[str, Any]]:
|
|
120
|
+
blockers: list[dict[str, Any]] = []
|
|
121
|
+
try:
|
|
122
|
+
lines = file_path.read_text(encoding="utf-8").splitlines()
|
|
123
|
+
except (OSError, UnicodeDecodeError):
|
|
124
|
+
return blockers
|
|
125
|
+
|
|
126
|
+
for line_num, line in enumerate(lines, start=1):
|
|
127
|
+
if forbid_version not in line:
|
|
128
|
+
continue
|
|
129
|
+
if file_path.name == "CHANGELOG.md" and _is_changelog_historical(line, forbid_version):
|
|
130
|
+
continue
|
|
131
|
+
blockers.append({
|
|
132
|
+
"file": rel_path,
|
|
133
|
+
"line": line_num,
|
|
134
|
+
"content": line.strip(),
|
|
135
|
+
})
|
|
136
|
+
return blockers
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def scan_scoped_residue(repo_root: Path, forbid_version: str) -> dict[str, Any]:
|
|
140
|
+
blockers: list[dict[str, Any]] = []
|
|
141
|
+
|
|
142
|
+
for target in SCOPED_RESIDUE_TARGETS:
|
|
143
|
+
full_path = repo_root / target
|
|
144
|
+
if not full_path.exists():
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
if full_path.is_file():
|
|
148
|
+
blockers.extend(_scan_file_for_residue(full_path, target, forbid_version))
|
|
149
|
+
elif full_path.is_dir():
|
|
150
|
+
for child in sorted(full_path.rglob("*")):
|
|
151
|
+
if child.is_file():
|
|
152
|
+
rel = str(child.relative_to(repo_root))
|
|
153
|
+
blockers.extend(_scan_file_for_residue(child, rel, forbid_version))
|
|
154
|
+
|
|
155
|
+
status = "fail" if blockers else "ok"
|
|
156
|
+
return {"status": status, "forbid_version": forbid_version, "blockers": blockers}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def build_report(
|
|
160
|
+
*,
|
|
161
|
+
canonical: str,
|
|
162
|
+
scope: str,
|
|
163
|
+
forbid_version: str | None,
|
|
164
|
+
authored: dict[str, Any] | None,
|
|
165
|
+
derived: dict[str, Any] | None,
|
|
166
|
+
scoped_residue: dict[str, Any] | None,
|
|
167
|
+
) -> dict[str, Any]:
|
|
168
|
+
has_failure = False
|
|
169
|
+
for section in (authored, derived, scoped_residue):
|
|
170
|
+
if section is not None and section.get("status") == "fail":
|
|
171
|
+
has_failure = True
|
|
172
|
+
break
|
|
173
|
+
|
|
174
|
+
report: dict[str, Any] = {
|
|
175
|
+
"canonical_version": canonical,
|
|
176
|
+
"scope": scope,
|
|
177
|
+
"forbid_version": forbid_version,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if authored is not None:
|
|
181
|
+
report["authored"] = authored
|
|
182
|
+
if derived is not None:
|
|
183
|
+
report["derived"] = derived
|
|
184
|
+
if scoped_residue is not None:
|
|
185
|
+
report["scoped_residue"] = scoped_residue
|
|
186
|
+
|
|
187
|
+
report["overall_status"] = "fail" if has_failure else "ok"
|
|
188
|
+
return report
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def main() -> int:
|
|
192
|
+
parser = argparse.ArgumentParser(description="Read-only release identity validator")
|
|
193
|
+
parser.add_argument("--scope", choices=["authored", "derived", "all"], default="all")
|
|
194
|
+
parser.add_argument("--forbid-version", default=None)
|
|
195
|
+
parser.add_argument("--output-json", default=None)
|
|
196
|
+
args = parser.parse_args()
|
|
197
|
+
|
|
198
|
+
adoption_file = _REPO_ROOT / "runtime" / "adoption.py"
|
|
199
|
+
if not adoption_file.exists():
|
|
200
|
+
print(json.dumps({"error": "runtime/adoption.py not found"}), file=sys.stderr)
|
|
201
|
+
return 1
|
|
202
|
+
|
|
203
|
+
canonical = extract_canonical_version(adoption_file)
|
|
204
|
+
if canonical is None:
|
|
205
|
+
print(json.dumps({"error": "CANONICAL_VERSION not found"}), file=sys.stderr)
|
|
206
|
+
return 1
|
|
207
|
+
|
|
208
|
+
authored_result = None
|
|
209
|
+
derived_result = None
|
|
210
|
+
residue_result = None
|
|
211
|
+
|
|
212
|
+
if args.scope in ("authored", "all"):
|
|
213
|
+
authored_result = validate_authored(_REPO_ROOT, canonical)
|
|
214
|
+
else:
|
|
215
|
+
authored_result = {"status": "skipped", "blockers": []}
|
|
216
|
+
|
|
217
|
+
if args.scope in ("derived", "all"):
|
|
218
|
+
derived_result = validate_derived(_REPO_ROOT, canonical)
|
|
219
|
+
else:
|
|
220
|
+
derived_result = {"status": "skipped", "blockers": []}
|
|
221
|
+
|
|
222
|
+
if args.forbid_version and args.scope in ("derived", "all"):
|
|
223
|
+
residue_result = scan_scoped_residue(_REPO_ROOT, args.forbid_version)
|
|
224
|
+
|
|
225
|
+
report = build_report(
|
|
226
|
+
canonical=canonical,
|
|
227
|
+
scope=args.scope,
|
|
228
|
+
forbid_version=args.forbid_version,
|
|
229
|
+
authored=authored_result,
|
|
230
|
+
derived=derived_result,
|
|
231
|
+
scoped_residue=residue_result,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
output = json.dumps(report, indent=2)
|
|
235
|
+
|
|
236
|
+
if args.output_json:
|
|
237
|
+
Path(args.output_json).parent.mkdir(parents=True, exist_ok=True)
|
|
238
|
+
Path(args.output_json).write_text(output + "\n", encoding="utf-8")
|
|
239
|
+
else:
|
|
240
|
+
print(output)
|
|
241
|
+
|
|
242
|
+
return 1 if report["overall_status"] == "fail" else 0
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if __name__ == "__main__":
|
|
246
|
+
sys.exit(main())
|
|
@@ -20,6 +20,9 @@ OMG_RELEASE_READY_PROVIDERS=claude,codex python3 scripts/omg.py release readines
|
|
|
20
20
|
python3 scripts/omg.py compat gate --max-bridge 0 --output .omg/evidence/omg-compat-gap.json
|
|
21
21
|
python3 scripts/check-omg-public-ready.py
|
|
22
22
|
|
|
23
|
+
echo "=== Validating release identity ==="
|
|
24
|
+
python3 scripts/validate-release-identity.py --scope all --forbid-version 2.1.1
|
|
25
|
+
|
|
23
26
|
if command -v pyenv >/dev/null 2>&1 && pyenv prefix 3.12.7 >/dev/null 2>&1; then
|
|
24
27
|
PYENV_VERSION=3.12.7 python -m pytest -q
|
|
25
28
|
else
|
package/settings.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
-
"_comment": "OMG 2.1.
|
|
3
|
+
"_comment": "OMG 2.1.4 - project-level config with hook registrations, presets, and feature flags.",
|
|
4
4
|
"permissions": {
|
|
5
5
|
"allow": [
|
|
6
6
|
"Agent",
|
|
@@ -316,7 +316,7 @@
|
|
|
316
316
|
]
|
|
317
317
|
},
|
|
318
318
|
"_omg": {
|
|
319
|
-
"_version": "2.1.
|
|
319
|
+
"_version": "2.1.4",
|
|
320
320
|
"preset": "safe",
|
|
321
321
|
"omgMode": "focused",
|
|
322
322
|
"default_mode": "ulw+ralph",
|
|
@@ -370,7 +370,7 @@
|
|
|
370
370
|
"CONTEXT_MANAGER": false
|
|
371
371
|
},
|
|
372
372
|
"generated": {
|
|
373
|
-
"contract_version": "2.1.
|
|
373
|
+
"contract_version": "2.1.4",
|
|
374
374
|
"channel": "enterprise",
|
|
375
375
|
"required_bundles": [
|
|
376
376
|
"control-plane",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Plugins allowlist schema template
|
|
2
|
+
#
|
|
3
|
+
# - source: "mcp:filesystem"
|
|
4
|
+
# host: "claude"
|
|
5
|
+
# resource_type: "mcp_server" # one of: mcp_server, skill, plugin
|
|
6
|
+
# reason: "trusted local plugin"
|
|
7
|
+
# scope: "project" # optional: project | global
|
|
8
|
+
# timestamp: "2026-03-09T00:00:00Z" # optional: ISO 8601 UTC
|
|
9
|
+
# approver: "user" # optional
|
|
10
|
+
#
|
|
11
|
+
# - source: "skill:omg/control-plane"
|
|
12
|
+
# host: "codex"
|
|
13
|
+
# resource_type: "skill"
|
|
14
|
+
# reason: "required control-plane skill"
|
|
15
|
+
#
|
|
16
|
+
# - source: "plugin:omg-core"
|
|
17
|
+
# host: "gemini"
|
|
18
|
+
# resource_type: "plugin"
|
|
19
|
+
# reason: "approved core plugin"
|
package/build/lib/yaml.py
DELETED
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
"""Minimal YAML compatibility layer for OMG.
|
|
2
|
-
|
|
3
|
-
This keeps standalone/no-vendor flows working when PyYAML is unavailable.
|
|
4
|
-
It supports the subset used in OMG configs: mappings, lists, quoted strings,
|
|
5
|
-
booleans, numbers, nulls, and simple flow-style collections.
|
|
6
|
-
"""
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import io
|
|
10
|
-
import json
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _read_text(stream_or_text: Any) -> str:
|
|
15
|
-
if hasattr(stream_or_text, "read"):
|
|
16
|
-
return str(stream_or_text.read())
|
|
17
|
-
return str(stream_or_text)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _strip_comments(line: str) -> str:
|
|
21
|
-
in_single = False
|
|
22
|
-
in_double = False
|
|
23
|
-
escaped = False
|
|
24
|
-
out: list[str] = []
|
|
25
|
-
for char in line:
|
|
26
|
-
if escaped:
|
|
27
|
-
out.append(char)
|
|
28
|
-
escaped = False
|
|
29
|
-
continue
|
|
30
|
-
if char == "\\" and in_double:
|
|
31
|
-
out.append(char)
|
|
32
|
-
escaped = True
|
|
33
|
-
continue
|
|
34
|
-
if char == "'" and not in_double:
|
|
35
|
-
in_single = not in_single
|
|
36
|
-
out.append(char)
|
|
37
|
-
continue
|
|
38
|
-
if char == '"' and not in_single:
|
|
39
|
-
in_double = not in_double
|
|
40
|
-
out.append(char)
|
|
41
|
-
continue
|
|
42
|
-
if char == "#" and not in_single and not in_double:
|
|
43
|
-
break
|
|
44
|
-
out.append(char)
|
|
45
|
-
return "".join(out).rstrip()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _prepare_lines(text: str) -> list[tuple[int, str]]:
|
|
49
|
-
prepared: list[tuple[int, str]] = []
|
|
50
|
-
for raw_line in text.splitlines():
|
|
51
|
-
stripped = _strip_comments(raw_line)
|
|
52
|
-
if not stripped.strip():
|
|
53
|
-
continue
|
|
54
|
-
indent = len(stripped) - len(stripped.lstrip(" "))
|
|
55
|
-
prepared.append((indent, stripped[indent:]))
|
|
56
|
-
return prepared
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _split_key_value(text: str) -> tuple[str, str] | None:
|
|
60
|
-
in_single = False
|
|
61
|
-
in_double = False
|
|
62
|
-
escaped = False
|
|
63
|
-
for index, char in enumerate(text):
|
|
64
|
-
if escaped:
|
|
65
|
-
escaped = False
|
|
66
|
-
continue
|
|
67
|
-
if char == "\\" and in_double:
|
|
68
|
-
escaped = True
|
|
69
|
-
continue
|
|
70
|
-
if char == "'" and not in_double:
|
|
71
|
-
in_single = not in_single
|
|
72
|
-
continue
|
|
73
|
-
if char == '"' and not in_single:
|
|
74
|
-
in_double = not in_double
|
|
75
|
-
continue
|
|
76
|
-
if char == ":" and not in_single and not in_double:
|
|
77
|
-
if index + 1 < len(text) and text[index + 1] not in {" ", "\t"}:
|
|
78
|
-
continue
|
|
79
|
-
return text[:index].strip(), text[index + 1 :].strip()
|
|
80
|
-
return None
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def _split_flow_items(text: str) -> list[str]:
|
|
84
|
-
items: list[str] = []
|
|
85
|
-
current: list[str] = []
|
|
86
|
-
in_single = False
|
|
87
|
-
in_double = False
|
|
88
|
-
escaped = False
|
|
89
|
-
depth = 0
|
|
90
|
-
for char in text:
|
|
91
|
-
if escaped:
|
|
92
|
-
current.append(char)
|
|
93
|
-
escaped = False
|
|
94
|
-
continue
|
|
95
|
-
if char == "\\" and in_double:
|
|
96
|
-
current.append(char)
|
|
97
|
-
escaped = True
|
|
98
|
-
continue
|
|
99
|
-
if char == "'" and not in_double:
|
|
100
|
-
in_single = not in_single
|
|
101
|
-
current.append(char)
|
|
102
|
-
continue
|
|
103
|
-
if char == '"' and not in_single:
|
|
104
|
-
in_double = not in_double
|
|
105
|
-
current.append(char)
|
|
106
|
-
continue
|
|
107
|
-
if char in "[{" and not in_single and not in_double:
|
|
108
|
-
depth += 1
|
|
109
|
-
current.append(char)
|
|
110
|
-
continue
|
|
111
|
-
if char in "]}" and not in_single and not in_double:
|
|
112
|
-
depth -= 1
|
|
113
|
-
current.append(char)
|
|
114
|
-
continue
|
|
115
|
-
if char == "," and not in_single and not in_double and depth == 0:
|
|
116
|
-
item = "".join(current).strip()
|
|
117
|
-
if item:
|
|
118
|
-
items.append(item)
|
|
119
|
-
current = []
|
|
120
|
-
continue
|
|
121
|
-
current.append(char)
|
|
122
|
-
tail = "".join(current).strip()
|
|
123
|
-
if tail:
|
|
124
|
-
items.append(tail)
|
|
125
|
-
return items
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _parse_scalar(text: str) -> Any:
|
|
129
|
-
if text == "":
|
|
130
|
-
return ""
|
|
131
|
-
if text[0] in {'"', "'"} and text[-1] == text[0]:
|
|
132
|
-
return text[1:-1]
|
|
133
|
-
lowered = text.lower()
|
|
134
|
-
if lowered in {"true", "yes", "on"}:
|
|
135
|
-
return True
|
|
136
|
-
if lowered in {"false", "no", "off"}:
|
|
137
|
-
return False
|
|
138
|
-
if lowered in {"null", "~"}:
|
|
139
|
-
return None
|
|
140
|
-
if text.startswith("[") and text.endswith("]"):
|
|
141
|
-
inner = text[1:-1].strip()
|
|
142
|
-
if not inner:
|
|
143
|
-
return []
|
|
144
|
-
return [_parse_scalar(item) for item in _split_flow_items(inner)]
|
|
145
|
-
if text.startswith("{") and text.endswith("}"):
|
|
146
|
-
inner = text[1:-1].strip()
|
|
147
|
-
if not inner:
|
|
148
|
-
return {}
|
|
149
|
-
out: dict[str, Any] = {}
|
|
150
|
-
for item in _split_flow_items(inner):
|
|
151
|
-
pair = _split_key_value(item)
|
|
152
|
-
if pair is None:
|
|
153
|
-
raise ValueError(f"Invalid flow mapping item: {item!r}")
|
|
154
|
-
out[str(_parse_scalar(pair[0]))] = _parse_scalar(pair[1])
|
|
155
|
-
return out
|
|
156
|
-
try:
|
|
157
|
-
if text.startswith("0") and text not in {"0", "0.0"} and not text.startswith("0."):
|
|
158
|
-
raise ValueError
|
|
159
|
-
return int(text)
|
|
160
|
-
except ValueError:
|
|
161
|
-
pass
|
|
162
|
-
try:
|
|
163
|
-
return float(text)
|
|
164
|
-
except ValueError:
|
|
165
|
-
return text
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
class _Parser:
|
|
169
|
-
def __init__(self, lines: list[tuple[int, str]]):
|
|
170
|
-
self.lines = lines
|
|
171
|
-
self.index = 0
|
|
172
|
-
|
|
173
|
-
def parse(self) -> Any:
|
|
174
|
-
if not self.lines:
|
|
175
|
-
return None
|
|
176
|
-
return self._parse_block(self.lines[0][0])
|
|
177
|
-
|
|
178
|
-
def _parse_block(self, indent: int) -> Any:
|
|
179
|
-
if self.index >= len(self.lines):
|
|
180
|
-
return None
|
|
181
|
-
current_indent, content = self.lines[self.index]
|
|
182
|
-
if current_indent < indent:
|
|
183
|
-
return None
|
|
184
|
-
if content.startswith("- "):
|
|
185
|
-
return self._parse_list(indent)
|
|
186
|
-
return self._parse_mapping(indent)
|
|
187
|
-
|
|
188
|
-
def _parse_mapping(self, indent: int) -> dict[str, Any]:
|
|
189
|
-
out: dict[str, Any] = {}
|
|
190
|
-
while self.index < len(self.lines):
|
|
191
|
-
current_indent, content = self.lines[self.index]
|
|
192
|
-
if current_indent < indent:
|
|
193
|
-
break
|
|
194
|
-
if current_indent != indent or content.startswith("- "):
|
|
195
|
-
break
|
|
196
|
-
pair = _split_key_value(content)
|
|
197
|
-
if pair is None:
|
|
198
|
-
raise ValueError(f"Invalid mapping line: {content!r}")
|
|
199
|
-
raw_key, raw_value = pair
|
|
200
|
-
key = str(_parse_scalar(raw_key))
|
|
201
|
-
self.index += 1
|
|
202
|
-
if raw_value:
|
|
203
|
-
out[key] = _parse_scalar(raw_value)
|
|
204
|
-
continue
|
|
205
|
-
if self.index < len(self.lines) and self.lines[self.index][0] > current_indent:
|
|
206
|
-
out[key] = self._parse_block(self.lines[self.index][0])
|
|
207
|
-
else:
|
|
208
|
-
out[key] = None
|
|
209
|
-
return out
|
|
210
|
-
|
|
211
|
-
def _parse_list(self, indent: int) -> list[Any]:
|
|
212
|
-
out: list[Any] = []
|
|
213
|
-
while self.index < len(self.lines):
|
|
214
|
-
current_indent, content = self.lines[self.index]
|
|
215
|
-
if current_indent < indent:
|
|
216
|
-
break
|
|
217
|
-
if current_indent != indent or not content.startswith("- "):
|
|
218
|
-
break
|
|
219
|
-
item_content = content[2:].strip()
|
|
220
|
-
self.index += 1
|
|
221
|
-
if not item_content:
|
|
222
|
-
if self.index < len(self.lines) and self.lines[self.index][0] > current_indent:
|
|
223
|
-
out.append(self._parse_block(self.lines[self.index][0]))
|
|
224
|
-
else:
|
|
225
|
-
out.append(None)
|
|
226
|
-
continue
|
|
227
|
-
|
|
228
|
-
pair = _split_key_value(item_content)
|
|
229
|
-
if pair is not None and pair[0] and " " not in pair[0]:
|
|
230
|
-
raw_key, raw_value = pair
|
|
231
|
-
item: dict[str, Any] = {str(_parse_scalar(raw_key)): _parse_scalar(raw_value) if raw_value else None}
|
|
232
|
-
if not raw_value and self.index < len(self.lines) and self.lines[self.index][0] > current_indent:
|
|
233
|
-
item[str(_parse_scalar(raw_key))] = self._parse_block(self.lines[self.index][0])
|
|
234
|
-
if self.index < len(self.lines) and self.lines[self.index][0] > current_indent:
|
|
235
|
-
extra = self._parse_block(self.lines[self.index][0])
|
|
236
|
-
if isinstance(extra, dict):
|
|
237
|
-
item.update(extra)
|
|
238
|
-
out.append(item)
|
|
239
|
-
continue
|
|
240
|
-
|
|
241
|
-
out.append(_parse_scalar(item_content))
|
|
242
|
-
return out
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def safe_load(stream: Any) -> Any:
|
|
246
|
-
text = _read_text(stream)
|
|
247
|
-
stripped = text.strip()
|
|
248
|
-
if not stripped:
|
|
249
|
-
return None
|
|
250
|
-
if stripped[0] in {"{", "["}:
|
|
251
|
-
try:
|
|
252
|
-
return json.loads(stripped)
|
|
253
|
-
except json.JSONDecodeError:
|
|
254
|
-
pass
|
|
255
|
-
return _Parser(_prepare_lines(text)).parse()
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def _dump_scalar(value: Any) -> str:
|
|
259
|
-
if value is None:
|
|
260
|
-
return "null"
|
|
261
|
-
if isinstance(value, bool):
|
|
262
|
-
return "true" if value else "false"
|
|
263
|
-
if isinstance(value, (int, float)):
|
|
264
|
-
return str(value)
|
|
265
|
-
return json.dumps(str(value), ensure_ascii=False)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def _dump_lines(value: Any, indent: int) -> list[str]:
|
|
269
|
-
prefix = " " * indent
|
|
270
|
-
if isinstance(value, dict):
|
|
271
|
-
lines: list[str] = []
|
|
272
|
-
for key, item in value.items():
|
|
273
|
-
if isinstance(item, (dict, list)):
|
|
274
|
-
lines.append(f"{prefix}{key}:")
|
|
275
|
-
lines.extend(_dump_lines(item, indent + 2))
|
|
276
|
-
else:
|
|
277
|
-
lines.append(f"{prefix}{key}: {_dump_scalar(item)}")
|
|
278
|
-
return lines
|
|
279
|
-
if isinstance(value, list):
|
|
280
|
-
lines = []
|
|
281
|
-
for item in value:
|
|
282
|
-
if isinstance(item, dict):
|
|
283
|
-
nested = _dump_lines(item, indent + 2)
|
|
284
|
-
if nested:
|
|
285
|
-
first = nested[0].strip()
|
|
286
|
-
lines.append(f"{prefix}- {first}")
|
|
287
|
-
for line in nested[1:]:
|
|
288
|
-
lines.append(line)
|
|
289
|
-
else:
|
|
290
|
-
lines.append(f"{prefix}- {{}}")
|
|
291
|
-
elif isinstance(item, list):
|
|
292
|
-
lines.append(f"{prefix}-")
|
|
293
|
-
lines.extend(_dump_lines(item, indent + 2))
|
|
294
|
-
else:
|
|
295
|
-
lines.append(f"{prefix}- {_dump_scalar(item)}")
|
|
296
|
-
return lines
|
|
297
|
-
return [f"{prefix}{_dump_scalar(value)}"]
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def safe_dump(
|
|
301
|
-
data: Any,
|
|
302
|
-
stream: io.TextIOBase | None = None,
|
|
303
|
-
default_flow_style: bool | None = None,
|
|
304
|
-
sort_keys: bool = False,
|
|
305
|
-
**_: Any,
|
|
306
|
-
) -> str | None:
|
|
307
|
-
_ = default_flow_style
|
|
308
|
-
dump_data = data
|
|
309
|
-
if sort_keys and isinstance(data, dict):
|
|
310
|
-
dump_data = dict(sorted(data.items()))
|
|
311
|
-
text = "\n".join(_dump_lines(dump_data, 0)) + "\n"
|
|
312
|
-
if stream is not None:
|
|
313
|
-
stream.write(text)
|
|
314
|
-
return None
|
|
315
|
-
return text
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
dump = safe_dump
|
|
319
|
-
load = safe_load
|
|
320
|
-
|
|
321
|
-
__all__ = ["dump", "load", "safe_dump", "safe_load"]
|