@probelabs/visor 0.1.106 → 0.1.111
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 +71 -2
- package/action.yml +1 -1
- package/defaults/code-refiner.yaml +114 -0
- package/defaults/{.visor.yaml → code-review.yaml} +35 -226
- package/defaults/override.yaml +52 -0
- package/defaults/task-refinement.yaml +624 -0
- package/defaults/visor.tests.yaml +685 -0
- package/defaults/visor.yaml +483 -0
- package/dist/action-cli-bridge.d.ts +11 -82
- package/dist/action-cli-bridge.d.ts.map +1 -1
- package/dist/ai-review-service.d.ts +28 -9
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +19 -331
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/cron-scheduler.d.ts +3 -3
- package/dist/cron-scheduler.d.ts.map +1 -1
- package/dist/debug-visualizer/ws-server.d.ts +7 -1
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
- package/dist/defaults/code-refiner.yaml +114 -0
- package/dist/defaults/{.visor.yaml → code-review.yaml} +35 -226
- package/dist/defaults/override.yaml +52 -0
- package/dist/defaults/task-refinement.yaml +624 -0
- package/dist/defaults/visor.tests.yaml +685 -0
- package/dist/defaults/visor.yaml +483 -0
- package/dist/docs/DEPLOYMENT.md +118 -0
- package/dist/docs/GITHUB_CHECKS.md +280 -0
- package/dist/docs/NPM_USAGE.md +208 -0
- package/dist/docs/action-reference.md +19 -0
- package/dist/docs/advanced-ai.md +237 -0
- package/dist/docs/ai-configuration.md +535 -0
- package/dist/docs/ai-custom-tools-usage.md +261 -0
- package/dist/docs/ai-custom-tools.md +392 -0
- package/dist/docs/author-permissions.md +610 -0
- package/dist/docs/bot-transports-rfc.md +23 -0
- package/dist/docs/ci-cli-mode.md +34 -0
- package/dist/docs/claude-code.md +74 -0
- package/dist/docs/command-provider.md +559 -0
- package/dist/docs/commands.md +8 -0
- package/dist/docs/configuration.md +324 -0
- package/dist/docs/custom-tools.md +424 -0
- package/dist/docs/dashboards/README.md +23 -0
- package/dist/docs/dashboards/grafana-visor-diagrams.json +20 -0
- package/dist/docs/dashboards/grafana-visor-overview.json +33 -0
- package/dist/docs/debug-visualizer-progress.md +572 -0
- package/dist/docs/debug-visualizer-rfc.md +691 -0
- package/dist/docs/debug-visualizer.md +114 -0
- package/dist/docs/debugging.md +636 -0
- package/dist/docs/default-output-schema.md +28 -0
- package/dist/docs/dependencies.md +369 -0
- package/dist/docs/dev-playbook.md +9 -0
- package/dist/docs/engine-pause-resume-rfc.md +192 -0
- package/dist/docs/engine-state-machine-plan.md +333 -0
- package/dist/docs/event-driven-github-integration-rfc.md +743 -0
- package/dist/docs/event-triggers.md +292 -0
- package/dist/docs/execution-statistics-rfc.md +290 -0
- package/dist/docs/fact-validator-gap-analysis.md +178 -0
- package/dist/docs/fact-validator-implementation-plan.md +1235 -0
- package/dist/docs/fail-if.md +95 -0
- package/dist/docs/failure-conditions-implementation.md +271 -0
- package/dist/docs/failure-conditions-schema.md +173 -0
- package/dist/docs/failure-routing-rfc.md +193 -0
- package/dist/docs/failure-routing.md +507 -0
- package/dist/docs/foreach-dependency-propagation.md +473 -0
- package/dist/docs/github-ops.md +89 -0
- package/dist/docs/goto-forward-run-plan.md +113 -0
- package/dist/docs/guides/criticality-modes.md +332 -0
- package/dist/docs/guides/fault-management-and-contracts.md +738 -0
- package/dist/docs/guides/workflow-style-guide.md +224 -0
- package/dist/docs/http.md +299 -0
- package/dist/docs/human-input-provider.md +372 -0
- package/dist/docs/lifecycle-hooks.md +253 -0
- package/dist/docs/limits.md +64 -0
- package/dist/docs/liquid-templates.md +490 -0
- package/dist/docs/loop-routing-refactor.md +89 -0
- package/dist/docs/mcp-provider.md +557 -0
- package/dist/docs/mcp.md +124 -0
- package/dist/docs/memory.md +903 -0
- package/dist/docs/observability.md +12 -0
- package/dist/docs/output-formats.md +20 -0
- package/dist/docs/output-formatting.md +29 -0
- package/dist/docs/output-history.md +383 -0
- package/dist/docs/performance.md +6 -0
- package/dist/docs/pluggable.md +124 -0
- package/dist/docs/proposals/snapshot-scope-execution.md +236 -0
- package/dist/docs/providers/git-checkout.md +589 -0
- package/dist/docs/recipes.md +474 -0
- package/dist/docs/rfc/git-checkout-step.md +601 -0
- package/dist/docs/rfc/on_init-hook.md +1294 -0
- package/dist/docs/rfc/workspace-isolation.md +216 -0
- package/dist/docs/roadmap/criticality-implementation-tasks.md +92 -0
- package/dist/docs/router-patterns.md +339 -0
- package/dist/docs/schema-next-pr.md +10 -0
- package/dist/docs/schema-templates.md +68 -0
- package/dist/docs/script.md +34 -0
- package/dist/docs/sdk.md +222 -0
- package/dist/docs/security.md +7 -0
- package/dist/docs/suppressions.md +89 -0
- package/dist/docs/tag-filtering.md +258 -0
- package/dist/docs/telemetry-setup.md +119 -0
- package/dist/docs/telemetry-tracing-rfc.md +275 -0
- package/dist/docs/test-framework-rfc.md +680 -0
- package/dist/docs/testing/assertions.md +85 -0
- package/dist/docs/testing/ci.md +44 -0
- package/dist/docs/testing/cli.md +41 -0
- package/dist/docs/testing/cookbook.md +172 -0
- package/dist/docs/testing/dsl-reference.md +199 -0
- package/dist/docs/testing/fixtures-and-mocks.md +91 -0
- package/dist/docs/testing/flows.md +92 -0
- package/dist/docs/testing/getting-started.md +93 -0
- package/dist/docs/testing/troubleshooting.md +55 -0
- package/dist/docs/timeouts.md +50 -0
- package/dist/docs/troubleshooting.md +7 -0
- package/dist/docs/visor-sdk-rfc.md +186 -0
- package/dist/docs/workflows.md +569 -0
- package/dist/engine/on-finish/orchestrator.d.ts +19 -0
- package/dist/engine/on-finish/orchestrator.d.ts.map +1 -0
- package/dist/engine/on-finish/utils.d.ts +44 -0
- package/dist/engine/on-finish/utils.d.ts.map +1 -0
- package/dist/event-bus/event-bus.d.ts +13 -0
- package/dist/event-bus/event-bus.d.ts.map +1 -0
- package/dist/event-bus/types.d.ts +71 -0
- package/dist/event-bus/types.d.ts.map +1 -0
- package/dist/examples/.claude/agents/code-reviewer.md +69 -0
- package/dist/examples/.mcp.json +34 -0
- package/dist/examples/CALCULATOR-SDK.md +364 -0
- package/dist/examples/README.md +384 -0
- package/dist/examples/ai-custom-tools-example.yaml +206 -0
- package/dist/examples/ai-custom-tools-simple.yaml +76 -0
- package/dist/examples/ai-retry-fallback-config.yaml +180 -0
- package/dist/examples/ai-with-bash.yaml +126 -0
- package/dist/examples/ai-with-mcp.yaml +82 -0
- package/dist/examples/basic-human-input.yaml +15 -0
- package/dist/examples/bedrock-config.yaml +77 -0
- package/dist/examples/calculator-config.yaml +133 -0
- package/dist/examples/calculator-json-output-guide.md +311 -0
- package/dist/examples/calculator-sdk-automated.ts +340 -0
- package/dist/examples/calculator-sdk-example.ts +275 -0
- package/dist/examples/calculator-sdk-json.ts +331 -0
- package/dist/examples/calculator-sdk-real.ts +374 -0
- package/dist/examples/calculator-sdk-test.ts +148 -0
- package/dist/examples/claude-code-config.yaml +191 -0
- package/dist/examples/cron-webhook-config.yaml +215 -0
- package/dist/examples/custom-template.liquid +57 -0
- package/dist/examples/custom-tools-example.yaml +281 -0
- package/dist/examples/enhanced-config.yaml +165 -0
- package/dist/examples/environments/visor.base.yaml +92 -0
- package/dist/examples/environments/visor.dev.yaml +33 -0
- package/dist/examples/environments/visor.prod.yaml +95 -0
- package/dist/examples/environments/visor.staging.yaml +46 -0
- package/dist/examples/fact-validator.yaml +361 -0
- package/dist/examples/fail-if-simple.yaml +90 -0
- package/dist/examples/failure-conditions-advanced.yaml +136 -0
- package/dist/examples/failure-conditions-basic.yaml +48 -0
- package/dist/examples/failure-conditions-github-style.yaml +119 -0
- package/dist/examples/failure-conditions-migration.yaml +74 -0
- package/dist/examples/for-loop-example.yaml +176 -0
- package/dist/examples/forEach-example.yaml +120 -0
- package/dist/examples/git-checkout-basic.yaml +32 -0
- package/dist/examples/git-checkout-compare.yaml +59 -0
- package/dist/examples/git-checkout-cross-repo.yaml +76 -0
- package/dist/examples/github-workflow-with-tags.yml +163 -0
- package/dist/examples/http-integration-config.yaml +240 -0
- package/dist/examples/https-server-config.yaml +209 -0
- package/dist/examples/human-input-example.yaml +63 -0
- package/dist/examples/if-conditions.yaml +173 -0
- package/dist/examples/jira-simple-example.yaml +56 -0
- package/dist/examples/jira-single-issue-workflow.yaml +166 -0
- package/dist/examples/jira-workflow-mcp.yaml +182 -0
- package/dist/examples/mcp/analyzer.py +119 -0
- package/dist/examples/mcp-provider-example.yaml +301 -0
- package/dist/examples/memory-counter.yaml +99 -0
- package/dist/examples/memory-error-collection.yaml +104 -0
- package/dist/examples/memory-exec-js.yaml +247 -0
- package/dist/examples/memory-namespace-isolation.yaml +184 -0
- package/dist/examples/memory-retry-counter.yaml +65 -0
- package/dist/examples/memory-state-machine.yaml +170 -0
- package/dist/examples/on-init-import-demo.yaml +179 -0
- package/dist/examples/outputs-raw-basic.yaml +26 -0
- package/dist/examples/project-with-tools.yaml +174 -0
- package/dist/examples/prompts/architecture-analysis.liquid +116 -0
- package/dist/examples/prompts/security-comprehensive.liquid +107 -0
- package/dist/examples/quick-start-tags.yaml +53 -0
- package/dist/examples/reusable-tools.yaml +92 -0
- package/dist/examples/reusable-workflows.yaml +88 -0
- package/dist/examples/routing-basic.yaml +35 -0
- package/dist/examples/routing-dynamic-js.yaml +46 -0
- package/dist/examples/routing-foreach.yaml +34 -0
- package/dist/examples/routing-goto-event.yaml +34 -0
- package/dist/examples/routing-on-success.yaml +25 -0
- package/dist/examples/run-calculator-demo.sh +71 -0
- package/dist/examples/sdk-basic.mjs +10 -0
- package/dist/examples/sdk-cjs.cjs +10 -0
- package/dist/examples/sdk-comprehensive.mjs +175 -0
- package/dist/examples/sdk-manual-config.mjs +65 -0
- package/dist/examples/sdk-typescript.js +81 -0
- package/dist/examples/sdk-typescript.ts +92 -0
- package/dist/examples/session-reuse-config.yaml +151 -0
- package/dist/examples/session-reuse-self.yaml +81 -0
- package/dist/examples/slack-simple-chat.yaml +775 -0
- package/dist/examples/templates/security-report.liquid +137 -0
- package/dist/examples/tools-library.yaml +281 -0
- package/dist/examples/transform-example.yaml +199 -0
- package/dist/examples/visor-with-tags.yaml +198 -0
- package/dist/examples/webhook-pipeline-config.yaml +218 -0
- package/dist/examples/workflows/calculator-workflow.yaml +163 -0
- package/dist/examples/workflows/code-quality.yaml +222 -0
- package/dist/examples/workflows/quick-pr-check.yaml +90 -0
- package/dist/examples/workflows/workflow-composition-example.yaml +130 -0
- package/dist/failure-condition-evaluator.d.ts +3 -0
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/frontends/github-frontend.d.ts +58 -0
- package/dist/frontends/github-frontend.d.ts.map +1 -0
- package/dist/frontends/host.d.ts +47 -0
- package/dist/frontends/host.d.ts.map +1 -0
- package/dist/frontends/ndjson-sink.d.ts +12 -0
- package/dist/frontends/ndjson-sink.d.ts.map +1 -0
- package/dist/frontends/slack-frontend.d.ts +58 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -0
- package/dist/generated/config-schema.d.ts +967 -57
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +1033 -56
- package/dist/github-check-service.d.ts +4 -6
- package/dist/github-check-service.d.ts.map +1 -1
- package/dist/github-comments.d.ts +2 -4
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134327 -99004
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/memory-store.d.ts +6 -0
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/output/assistant-json/template.liquid +0 -0
- package/dist/output/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
- package/dist/output/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
- package/dist/output-formatters.d.ts +1 -1
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts +12 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts +6 -0
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +43 -1
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/claude-code-check-provider.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts +61 -0
- package/dist/providers/custom-tool-executor.d.ts.map +1 -0
- package/dist/providers/git-checkout-provider.d.ts +25 -0
- package/dist/providers/git-checkout-provider.d.ts.map +1 -0
- package/dist/providers/github-ops-provider.d.ts.map +1 -1
- package/dist/providers/http-client-provider.d.ts +4 -4
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts +5 -0
- package/dist/providers/human-input-check-provider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts +2 -5
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts +10 -4
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
- package/dist/providers/memory-check-provider.d.ts +2 -8
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts +25 -0
- package/dist/providers/script-check-provider.d.ts.map +1 -0
- package/dist/providers/workflow-check-provider.d.ts +56 -0
- package/dist/providers/workflow-check-provider.d.ts.map +1 -0
- package/dist/reviewer.d.ts +2 -1
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
- package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
- package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
- package/dist/sdk/{chunk-TUTOLSFV.mjs → chunk-3OMWVM6J.mjs} +11 -1
- package/dist/sdk/chunk-3OMWVM6J.mjs.map +1 -0
- package/dist/sdk/chunk-7UK3NIIT.mjs +482 -0
- package/dist/sdk/chunk-7UK3NIIT.mjs.map +1 -0
- package/dist/sdk/chunk-AGIZJ4UZ.mjs +173 -0
- package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
- package/dist/sdk/chunk-AIVFBIS4.mjs +1371 -0
- package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
- package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
- package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
- package/dist/sdk/chunk-CNX7V5JK.mjs +89 -0
- package/dist/sdk/chunk-CNX7V5JK.mjs.map +1 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
- package/dist/sdk/chunk-NAW3DB3I.mjs +197 -0
- package/dist/sdk/chunk-NAW3DB3I.mjs.map +1 -0
- package/dist/sdk/chunk-O5EZDNYL.mjs +274 -0
- package/dist/sdk/chunk-O5EZDNYL.mjs.map +1 -0
- package/dist/sdk/chunk-QR7MOMJH.mjs +558 -0
- package/dist/sdk/chunk-QR7MOMJH.mjs.map +1 -0
- package/dist/sdk/chunk-QY2XYPEV.mjs +3556 -0
- package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
- package/dist/sdk/chunk-S2RUE2RG.mjs +145 -0
- package/dist/sdk/chunk-S2RUE2RG.mjs.map +1 -0
- package/dist/sdk/chunk-SIWNBRTK.mjs +800 -0
- package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
- package/dist/sdk/chunk-YSN4G6CI.mjs +146 -0
- package/dist/sdk/chunk-YSN4G6CI.mjs.map +1 -0
- package/dist/sdk/chunk-ZYAUYXSW.mjs +206 -0
- package/dist/sdk/chunk-ZYAUYXSW.mjs.map +1 -0
- package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
- package/dist/sdk/config-YNC2EOOT.mjs +16 -0
- package/dist/sdk/config-merger-PX3WIT57.mjs +10 -0
- package/dist/sdk/event-bus-5BEVPQ6T.mjs +35 -0
- package/dist/sdk/event-bus-5BEVPQ6T.mjs.map +1 -0
- package/dist/sdk/failure-condition-evaluator-YGTF2GHG.mjs +17 -0
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs +458 -0
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs.map +1 -0
- package/dist/sdk/github-frontend-SIAEOCON.mjs +1420 -0
- package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
- package/dist/sdk/host-DXUYTNMU.mjs +52 -0
- package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs → liquid-extensions-PKWCKK7E.mjs} +8 -2
- package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
- package/dist/sdk/memory-store-XGBB7LX7.mjs.map +1 -0
- package/dist/sdk/metrics-7PP3EJUH.mjs +29 -0
- package/dist/sdk/metrics-7PP3EJUH.mjs.map +1 -0
- package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs +44 -0
- package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs.map +1 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs.map +1 -0
- package/dist/sdk/renderer-schema-LPKN5UJS.mjs +51 -0
- package/dist/sdk/renderer-schema-LPKN5UJS.mjs.map +1 -0
- package/dist/sdk/routing-6N45MJ4F.mjs +24 -0
- package/dist/sdk/routing-6N45MJ4F.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +541 -22
- package/dist/sdk/sdk.d.ts +541 -22
- package/dist/sdk/sdk.js +27963 -16505
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +1116 -2169
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/session-registry-4E6YRQ77.mjs +10 -0
- package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
- package/dist/sdk/trace-helpers-VP6QYVBX.mjs +23 -0
- package/dist/sdk/trace-helpers-VP6QYVBX.mjs.map +1 -0
- package/dist/sdk/{tracer-init-WC75N5NW.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
- package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/slack/adapter.d.ts +36 -0
- package/dist/slack/adapter.d.ts.map +1 -0
- package/dist/slack/cache-prewarmer.d.ts +31 -0
- package/dist/slack/cache-prewarmer.d.ts.map +1 -0
- package/dist/slack/client.d.ts +77 -0
- package/dist/slack/client.d.ts.map +1 -0
- package/dist/slack/markdown.d.ts +45 -0
- package/dist/slack/markdown.d.ts.map +1 -0
- package/dist/slack/prompt-state.d.ts +33 -0
- package/dist/slack/prompt-state.d.ts.map +1 -0
- package/dist/slack/rate-limiter.d.ts +56 -0
- package/dist/slack/rate-limiter.d.ts.map +1 -0
- package/dist/slack/signature.d.ts +2 -0
- package/dist/slack/signature.d.ts.map +1 -0
- package/dist/slack/socket-runner.d.ts +42 -0
- package/dist/slack/socket-runner.d.ts.map +1 -0
- package/dist/slack/thread-cache.d.ts +51 -0
- package/dist/slack/thread-cache.d.ts.map +1 -0
- package/dist/snapshot-store.d.ts +59 -0
- package/dist/snapshot-store.d.ts.map +1 -0
- package/dist/state-machine/context/build-engine-context.d.ts +17 -0
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -0
- package/dist/state-machine/dispatch/dependency-gating.d.ts +12 -0
- package/dist/state-machine/dispatch/dependency-gating.d.ts.map +1 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts +14 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -0
- package/dist/state-machine/dispatch/foreach-processor.d.ts +8 -0
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -0
- package/dist/state-machine/dispatch/history-snapshot.d.ts +8 -0
- package/dist/state-machine/dispatch/history-snapshot.d.ts.map +1 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
- package/dist/state-machine/dispatch/renderer-schema.d.ts +8 -0
- package/dist/state-machine/dispatch/renderer-schema.d.ts.map +1 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts +15 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -0
- package/dist/state-machine/dispatch/template-renderer.d.ts +7 -0
- package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -0
- package/dist/state-machine/execution/summary.d.ts +8 -0
- package/dist/state-machine/execution/summary.d.ts.map +1 -0
- package/dist/state-machine/runner.d.ts +79 -0
- package/dist/state-machine/runner.d.ts.map +1 -0
- package/dist/state-machine/states/check-running.d.ts +14 -0
- package/dist/state-machine/states/check-running.d.ts.map +1 -0
- package/dist/state-machine/states/completed.d.ts +12 -0
- package/dist/state-machine/states/completed.d.ts.map +1 -0
- package/dist/state-machine/states/error.d.ts +11 -0
- package/dist/state-machine/states/error.d.ts.map +1 -0
- package/dist/state-machine/states/init.d.ts +11 -0
- package/dist/state-machine/states/init.d.ts.map +1 -0
- package/dist/state-machine/states/level-dispatch.d.ts +17 -0
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -0
- package/dist/state-machine/states/plan-ready.d.ts +12 -0
- package/dist/state-machine/states/plan-ready.d.ts.map +1 -0
- package/dist/state-machine/states/routing.d.ts +52 -0
- package/dist/state-machine/states/routing.d.ts.map +1 -0
- package/dist/state-machine/states/wave-planning.d.ts +14 -0
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -0
- package/dist/state-machine/workflow-projection.d.ts +47 -0
- package/dist/state-machine/workflow-projection.d.ts.map +1 -0
- package/dist/state-machine-execution-engine.d.ts +159 -0
- package/dist/state-machine-execution-engine.d.ts.map +1 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts +5 -0
- package/dist/telemetry/state-capture.d.ts.map +1 -1
- package/dist/test-runner/assertions.d.ts +59 -0
- package/dist/test-runner/assertions.d.ts.map +1 -0
- package/dist/test-runner/core/environment.d.ts +8 -0
- package/dist/test-runner/core/environment.d.ts.map +1 -0
- package/dist/test-runner/core/fixture.d.ts +3 -0
- package/dist/test-runner/core/fixture.d.ts.map +1 -0
- package/dist/test-runner/core/flow-stage.d.ts +32 -0
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -0
- package/dist/test-runner/core/mocks.d.ts +8 -0
- package/dist/test-runner/core/mocks.d.ts.map +1 -0
- package/dist/test-runner/core/test-execution-wrapper.d.ts +18 -0
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -0
- package/dist/test-runner/evaluators.d.ts +45 -0
- package/dist/test-runner/evaluators.d.ts.map +1 -0
- package/dist/test-runner/fixture-loader.d.ts +30 -0
- package/dist/test-runner/fixture-loader.d.ts.map +1 -0
- package/dist/test-runner/index.d.ts +127 -0
- package/dist/test-runner/index.d.ts.map +1 -0
- package/dist/test-runner/recorders/github-recorder.d.ts +23 -0
- package/dist/test-runner/recorders/github-recorder.d.ts.map +1 -0
- package/dist/test-runner/recorders/global-recorder.d.ts +4 -0
- package/dist/test-runner/recorders/global-recorder.d.ts.map +1 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
- package/dist/test-runner/utils/selectors.d.ts +2 -0
- package/dist/test-runner/utils/selectors.d.ts.map +1 -0
- package/dist/test-runner/validator.d.ts +8 -0
- package/dist/test-runner/validator.d.ts.map +1 -0
- package/dist/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
- package/dist/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
- package/dist/types/bot.d.ts +109 -0
- package/dist/types/bot.d.ts.map +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +459 -9
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +177 -0
- package/dist/types/engine.d.ts.map +1 -0
- package/dist/types/execution.d.ts +73 -0
- package/dist/types/execution.d.ts.map +1 -0
- package/dist/types/git-checkout.d.ts +76 -0
- package/dist/types/git-checkout.d.ts.map +1 -0
- package/dist/types/github.d.ts +51 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/workflow.d.ts +237 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/utils/command-executor.d.ts +43 -0
- package/dist/utils/command-executor.d.ts.map +1 -0
- package/dist/utils/comment-metadata.d.ts +21 -0
- package/dist/utils/comment-metadata.d.ts.map +1 -0
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-merger.d.ts.map +1 -1
- package/dist/utils/env-exposure.d.ts +3 -0
- package/dist/utils/env-exposure.d.ts.map +1 -0
- package/dist/utils/file-exclusion.d.ts.map +1 -1
- package/dist/utils/interactive-prompt.d.ts +1 -1
- package/dist/utils/interactive-prompt.d.ts.map +1 -1
- package/dist/utils/json-text-extractor.d.ts +17 -0
- package/dist/utils/json-text-extractor.d.ts.map +1 -0
- package/dist/utils/sandbox.d.ts +10 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/script-memory-ops.d.ts +21 -0
- package/dist/utils/script-memory-ops.d.ts.map +1 -0
- package/dist/utils/template-context.d.ts +8 -0
- package/dist/utils/template-context.d.ts.map +1 -0
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +118 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -0
- package/dist/utils/worktree-cleanup.d.ts +33 -0
- package/dist/utils/worktree-cleanup.d.ts.map +1 -0
- package/dist/utils/worktree-manager.d.ts +153 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -0
- package/dist/webhook-server.d.ts +3 -3
- package/dist/webhook-server.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts +81 -0
- package/dist/workflow-executor.d.ts.map +1 -0
- package/dist/workflow-registry.d.ts +79 -0
- package/dist/workflow-registry.d.ts.map +1 -0
- package/package.json +12 -5
- package/dist/output/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
- package/dist/sdk/check-execution-engine-2YYKUUSH.mjs +0 -11
- package/dist/sdk/check-execution-engine-6QJXYYON.mjs +0 -11
- package/dist/sdk/check-execution-engine-PJZ4ZOKG.mjs +0 -11
- package/dist/sdk/chunk-33QVZ2D4.mjs +0 -316
- package/dist/sdk/chunk-33QVZ2D4.mjs.map +0 -1
- package/dist/sdk/chunk-B5QBV2QJ.mjs +0 -752
- package/dist/sdk/chunk-B5QBV2QJ.mjs.map +0 -1
- package/dist/sdk/chunk-BVFNRCHT.mjs +0 -14129
- package/dist/sdk/chunk-BVFNRCHT.mjs.map +0 -1
- package/dist/sdk/chunk-KWZW23FG.mjs +0 -14129
- package/dist/sdk/chunk-KWZW23FG.mjs.map +0 -1
- package/dist/sdk/chunk-O4RP4BRH.mjs +0 -14092
- package/dist/sdk/chunk-O4RP4BRH.mjs.map +0 -1
- package/dist/sdk/chunk-TUTOLSFV.mjs.map +0 -1
- package/dist/sdk/chunk-U5D2LY66.mjs +0 -245
- package/dist/sdk/chunk-U5D2LY66.mjs.map +0 -1
- package/dist/sdk/chunk-U7X54EMV.mjs +0 -331
- package/dist/sdk/chunk-U7X54EMV.mjs.map +0 -1
- package/dist/sdk/config-merger-TWUBWFC2.mjs +0 -8
- package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs +0 -61
- package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs.map +0 -1
- package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs +0 -61
- package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs.map +0 -1
- package/dist/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
- /package/dist/sdk/{check-execution-engine-2YYKUUSH.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
- /package/dist/sdk/{check-execution-engine-6QJXYYON.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
- /package/dist/sdk/{check-execution-engine-PJZ4ZOKG.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
- /package/dist/sdk/{config-merger-TWUBWFC2.mjs.map → config-merger-PX3WIT57.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-WC75N5NW.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
package/dist/sdk/sdk.mjs
CHANGED
|
@@ -1,2320 +1,1267 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
StateMachineRunner,
|
|
3
|
+
check_provider_registry_exports,
|
|
4
|
+
init_check_provider_registry,
|
|
5
|
+
init_runner
|
|
6
|
+
} from "./chunk-23L3QRYX.mjs";
|
|
7
|
+
import "./chunk-NAW3DB3I.mjs";
|
|
8
|
+
import {
|
|
9
|
+
commandExecutor,
|
|
10
|
+
init_command_executor
|
|
11
|
+
} from "./chunk-AUT26LHW.mjs";
|
|
12
|
+
import "./chunk-HTOKWMPO.mjs";
|
|
13
|
+
import "./chunk-QR7MOMJH.mjs";
|
|
14
|
+
import {
|
|
15
|
+
ConfigManager,
|
|
16
|
+
init_config
|
|
17
|
+
} from "./chunk-QY2XYPEV.mjs";
|
|
18
|
+
import "./chunk-O5EZDNYL.mjs";
|
|
19
|
+
import {
|
|
20
|
+
ExecutionJournal,
|
|
21
|
+
init_snapshot_store
|
|
22
|
+
} from "./chunk-AIVFBIS4.mjs";
|
|
23
|
+
import "./chunk-SIWNBRTK.mjs";
|
|
24
|
+
import "./chunk-BOVFH3LI.mjs";
|
|
25
|
+
import "./chunk-ZYAUYXSW.mjs";
|
|
26
|
+
import "./chunk-S2RUE2RG.mjs";
|
|
27
|
+
import "./chunk-AK6BVWIT.mjs";
|
|
28
|
+
import "./chunk-CNX7V5JK.mjs";
|
|
29
|
+
import {
|
|
30
|
+
MemoryStore,
|
|
31
|
+
init_memory_store
|
|
32
|
+
} from "./chunk-7UK3NIIT.mjs";
|
|
5
33
|
import {
|
|
6
34
|
init_logger,
|
|
7
35
|
logger
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import
|
|
11
|
-
ConfigMerger
|
|
12
|
-
} from "./chunk-U5D2LY66.mjs";
|
|
36
|
+
} from "./chunk-AGIZJ4UZ.mjs";
|
|
37
|
+
import "./chunk-YSN4G6CI.mjs";
|
|
38
|
+
import "./chunk-3OMWVM6J.mjs";
|
|
13
39
|
import {
|
|
14
40
|
__esm,
|
|
15
41
|
__export,
|
|
16
|
-
__require,
|
|
17
42
|
__toCommonJS
|
|
18
43
|
} from "./chunk-WMJKH4XE.mjs";
|
|
19
44
|
|
|
20
|
-
// src/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
45
|
+
// src/utils/workspace-manager.ts
|
|
46
|
+
import * as fsp from "fs/promises";
|
|
47
|
+
import * as path from "path";
|
|
48
|
+
function shellEscape(str) {
|
|
49
|
+
return "'" + str.replace(/'/g, "'\\''") + "'";
|
|
50
|
+
}
|
|
51
|
+
function sanitizePathComponent(name) {
|
|
52
|
+
return name.replace(/\.\./g, "").replace(/[\/\\]/g, "-").replace(/^\.+/, "").trim() || "unnamed";
|
|
53
|
+
}
|
|
54
|
+
var WorkspaceManager;
|
|
55
|
+
var init_workspace_manager = __esm({
|
|
56
|
+
"src/utils/workspace-manager.ts"() {
|
|
29
57
|
"use strict";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
"Record<string,CheckConfig>": {
|
|
124
|
-
type: "object",
|
|
125
|
-
additionalProperties: {
|
|
126
|
-
$ref: "#/definitions/CheckConfig"
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
CheckConfig: {
|
|
130
|
-
type: "object",
|
|
131
|
-
properties: {
|
|
132
|
-
type: {
|
|
133
|
-
$ref: "#/definitions/ConfigCheckType",
|
|
134
|
-
description: "Type of check to perform (defaults to 'ai' if not specified)"
|
|
135
|
-
},
|
|
136
|
-
prompt: {
|
|
137
|
-
type: "string",
|
|
138
|
-
description: "AI prompt for the check - can be inline string or file path (auto-detected) - required for AI checks"
|
|
139
|
-
},
|
|
140
|
-
appendPrompt: {
|
|
141
|
-
type: "string",
|
|
142
|
-
description: "Additional prompt to append when extending configurations - merged with parent prompt"
|
|
143
|
-
},
|
|
144
|
-
exec: {
|
|
145
|
-
type: "string",
|
|
146
|
-
description: "Command execution with Liquid template support - required for command checks"
|
|
147
|
-
},
|
|
148
|
-
stdin: {
|
|
149
|
-
type: "string",
|
|
150
|
-
description: "Stdin input for tools with Liquid template support - optional for tool checks"
|
|
151
|
-
},
|
|
152
|
-
url: {
|
|
153
|
-
type: "string",
|
|
154
|
-
description: "HTTP URL - required for http output checks"
|
|
155
|
-
},
|
|
156
|
-
body: {
|
|
157
|
-
type: "string",
|
|
158
|
-
description: "HTTP body template (Liquid) - required for http output checks"
|
|
159
|
-
},
|
|
160
|
-
method: {
|
|
161
|
-
type: "string",
|
|
162
|
-
description: "HTTP method (defaults to POST)"
|
|
163
|
-
},
|
|
164
|
-
headers: {
|
|
165
|
-
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
166
|
-
description: "HTTP headers"
|
|
167
|
-
},
|
|
168
|
-
endpoint: {
|
|
169
|
-
type: "string",
|
|
170
|
-
description: "HTTP endpoint path - required for http_input checks"
|
|
171
|
-
},
|
|
172
|
-
transform: {
|
|
173
|
-
type: "string",
|
|
174
|
-
description: "Transform template for http_input data (Liquid) - optional"
|
|
175
|
-
},
|
|
176
|
-
transform_js: {
|
|
177
|
-
type: "string",
|
|
178
|
-
description: "Transform using JavaScript expressions (evaluated in secure sandbox) - optional"
|
|
179
|
-
},
|
|
180
|
-
schedule: {
|
|
181
|
-
type: "string",
|
|
182
|
-
description: 'Cron schedule expression (e.g., "0 2 * * *") - optional for any check type'
|
|
183
|
-
},
|
|
184
|
-
focus: {
|
|
185
|
-
type: "string",
|
|
186
|
-
description: "Focus area for the check (security/performance/style/architecture/all) - optional"
|
|
187
|
-
},
|
|
188
|
-
command: {
|
|
189
|
-
type: "string",
|
|
190
|
-
description: 'Command that triggers this check (e.g., "review", "security-scan") - optional'
|
|
191
|
-
},
|
|
192
|
-
on: {
|
|
193
|
-
type: "array",
|
|
194
|
-
items: {
|
|
195
|
-
$ref: "#/definitions/EventTrigger"
|
|
196
|
-
},
|
|
197
|
-
description: "Events that trigger this check (defaults to ['manual'] if not specified)"
|
|
198
|
-
},
|
|
199
|
-
triggers: {
|
|
200
|
-
type: "array",
|
|
201
|
-
items: {
|
|
202
|
-
type: "string"
|
|
203
|
-
},
|
|
204
|
-
description: "File patterns that trigger this check (optional)"
|
|
205
|
-
},
|
|
206
|
-
ai: {
|
|
207
|
-
$ref: "#/definitions/AIProviderConfig",
|
|
208
|
-
description: "AI provider configuration (optional)"
|
|
209
|
-
},
|
|
210
|
-
ai_model: {
|
|
211
|
-
type: "string",
|
|
212
|
-
description: "AI model to use for this check - overrides global setting"
|
|
213
|
-
},
|
|
214
|
-
ai_provider: {
|
|
215
|
-
type: "string",
|
|
216
|
-
description: "AI provider to use for this check - overrides global setting"
|
|
217
|
-
},
|
|
218
|
-
ai_mcp_servers: {
|
|
219
|
-
$ref: "#/definitions/Record%3Cstring%2CMcpServerConfig%3E",
|
|
220
|
-
description: "MCP servers for this AI check - overrides global setting"
|
|
221
|
-
},
|
|
222
|
-
claude_code: {
|
|
223
|
-
$ref: "#/definitions/ClaudeCodeConfig",
|
|
224
|
-
description: "Claude Code configuration (for claude-code type checks)"
|
|
225
|
-
},
|
|
226
|
-
env: {
|
|
227
|
-
$ref: "#/definitions/EnvConfig",
|
|
228
|
-
description: "Environment variables for this check"
|
|
229
|
-
},
|
|
230
|
-
timeout: {
|
|
231
|
-
type: "number",
|
|
232
|
-
description: "Timeout in seconds for command execution (default: 60)"
|
|
233
|
-
},
|
|
234
|
-
depends_on: {
|
|
235
|
-
type: "array",
|
|
236
|
-
items: {
|
|
237
|
-
type: "string"
|
|
238
|
-
},
|
|
239
|
-
description: "Check IDs that this check depends on (optional)"
|
|
240
|
-
},
|
|
241
|
-
group: {
|
|
242
|
-
type: "string",
|
|
243
|
-
description: 'Group name for comment separation (e.g., "code-review", "pr-overview") - optional'
|
|
244
|
-
},
|
|
245
|
-
schema: {
|
|
246
|
-
anyOf: [
|
|
247
|
-
{
|
|
248
|
-
type: "string"
|
|
249
|
-
},
|
|
250
|
-
{
|
|
251
|
-
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E"
|
|
252
|
-
}
|
|
253
|
-
],
|
|
254
|
-
description: 'Schema type for template rendering (e.g., "code-review", "markdown") or inline JSON schema object - optional'
|
|
255
|
-
},
|
|
256
|
-
template: {
|
|
257
|
-
$ref: "#/definitions/CustomTemplateConfig",
|
|
258
|
-
description: "Custom template configuration - optional"
|
|
259
|
-
},
|
|
260
|
-
if: {
|
|
261
|
-
type: "string",
|
|
262
|
-
description: "Condition to determine if check should run - runs if expression evaluates to true"
|
|
263
|
-
},
|
|
264
|
-
reuse_ai_session: {
|
|
265
|
-
type: ["string", "boolean"],
|
|
266
|
-
description: "Check name to reuse AI session from, or true to use first dependency (only works with depends_on)"
|
|
267
|
-
},
|
|
268
|
-
session_mode: {
|
|
269
|
-
type: "string",
|
|
270
|
-
enum: ["clone", "append"],
|
|
271
|
-
description: "How to reuse AI session: 'clone' (default, copy history) or 'append' (share history)"
|
|
272
|
-
},
|
|
273
|
-
fail_if: {
|
|
274
|
-
type: "string",
|
|
275
|
-
description: "Simple fail condition - fails check if expression evaluates to true"
|
|
276
|
-
},
|
|
277
|
-
failure_conditions: {
|
|
278
|
-
$ref: "#/definitions/FailureConditions",
|
|
279
|
-
description: "Check-specific failure conditions - optional (deprecated, use fail_if)"
|
|
280
|
-
},
|
|
281
|
-
tags: {
|
|
282
|
-
type: "array",
|
|
283
|
-
items: {
|
|
284
|
-
type: "string"
|
|
285
|
-
},
|
|
286
|
-
description: 'Tags for categorizing and filtering checks (e.g., ["local", "fast", "security"])'
|
|
287
|
-
},
|
|
288
|
-
forEach: {
|
|
289
|
-
type: "boolean",
|
|
290
|
-
description: "Process output as array and run dependent checks for each item"
|
|
291
|
-
},
|
|
292
|
-
on_fail: {
|
|
293
|
-
$ref: "#/definitions/OnFailConfig",
|
|
294
|
-
description: "Failure routing configuration for this check (retry/goto/run)"
|
|
295
|
-
},
|
|
296
|
-
on_success: {
|
|
297
|
-
$ref: "#/definitions/OnSuccessConfig",
|
|
298
|
-
description: "Success routing configuration for this check (post-actions and optional goto)"
|
|
299
|
-
},
|
|
300
|
-
message: {
|
|
301
|
-
type: "string",
|
|
302
|
-
description: "Message template for log checks"
|
|
303
|
-
},
|
|
304
|
-
level: {
|
|
305
|
-
type: "string",
|
|
306
|
-
enum: ["debug", "info", "warn", "error"],
|
|
307
|
-
description: "Log level for log checks"
|
|
308
|
-
},
|
|
309
|
-
include_pr_context: {
|
|
310
|
-
type: "boolean",
|
|
311
|
-
description: "Include PR context in log output"
|
|
312
|
-
},
|
|
313
|
-
include_dependencies: {
|
|
314
|
-
type: "boolean",
|
|
315
|
-
description: "Include dependency summaries in log output"
|
|
316
|
-
},
|
|
317
|
-
include_metadata: {
|
|
318
|
-
type: "boolean",
|
|
319
|
-
description: "Include execution metadata in log output"
|
|
320
|
-
},
|
|
321
|
-
operation: {
|
|
322
|
-
type: "string",
|
|
323
|
-
enum: ["get", "set", "append", "increment", "delete", "clear", "list", "exec_js"],
|
|
324
|
-
description: "Memory operation to perform"
|
|
325
|
-
},
|
|
326
|
-
key: {
|
|
327
|
-
type: "string",
|
|
328
|
-
description: "Key for memory operation"
|
|
329
|
-
},
|
|
330
|
-
value: {
|
|
331
|
-
description: "Value for set/append operations"
|
|
332
|
-
},
|
|
333
|
-
value_js: {
|
|
334
|
-
type: "string",
|
|
335
|
-
description: "JavaScript expression to compute value dynamically"
|
|
336
|
-
},
|
|
337
|
-
memory_js: {
|
|
338
|
-
type: "string",
|
|
339
|
-
description: "JavaScript code for exec_js operation with full memory access"
|
|
340
|
-
},
|
|
341
|
-
namespace: {
|
|
342
|
-
type: "string",
|
|
343
|
-
description: "Override namespace for this check"
|
|
344
|
-
},
|
|
345
|
-
op: {
|
|
346
|
-
type: "string",
|
|
347
|
-
description: "GitHub operation to perform (e.g., 'labels.add', 'labels.remove', 'comment.create')"
|
|
348
|
-
},
|
|
349
|
-
values: {
|
|
350
|
-
anyOf: [
|
|
351
|
-
{
|
|
352
|
-
type: "array",
|
|
353
|
-
items: {
|
|
354
|
-
type: "string"
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
type: "string"
|
|
359
|
-
}
|
|
360
|
-
],
|
|
361
|
-
description: "Values for GitHub operations (can be array or single value)"
|
|
362
|
-
},
|
|
363
|
-
transport: {
|
|
364
|
-
type: "string",
|
|
365
|
-
enum: ["stdio", "sse", "http"],
|
|
366
|
-
description: "Transport type for MCP: stdio (default), sse (legacy), or http (streamable HTTP)"
|
|
367
|
-
},
|
|
368
|
-
methodArgs: {
|
|
369
|
-
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
370
|
-
description: "Arguments to pass to the MCP method (supports Liquid templates)"
|
|
371
|
-
},
|
|
372
|
-
argsTransform: {
|
|
373
|
-
type: "string",
|
|
374
|
-
description: "Transform template for method arguments (Liquid)"
|
|
375
|
-
},
|
|
376
|
-
sessionId: {
|
|
377
|
-
type: "string",
|
|
378
|
-
description: "Session ID for HTTP transport (optional, server may generate one)"
|
|
379
|
-
},
|
|
380
|
-
args: {
|
|
381
|
-
type: "array",
|
|
382
|
-
items: {
|
|
383
|
-
type: "string"
|
|
384
|
-
},
|
|
385
|
-
description: "Command arguments (for stdio transport in MCP checks)"
|
|
386
|
-
},
|
|
387
|
-
workingDirectory: {
|
|
388
|
-
type: "string",
|
|
389
|
-
description: "Working directory (for stdio transport in MCP checks)"
|
|
390
|
-
}
|
|
391
|
-
},
|
|
392
|
-
additionalProperties: false,
|
|
393
|
-
description: "Configuration for a single check",
|
|
394
|
-
patternProperties: {
|
|
395
|
-
"^x-": {}
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
ConfigCheckType: {
|
|
399
|
-
type: "string",
|
|
400
|
-
enum: [
|
|
401
|
-
"ai",
|
|
402
|
-
"command",
|
|
403
|
-
"http",
|
|
404
|
-
"http_input",
|
|
405
|
-
"http_client",
|
|
406
|
-
"noop",
|
|
407
|
-
"log",
|
|
408
|
-
"memory",
|
|
409
|
-
"github",
|
|
410
|
-
"claude-code",
|
|
411
|
-
"mcp",
|
|
412
|
-
"human-input"
|
|
413
|
-
],
|
|
414
|
-
description: "Valid check types in configuration"
|
|
415
|
-
},
|
|
416
|
-
"Record<string,string>": {
|
|
417
|
-
type: "object",
|
|
418
|
-
additionalProperties: {
|
|
419
|
-
type: "string"
|
|
420
|
-
}
|
|
421
|
-
},
|
|
422
|
-
EventTrigger: {
|
|
423
|
-
type: "string",
|
|
424
|
-
enum: [
|
|
425
|
-
"pr_opened",
|
|
426
|
-
"pr_updated",
|
|
427
|
-
"pr_closed",
|
|
428
|
-
"issue_opened",
|
|
429
|
-
"issue_comment",
|
|
430
|
-
"manual",
|
|
431
|
-
"schedule",
|
|
432
|
-
"webhook_received"
|
|
433
|
-
],
|
|
434
|
-
description: "Valid event triggers for checks"
|
|
435
|
-
},
|
|
436
|
-
AIProviderConfig: {
|
|
437
|
-
type: "object",
|
|
438
|
-
properties: {
|
|
439
|
-
provider: {
|
|
440
|
-
type: "string",
|
|
441
|
-
enum: ["google", "anthropic", "openai", "bedrock", "mock"],
|
|
442
|
-
description: "AI provider to use"
|
|
443
|
-
},
|
|
444
|
-
model: {
|
|
445
|
-
type: "string",
|
|
446
|
-
description: "Model name to use"
|
|
447
|
-
},
|
|
448
|
-
apiKey: {
|
|
449
|
-
type: "string",
|
|
450
|
-
description: "API key (usually from environment variables)"
|
|
451
|
-
},
|
|
452
|
-
timeout: {
|
|
453
|
-
type: "number",
|
|
454
|
-
description: "Request timeout in milliseconds"
|
|
455
|
-
},
|
|
456
|
-
debug: {
|
|
457
|
-
type: "boolean",
|
|
458
|
-
description: "Enable debug mode"
|
|
459
|
-
},
|
|
460
|
-
mcpServers: {
|
|
461
|
-
$ref: "#/definitions/Record%3Cstring%2CMcpServerConfig%3E",
|
|
462
|
-
description: "MCP servers configuration"
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
additionalProperties: false,
|
|
466
|
-
description: "AI provider configuration",
|
|
467
|
-
patternProperties: {
|
|
468
|
-
"^x-": {}
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
"Record<string,McpServerConfig>": {
|
|
472
|
-
type: "object",
|
|
473
|
-
additionalProperties: {
|
|
474
|
-
$ref: "#/definitions/McpServerConfig"
|
|
475
|
-
}
|
|
476
|
-
},
|
|
477
|
-
McpServerConfig: {
|
|
478
|
-
type: "object",
|
|
479
|
-
properties: {
|
|
480
|
-
command: {
|
|
481
|
-
type: "string",
|
|
482
|
-
description: "Command to execute for the MCP server"
|
|
483
|
-
},
|
|
484
|
-
args: {
|
|
485
|
-
type: "array",
|
|
486
|
-
items: {
|
|
487
|
-
type: "string"
|
|
488
|
-
},
|
|
489
|
-
description: "Arguments to pass to the command"
|
|
490
|
-
},
|
|
491
|
-
env: {
|
|
492
|
-
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
493
|
-
description: "Environment variables for the MCP server"
|
|
494
|
-
}
|
|
495
|
-
},
|
|
496
|
-
required: ["command"],
|
|
497
|
-
additionalProperties: false,
|
|
498
|
-
description: "MCP Server configuration",
|
|
499
|
-
patternProperties: {
|
|
500
|
-
"^x-": {}
|
|
58
|
+
init_command_executor();
|
|
59
|
+
init_logger();
|
|
60
|
+
WorkspaceManager = class _WorkspaceManager {
|
|
61
|
+
static instances = /* @__PURE__ */ new Map();
|
|
62
|
+
sessionId;
|
|
63
|
+
basePath;
|
|
64
|
+
workspacePath;
|
|
65
|
+
originalPath;
|
|
66
|
+
config;
|
|
67
|
+
initialized = false;
|
|
68
|
+
mainProjectInfo = null;
|
|
69
|
+
projects = /* @__PURE__ */ new Map();
|
|
70
|
+
cleanupHandlersRegistered = false;
|
|
71
|
+
usedNames = /* @__PURE__ */ new Set();
|
|
72
|
+
constructor(sessionId, originalPath, config) {
|
|
73
|
+
this.sessionId = sessionId;
|
|
74
|
+
this.originalPath = originalPath;
|
|
75
|
+
this.config = {
|
|
76
|
+
enabled: true,
|
|
77
|
+
basePath: process.env.VISOR_WORKSPACE_PATH || "/tmp/visor-workspaces",
|
|
78
|
+
cleanupOnExit: true,
|
|
79
|
+
...config
|
|
80
|
+
};
|
|
81
|
+
this.basePath = this.config.basePath;
|
|
82
|
+
this.workspacePath = path.join(this.basePath, sanitizePathComponent(this.sessionId));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get or create a WorkspaceManager instance for a session
|
|
86
|
+
*/
|
|
87
|
+
static getInstance(sessionId, originalPath, config) {
|
|
88
|
+
if (!_WorkspaceManager.instances.has(sessionId)) {
|
|
89
|
+
_WorkspaceManager.instances.set(
|
|
90
|
+
sessionId,
|
|
91
|
+
new _WorkspaceManager(sessionId, originalPath, config)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return _WorkspaceManager.instances.get(sessionId);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Clear all instances (for testing)
|
|
98
|
+
*/
|
|
99
|
+
static clearInstances() {
|
|
100
|
+
_WorkspaceManager.instances.clear();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if workspace isolation is enabled
|
|
104
|
+
*/
|
|
105
|
+
isEnabled() {
|
|
106
|
+
return this.config.enabled;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get the workspace path
|
|
110
|
+
*/
|
|
111
|
+
getWorkspacePath() {
|
|
112
|
+
return this.workspacePath;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the original working directory
|
|
116
|
+
*/
|
|
117
|
+
getOriginalPath() {
|
|
118
|
+
return this.originalPath;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get workspace info (only available after initialize)
|
|
122
|
+
*/
|
|
123
|
+
getWorkspaceInfo() {
|
|
124
|
+
return this.mainProjectInfo;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Initialize the workspace - creates workspace directory and main project worktree
|
|
128
|
+
*/
|
|
129
|
+
async initialize() {
|
|
130
|
+
if (!this.config.enabled) {
|
|
131
|
+
throw new Error("Workspace isolation is not enabled");
|
|
132
|
+
}
|
|
133
|
+
if (this.initialized && this.mainProjectInfo) {
|
|
134
|
+
return this.mainProjectInfo;
|
|
135
|
+
}
|
|
136
|
+
logger.info(`Initializing workspace: ${this.workspacePath}`);
|
|
137
|
+
await fsp.mkdir(this.workspacePath, { recursive: true });
|
|
138
|
+
logger.debug(`Created workspace directory: ${this.workspacePath}`);
|
|
139
|
+
const mainProjectName = sanitizePathComponent(this.extractProjectName(this.originalPath));
|
|
140
|
+
this.usedNames.add(mainProjectName);
|
|
141
|
+
const mainProjectPath = path.join(this.workspacePath, mainProjectName);
|
|
142
|
+
const isGitRepo = await this.isGitRepository(this.originalPath);
|
|
143
|
+
if (isGitRepo) {
|
|
144
|
+
await this.createMainProjectWorktree(mainProjectPath);
|
|
145
|
+
} else {
|
|
146
|
+
logger.debug(`Original path is not a git repo, creating symlink`);
|
|
147
|
+
try {
|
|
148
|
+
await fsp.symlink(this.originalPath, mainProjectPath);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
throw new Error(`Failed to create symlink for main project: ${error}`);
|
|
501
151
|
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
152
|
+
}
|
|
153
|
+
this.registerCleanupHandlers();
|
|
154
|
+
this.mainProjectInfo = {
|
|
155
|
+
sessionId: this.sessionId,
|
|
156
|
+
workspacePath: this.workspacePath,
|
|
157
|
+
mainProjectPath,
|
|
158
|
+
mainProjectName,
|
|
159
|
+
originalPath: this.originalPath
|
|
160
|
+
};
|
|
161
|
+
this.initialized = true;
|
|
162
|
+
logger.info(`Workspace initialized: ${this.workspacePath}`);
|
|
163
|
+
return this.mainProjectInfo;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Add a project to the workspace (creates symlink to worktree)
|
|
167
|
+
*/
|
|
168
|
+
async addProject(repository, worktreePath, description) {
|
|
169
|
+
if (!this.initialized) {
|
|
170
|
+
throw new Error("Workspace not initialized. Call initialize() first.");
|
|
171
|
+
}
|
|
172
|
+
let projectName = sanitizePathComponent(description || this.extractRepoName(repository));
|
|
173
|
+
projectName = this.getUniqueName(projectName);
|
|
174
|
+
this.usedNames.add(projectName);
|
|
175
|
+
const workspacePath = path.join(this.workspacePath, projectName);
|
|
176
|
+
await fsp.rm(workspacePath, { recursive: true, force: true });
|
|
177
|
+
try {
|
|
178
|
+
await fsp.symlink(worktreePath, workspacePath);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
throw new Error(`Failed to create symlink for project ${projectName}: ${error}`);
|
|
181
|
+
}
|
|
182
|
+
this.projects.set(projectName, {
|
|
183
|
+
name: projectName,
|
|
184
|
+
path: workspacePath,
|
|
185
|
+
worktreePath,
|
|
186
|
+
repository
|
|
187
|
+
});
|
|
188
|
+
logger.info(`Added project to workspace: ${projectName} -> ${worktreePath}`);
|
|
189
|
+
return workspacePath;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* List all projects in the workspace
|
|
193
|
+
*/
|
|
194
|
+
listProjects() {
|
|
195
|
+
return Array.from(this.projects.values());
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Cleanup the workspace
|
|
199
|
+
*/
|
|
200
|
+
async cleanup() {
|
|
201
|
+
logger.info(`Cleaning up workspace: ${this.workspacePath}`);
|
|
202
|
+
try {
|
|
203
|
+
if (this.mainProjectInfo) {
|
|
204
|
+
const mainProjectPath = this.mainProjectInfo.mainProjectPath;
|
|
205
|
+
try {
|
|
206
|
+
const stats = await fsp.lstat(mainProjectPath);
|
|
207
|
+
if (!stats.isSymbolicLink()) {
|
|
208
|
+
await this.removeMainProjectWorktree(mainProjectPath);
|
|
549
209
|
}
|
|
210
|
+
} catch {
|
|
550
211
|
}
|
|
551
|
-
},
|
|
552
|
-
additionalProperties: false,
|
|
553
|
-
description: "Claude Code configuration",
|
|
554
|
-
patternProperties: {
|
|
555
|
-
"^x-": {}
|
|
556
212
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
213
|
+
await fsp.rm(this.workspacePath, { recursive: true, force: true });
|
|
214
|
+
logger.debug(`Removed workspace directory: ${this.workspacePath}`);
|
|
215
|
+
_WorkspaceManager.instances.delete(this.sessionId);
|
|
216
|
+
this.initialized = false;
|
|
217
|
+
this.mainProjectInfo = null;
|
|
218
|
+
this.projects.clear();
|
|
219
|
+
this.usedNames.clear();
|
|
220
|
+
logger.info(`Workspace cleanup completed: ${this.sessionId}`);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
logger.warn(`Failed to cleanup workspace: ${error}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create worktree for the main project
|
|
227
|
+
*
|
|
228
|
+
* visor-disable: architecture - Not using WorktreeManager here because:
|
|
229
|
+
* 1. WorktreeManager expects remote URLs and clones to bare repos first
|
|
230
|
+
* 2. This operates on the LOCAL repo we're already in (no cloning needed)
|
|
231
|
+
* 3. Adding a "local mode" to WorktreeManager would add complexity for minimal benefit
|
|
232
|
+
* The git commands here are simpler (just rev-parse + worktree add) vs WorktreeManager's
|
|
233
|
+
* full clone/bare-repo/fetch/worktree pipeline.
|
|
234
|
+
*/
|
|
235
|
+
async createMainProjectWorktree(targetPath) {
|
|
236
|
+
logger.debug(`Creating main project worktree: ${targetPath}`);
|
|
237
|
+
const headResult = await commandExecutor.execute(
|
|
238
|
+
`git -C ${shellEscape(this.originalPath)} rev-parse HEAD`,
|
|
239
|
+
{
|
|
240
|
+
timeout: 1e4
|
|
585
241
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
242
|
+
);
|
|
243
|
+
if (headResult.exitCode !== 0) {
|
|
244
|
+
throw new Error(`Failed to get HEAD: ${headResult.stderr}`);
|
|
245
|
+
}
|
|
246
|
+
const headRef = headResult.stdout.trim();
|
|
247
|
+
const createCmd = `git -C ${shellEscape(this.originalPath)} worktree add --detach ${shellEscape(targetPath)} ${shellEscape(headRef)}`;
|
|
248
|
+
const result = await commandExecutor.execute(createCmd, { timeout: 6e4 });
|
|
249
|
+
if (result.exitCode !== 0) {
|
|
250
|
+
throw new Error(`Failed to create main project worktree: ${result.stderr}`);
|
|
251
|
+
}
|
|
252
|
+
logger.debug(`Created main project worktree at ${targetPath}`);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Remove main project worktree
|
|
256
|
+
*/
|
|
257
|
+
async removeMainProjectWorktree(worktreePath) {
|
|
258
|
+
logger.debug(`Removing main project worktree: ${worktreePath}`);
|
|
259
|
+
const removeCmd = `git -C ${shellEscape(this.originalPath)} worktree remove ${shellEscape(worktreePath)} --force`;
|
|
260
|
+
const result = await commandExecutor.execute(removeCmd, { timeout: 3e4 });
|
|
261
|
+
if (result.exitCode !== 0) {
|
|
262
|
+
logger.warn(`Failed to remove worktree via git: ${result.stderr}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Check if a path is a git repository
|
|
267
|
+
*/
|
|
268
|
+
async isGitRepository(dirPath) {
|
|
269
|
+
try {
|
|
270
|
+
const result = await commandExecutor.execute(
|
|
271
|
+
`git -C ${shellEscape(dirPath)} rev-parse --git-dir`,
|
|
599
272
|
{
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
],
|
|
603
|
-
description: "Failure condition - can be a simple expression string or complex object"
|
|
604
|
-
},
|
|
605
|
-
SimpleFailureCondition: {
|
|
606
|
-
type: "string",
|
|
607
|
-
description: "Simple failure condition - just an expression string"
|
|
608
|
-
},
|
|
609
|
-
ComplexFailureCondition: {
|
|
610
|
-
type: "object",
|
|
611
|
-
properties: {
|
|
612
|
-
condition: {
|
|
613
|
-
type: "string",
|
|
614
|
-
description: "Expression to evaluate using Function Constructor"
|
|
615
|
-
},
|
|
616
|
-
message: {
|
|
617
|
-
type: "string",
|
|
618
|
-
description: "Human-readable message when condition is met"
|
|
619
|
-
},
|
|
620
|
-
severity: {
|
|
621
|
-
$ref: "#/definitions/FailureConditionSeverity",
|
|
622
|
-
description: "Severity level of the failure"
|
|
623
|
-
},
|
|
624
|
-
halt_execution: {
|
|
625
|
-
type: "boolean",
|
|
626
|
-
description: "Whether this condition should halt execution"
|
|
627
|
-
}
|
|
628
|
-
},
|
|
629
|
-
required: ["condition"],
|
|
630
|
-
additionalProperties: false,
|
|
631
|
-
description: "Complex failure condition with additional metadata",
|
|
632
|
-
patternProperties: {
|
|
633
|
-
"^x-": {}
|
|
634
|
-
}
|
|
635
|
-
},
|
|
636
|
-
FailureConditionSeverity: {
|
|
637
|
-
type: "string",
|
|
638
|
-
enum: ["error", "warning", "info"],
|
|
639
|
-
description: "Failure condition severity levels"
|
|
640
|
-
},
|
|
641
|
-
OnFailConfig: {
|
|
642
|
-
type: "object",
|
|
643
|
-
properties: {
|
|
644
|
-
retry: {
|
|
645
|
-
$ref: "#/definitions/RetryPolicy",
|
|
646
|
-
description: "Retry policy"
|
|
647
|
-
},
|
|
648
|
-
run: {
|
|
649
|
-
type: "array",
|
|
650
|
-
items: {
|
|
651
|
-
type: "string"
|
|
652
|
-
},
|
|
653
|
-
description: "Remediation steps to run before reattempt"
|
|
654
|
-
},
|
|
655
|
-
goto: {
|
|
656
|
-
type: "string",
|
|
657
|
-
description: "Jump back to an ancestor step (by id)"
|
|
658
|
-
},
|
|
659
|
-
goto_event: {
|
|
660
|
-
$ref: "#/definitions/EventTrigger",
|
|
661
|
-
description: "Simulate a different event when performing goto (e.g., 'pr_updated')"
|
|
662
|
-
},
|
|
663
|
-
goto_js: {
|
|
664
|
-
type: "string",
|
|
665
|
-
description: "Dynamic goto: JS expression returning step id or null"
|
|
666
|
-
},
|
|
667
|
-
run_js: {
|
|
668
|
-
type: "string",
|
|
669
|
-
description: "Dynamic remediation list: JS expression returning string[]"
|
|
670
|
-
}
|
|
671
|
-
},
|
|
672
|
-
additionalProperties: false,
|
|
673
|
-
description: "Failure routing configuration per check",
|
|
674
|
-
patternProperties: {
|
|
675
|
-
"^x-": {}
|
|
676
|
-
}
|
|
677
|
-
},
|
|
678
|
-
RetryPolicy: {
|
|
679
|
-
type: "object",
|
|
680
|
-
properties: {
|
|
681
|
-
max: {
|
|
682
|
-
type: "number",
|
|
683
|
-
description: "Maximum retry attempts (excluding the first attempt)"
|
|
684
|
-
},
|
|
685
|
-
backoff: {
|
|
686
|
-
$ref: "#/definitions/BackoffPolicy",
|
|
687
|
-
description: "Backoff policy"
|
|
688
|
-
}
|
|
689
|
-
},
|
|
690
|
-
additionalProperties: false,
|
|
691
|
-
description: "Retry policy for a step",
|
|
692
|
-
patternProperties: {
|
|
693
|
-
"^x-": {}
|
|
694
|
-
}
|
|
695
|
-
},
|
|
696
|
-
BackoffPolicy: {
|
|
697
|
-
type: "object",
|
|
698
|
-
properties: {
|
|
699
|
-
mode: {
|
|
700
|
-
type: "string",
|
|
701
|
-
enum: ["fixed", "exponential"],
|
|
702
|
-
description: "Backoff mode"
|
|
703
|
-
},
|
|
704
|
-
delay_ms: {
|
|
705
|
-
type: "number",
|
|
706
|
-
description: "Initial delay in milliseconds"
|
|
707
|
-
}
|
|
708
|
-
},
|
|
709
|
-
additionalProperties: false,
|
|
710
|
-
description: "Backoff policy for retries",
|
|
711
|
-
patternProperties: {
|
|
712
|
-
"^x-": {}
|
|
713
|
-
}
|
|
714
|
-
},
|
|
715
|
-
OnSuccessConfig: {
|
|
716
|
-
type: "object",
|
|
717
|
-
properties: {
|
|
718
|
-
run: {
|
|
719
|
-
type: "array",
|
|
720
|
-
items: {
|
|
721
|
-
type: "string"
|
|
722
|
-
},
|
|
723
|
-
description: "Post-success steps to run"
|
|
724
|
-
},
|
|
725
|
-
goto: {
|
|
726
|
-
type: "string",
|
|
727
|
-
description: "Optional jump back to ancestor step (by id)"
|
|
728
|
-
},
|
|
729
|
-
goto_event: {
|
|
730
|
-
$ref: "#/definitions/EventTrigger",
|
|
731
|
-
description: "Simulate a different event when performing goto (e.g., 'pr_updated')"
|
|
732
|
-
},
|
|
733
|
-
goto_js: {
|
|
734
|
-
type: "string",
|
|
735
|
-
description: "Dynamic goto: JS expression returning step id or null"
|
|
736
|
-
},
|
|
737
|
-
run_js: {
|
|
738
|
-
type: "string",
|
|
739
|
-
description: "Dynamic post-success steps: JS expression returning string[]"
|
|
740
|
-
}
|
|
741
|
-
},
|
|
742
|
-
additionalProperties: false,
|
|
743
|
-
description: "Success routing configuration per check",
|
|
744
|
-
patternProperties: {
|
|
745
|
-
"^x-": {}
|
|
746
|
-
}
|
|
747
|
-
},
|
|
748
|
-
OutputConfig: {
|
|
749
|
-
type: "object",
|
|
750
|
-
properties: {
|
|
751
|
-
pr_comment: {
|
|
752
|
-
$ref: "#/definitions/PrCommentOutput",
|
|
753
|
-
description: "PR comment configuration"
|
|
754
|
-
},
|
|
755
|
-
file_comment: {
|
|
756
|
-
$ref: "#/definitions/FileCommentOutput",
|
|
757
|
-
description: "File comment configuration (optional)"
|
|
758
|
-
},
|
|
759
|
-
github_checks: {
|
|
760
|
-
$ref: "#/definitions/GitHubCheckOutput",
|
|
761
|
-
description: "GitHub check runs configuration (optional)"
|
|
762
|
-
},
|
|
763
|
-
suppressionEnabled: {
|
|
764
|
-
type: "boolean",
|
|
765
|
-
description: "Whether to enable issue suppression via visor-disable comments (default: true)"
|
|
766
|
-
}
|
|
767
|
-
},
|
|
768
|
-
required: ["pr_comment"],
|
|
769
|
-
additionalProperties: false,
|
|
770
|
-
description: "Output configuration",
|
|
771
|
-
patternProperties: {
|
|
772
|
-
"^x-": {}
|
|
773
|
-
}
|
|
774
|
-
},
|
|
775
|
-
PrCommentOutput: {
|
|
776
|
-
type: "object",
|
|
777
|
-
properties: {
|
|
778
|
-
format: {
|
|
779
|
-
$ref: "#/definitions/ConfigOutputFormat",
|
|
780
|
-
description: "Format of the output"
|
|
781
|
-
},
|
|
782
|
-
group_by: {
|
|
783
|
-
$ref: "#/definitions/GroupByOption",
|
|
784
|
-
description: "How to group the results"
|
|
785
|
-
},
|
|
786
|
-
collapse: {
|
|
787
|
-
type: "boolean",
|
|
788
|
-
description: "Whether to collapse sections by default"
|
|
789
|
-
},
|
|
790
|
-
debug: {
|
|
791
|
-
$ref: "#/definitions/DebugConfig",
|
|
792
|
-
description: "Debug mode configuration (optional)"
|
|
793
|
-
}
|
|
794
|
-
},
|
|
795
|
-
required: ["format", "group_by", "collapse"],
|
|
796
|
-
additionalProperties: false,
|
|
797
|
-
description: "PR comment output configuration",
|
|
798
|
-
patternProperties: {
|
|
799
|
-
"^x-": {}
|
|
800
|
-
}
|
|
801
|
-
},
|
|
802
|
-
ConfigOutputFormat: {
|
|
803
|
-
type: "string",
|
|
804
|
-
enum: ["table", "json", "markdown", "sarif"],
|
|
805
|
-
description: "Valid output formats"
|
|
806
|
-
},
|
|
807
|
-
GroupByOption: {
|
|
808
|
-
type: "string",
|
|
809
|
-
enum: ["check", "file", "severity", "group"],
|
|
810
|
-
description: "Valid grouping options"
|
|
811
|
-
},
|
|
812
|
-
DebugConfig: {
|
|
813
|
-
type: "object",
|
|
814
|
-
properties: {
|
|
815
|
-
enabled: {
|
|
816
|
-
type: "boolean",
|
|
817
|
-
description: "Enable debug mode"
|
|
818
|
-
},
|
|
819
|
-
includePrompts: {
|
|
820
|
-
type: "boolean",
|
|
821
|
-
description: "Include AI prompts in debug output"
|
|
822
|
-
},
|
|
823
|
-
includeRawResponses: {
|
|
824
|
-
type: "boolean",
|
|
825
|
-
description: "Include raw AI responses in debug output"
|
|
826
|
-
},
|
|
827
|
-
includeTiming: {
|
|
828
|
-
type: "boolean",
|
|
829
|
-
description: "Include timing information"
|
|
830
|
-
},
|
|
831
|
-
includeProviderInfo: {
|
|
832
|
-
type: "boolean",
|
|
833
|
-
description: "Include provider information"
|
|
834
|
-
}
|
|
835
|
-
},
|
|
836
|
-
required: [
|
|
837
|
-
"enabled",
|
|
838
|
-
"includePrompts",
|
|
839
|
-
"includeRawResponses",
|
|
840
|
-
"includeTiming",
|
|
841
|
-
"includeProviderInfo"
|
|
842
|
-
],
|
|
843
|
-
additionalProperties: false,
|
|
844
|
-
description: "Debug mode configuration",
|
|
845
|
-
patternProperties: {
|
|
846
|
-
"^x-": {}
|
|
847
|
-
}
|
|
848
|
-
},
|
|
849
|
-
FileCommentOutput: {
|
|
850
|
-
type: "object",
|
|
851
|
-
properties: {
|
|
852
|
-
enabled: {
|
|
853
|
-
type: "boolean",
|
|
854
|
-
description: "Whether file comments are enabled"
|
|
855
|
-
},
|
|
856
|
-
inline: {
|
|
857
|
-
type: "boolean",
|
|
858
|
-
description: "Whether to show inline comments"
|
|
859
|
-
}
|
|
860
|
-
},
|
|
861
|
-
required: ["enabled", "inline"],
|
|
862
|
-
additionalProperties: false,
|
|
863
|
-
description: "File comment output configuration",
|
|
864
|
-
patternProperties: {
|
|
865
|
-
"^x-": {}
|
|
866
|
-
}
|
|
867
|
-
},
|
|
868
|
-
GitHubCheckOutput: {
|
|
869
|
-
type: "object",
|
|
870
|
-
properties: {
|
|
871
|
-
enabled: {
|
|
872
|
-
type: "boolean",
|
|
873
|
-
description: "Whether GitHub check runs are enabled"
|
|
874
|
-
},
|
|
875
|
-
per_check: {
|
|
876
|
-
type: "boolean",
|
|
877
|
-
description: "Whether to create individual check runs per configured check"
|
|
878
|
-
},
|
|
879
|
-
name_prefix: {
|
|
880
|
-
type: "string",
|
|
881
|
-
description: "Custom name prefix for check runs"
|
|
273
|
+
timeout: 5e3
|
|
882
274
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
type: "string",
|
|
904
|
-
description: "Host/IP to bind to (defaults to 0.0.0.0)"
|
|
905
|
-
},
|
|
906
|
-
tls: {
|
|
907
|
-
$ref: "#/definitions/TlsConfig",
|
|
908
|
-
description: "TLS/SSL configuration for HTTPS"
|
|
909
|
-
},
|
|
910
|
-
auth: {
|
|
911
|
-
$ref: "#/definitions/HttpAuthConfig",
|
|
912
|
-
description: "Authentication configuration"
|
|
913
|
-
},
|
|
914
|
-
endpoints: {
|
|
915
|
-
type: "array",
|
|
916
|
-
items: {
|
|
917
|
-
$ref: "#/definitions/HttpEndpointConfig"
|
|
918
|
-
},
|
|
919
|
-
description: "HTTP endpoints configuration"
|
|
920
|
-
}
|
|
921
|
-
},
|
|
922
|
-
required: ["enabled", "port"],
|
|
923
|
-
additionalProperties: false,
|
|
924
|
-
description: "HTTP server configuration for receiving webhooks",
|
|
925
|
-
patternProperties: {
|
|
926
|
-
"^x-": {}
|
|
927
|
-
}
|
|
928
|
-
},
|
|
929
|
-
TlsConfig: {
|
|
930
|
-
type: "object",
|
|
931
|
-
properties: {
|
|
932
|
-
enabled: {
|
|
933
|
-
type: "boolean",
|
|
934
|
-
description: "Enable TLS/HTTPS"
|
|
935
|
-
},
|
|
936
|
-
cert: {
|
|
937
|
-
type: "string",
|
|
938
|
-
description: "Path to TLS certificate file or certificate content"
|
|
939
|
-
},
|
|
940
|
-
key: {
|
|
941
|
-
type: "string",
|
|
942
|
-
description: "Path to TLS key file or key content"
|
|
943
|
-
},
|
|
944
|
-
ca: {
|
|
945
|
-
type: "string",
|
|
946
|
-
description: "Path to CA certificate file or CA content (optional)"
|
|
947
|
-
},
|
|
948
|
-
rejectUnauthorized: {
|
|
949
|
-
type: "boolean",
|
|
950
|
-
description: "Reject unauthorized connections (default: true)"
|
|
951
|
-
}
|
|
952
|
-
},
|
|
953
|
-
required: ["enabled"],
|
|
954
|
-
additionalProperties: false,
|
|
955
|
-
description: "TLS/SSL configuration for HTTPS server",
|
|
956
|
-
patternProperties: {
|
|
957
|
-
"^x-": {}
|
|
958
|
-
}
|
|
959
|
-
},
|
|
960
|
-
HttpAuthConfig: {
|
|
961
|
-
type: "object",
|
|
962
|
-
properties: {
|
|
963
|
-
type: {
|
|
964
|
-
type: "string",
|
|
965
|
-
enum: ["bearer_token", "hmac", "basic", "none"],
|
|
966
|
-
description: "Authentication type"
|
|
967
|
-
},
|
|
968
|
-
secret: {
|
|
969
|
-
type: "string",
|
|
970
|
-
description: "Secret or token for authentication"
|
|
971
|
-
},
|
|
972
|
-
username: {
|
|
973
|
-
type: "string",
|
|
974
|
-
description: "Username for basic auth"
|
|
975
|
-
},
|
|
976
|
-
password: {
|
|
977
|
-
type: "string",
|
|
978
|
-
description: "Password for basic auth"
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
required: ["type"],
|
|
982
|
-
additionalProperties: false,
|
|
983
|
-
description: "HTTP server authentication configuration",
|
|
984
|
-
patternProperties: {
|
|
985
|
-
"^x-": {}
|
|
986
|
-
}
|
|
987
|
-
},
|
|
988
|
-
HttpEndpointConfig: {
|
|
989
|
-
type: "object",
|
|
990
|
-
properties: {
|
|
991
|
-
path: {
|
|
992
|
-
type: "string",
|
|
993
|
-
description: "Path for the webhook endpoint"
|
|
994
|
-
},
|
|
995
|
-
transform: {
|
|
996
|
-
type: "string",
|
|
997
|
-
description: "Optional transform template (Liquid) for the received data"
|
|
998
|
-
},
|
|
999
|
-
name: {
|
|
1000
|
-
type: "string",
|
|
1001
|
-
description: "Optional name/ID for this endpoint"
|
|
1002
|
-
}
|
|
1003
|
-
},
|
|
1004
|
-
required: ["path"],
|
|
1005
|
-
additionalProperties: false,
|
|
1006
|
-
description: "HTTP server endpoint configuration",
|
|
1007
|
-
patternProperties: {
|
|
1008
|
-
"^x-": {}
|
|
1009
|
-
}
|
|
1010
|
-
},
|
|
1011
|
-
MemoryConfig: {
|
|
1012
|
-
type: "object",
|
|
1013
|
-
properties: {
|
|
1014
|
-
storage: {
|
|
1015
|
-
type: "string",
|
|
1016
|
-
enum: ["memory", "file"],
|
|
1017
|
-
description: 'Storage mode: "memory" (in-memory, default) or "file" (persistent)'
|
|
1018
|
-
},
|
|
1019
|
-
format: {
|
|
1020
|
-
type: "string",
|
|
1021
|
-
enum: ["json", "csv"],
|
|
1022
|
-
description: "Storage format (only for file storage, default: json)"
|
|
1023
|
-
},
|
|
1024
|
-
file: {
|
|
1025
|
-
type: "string",
|
|
1026
|
-
description: "File path (required if storage: file)"
|
|
1027
|
-
},
|
|
1028
|
-
namespace: {
|
|
1029
|
-
type: "string",
|
|
1030
|
-
description: 'Default namespace (default: "default")'
|
|
1031
|
-
},
|
|
1032
|
-
auto_load: {
|
|
1033
|
-
type: "boolean",
|
|
1034
|
-
description: "Auto-load on startup (default: true if storage: file)"
|
|
1035
|
-
},
|
|
1036
|
-
auto_save: {
|
|
1037
|
-
type: "boolean",
|
|
1038
|
-
description: "Auto-save after operations (default: true if storage: file)"
|
|
1039
|
-
}
|
|
1040
|
-
},
|
|
1041
|
-
additionalProperties: false,
|
|
1042
|
-
description: "Memory storage configuration",
|
|
1043
|
-
patternProperties: {
|
|
1044
|
-
"^x-": {}
|
|
1045
|
-
}
|
|
1046
|
-
},
|
|
1047
|
-
TagFilter: {
|
|
1048
|
-
type: "object",
|
|
1049
|
-
properties: {
|
|
1050
|
-
include: {
|
|
1051
|
-
type: "array",
|
|
1052
|
-
items: {
|
|
1053
|
-
type: "string"
|
|
1054
|
-
},
|
|
1055
|
-
description: "Tags that checks must have to be included (ANY match)"
|
|
1056
|
-
},
|
|
1057
|
-
exclude: {
|
|
1058
|
-
type: "array",
|
|
1059
|
-
items: {
|
|
1060
|
-
type: "string"
|
|
1061
|
-
},
|
|
1062
|
-
description: "Tags that will exclude checks if present (ANY match)"
|
|
1063
|
-
}
|
|
1064
|
-
},
|
|
1065
|
-
additionalProperties: false,
|
|
1066
|
-
description: "Tag filter configuration for selective check execution",
|
|
1067
|
-
patternProperties: {
|
|
1068
|
-
"^x-": {}
|
|
1069
|
-
}
|
|
1070
|
-
},
|
|
1071
|
-
RoutingDefaults: {
|
|
1072
|
-
type: "object",
|
|
1073
|
-
properties: {
|
|
1074
|
-
max_loops: {
|
|
1075
|
-
type: "number",
|
|
1076
|
-
description: "Per-scope cap on routing transitions (success + failure)"
|
|
1077
|
-
},
|
|
1078
|
-
defaults: {
|
|
1079
|
-
type: "object",
|
|
1080
|
-
properties: {
|
|
1081
|
-
on_fail: {
|
|
1082
|
-
$ref: "#/definitions/OnFailConfig"
|
|
1083
|
-
}
|
|
1084
|
-
},
|
|
1085
|
-
additionalProperties: false,
|
|
1086
|
-
description: "Default policies applied to checks (step-level overrides take precedence)",
|
|
1087
|
-
patternProperties: {
|
|
1088
|
-
"^x-": {}
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
},
|
|
1092
|
-
additionalProperties: false,
|
|
1093
|
-
description: "Global routing defaults",
|
|
1094
|
-
patternProperties: {
|
|
1095
|
-
"^x-": {}
|
|
275
|
+
);
|
|
276
|
+
return result.exitCode === 0;
|
|
277
|
+
} catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Extract project name from path
|
|
283
|
+
*/
|
|
284
|
+
extractProjectName(dirPath) {
|
|
285
|
+
return path.basename(dirPath);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Extract repository name from owner/repo format
|
|
289
|
+
*/
|
|
290
|
+
extractRepoName(repository) {
|
|
291
|
+
if (repository.includes("://") || repository.startsWith("git@")) {
|
|
292
|
+
const match = repository.match(/[/:]([^/:]+\/[^/:]+?)(?:\.git)?$/);
|
|
293
|
+
if (match) {
|
|
294
|
+
return match[1].split("/").pop() || repository;
|
|
1096
295
|
}
|
|
1097
296
|
}
|
|
297
|
+
if (repository.includes("/")) {
|
|
298
|
+
return repository.split("/").pop() || repository;
|
|
299
|
+
}
|
|
300
|
+
return repository;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Get a unique name by appending a number if needed
|
|
304
|
+
*/
|
|
305
|
+
getUniqueName(baseName) {
|
|
306
|
+
if (!this.usedNames.has(baseName)) {
|
|
307
|
+
return baseName;
|
|
308
|
+
}
|
|
309
|
+
let counter = 2;
|
|
310
|
+
let uniqueName = `${baseName}-${counter}`;
|
|
311
|
+
while (this.usedNames.has(uniqueName)) {
|
|
312
|
+
counter++;
|
|
313
|
+
uniqueName = `${baseName}-${counter}`;
|
|
314
|
+
}
|
|
315
|
+
return uniqueName;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Register cleanup handlers for process exit
|
|
319
|
+
*/
|
|
320
|
+
registerCleanupHandlers() {
|
|
321
|
+
if (this.cleanupHandlersRegistered || !this.config.cleanupOnExit) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
this.cleanupHandlersRegistered = true;
|
|
1098
325
|
}
|
|
1099
326
|
};
|
|
1100
|
-
config_schema_default = configSchema;
|
|
1101
327
|
}
|
|
1102
328
|
});
|
|
1103
329
|
|
|
1104
|
-
// src/
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
330
|
+
// src/state-machine/context/build-engine-context.ts
|
|
331
|
+
var build_engine_context_exports = {};
|
|
332
|
+
__export(build_engine_context_exports, {
|
|
333
|
+
buildEngineContextForRun: () => buildEngineContextForRun,
|
|
334
|
+
initializeWorkspace: () => initializeWorkspace
|
|
335
|
+
});
|
|
336
|
+
import { v4 as uuidv4 } from "uuid";
|
|
337
|
+
function applyCriticalityDefaults(cfg) {
|
|
338
|
+
const checks = cfg.checks || {};
|
|
339
|
+
for (const id of Object.keys(checks)) {
|
|
340
|
+
const c = checks[id];
|
|
341
|
+
if (!c.criticality) c.criticality = "policy";
|
|
342
|
+
if (c.criticality === "info" && typeof c.continue_on_failure === "undefined")
|
|
343
|
+
c.continue_on_failure = true;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function buildEngineContextForRun(workingDirectory, config, prInfo, debug, maxParallelism, failFast, requestedChecks) {
|
|
347
|
+
const clonedConfig = JSON.parse(JSON.stringify(config));
|
|
348
|
+
const checks = {};
|
|
349
|
+
applyCriticalityDefaults(clonedConfig);
|
|
350
|
+
for (const [checkId, checkConfig] of Object.entries(clonedConfig.checks || {})) {
|
|
351
|
+
checks[checkId] = {
|
|
352
|
+
tags: checkConfig.tags || [],
|
|
353
|
+
triggers: (Array.isArray(checkConfig.on) ? checkConfig.on : [checkConfig.on]).filter(
|
|
354
|
+
Boolean
|
|
355
|
+
),
|
|
356
|
+
group: checkConfig.group,
|
|
357
|
+
providerType: checkConfig.type || "ai",
|
|
358
|
+
// Normalize depends_on to array (supports string | string[])
|
|
359
|
+
dependencies: Array.isArray(checkConfig.depends_on) ? checkConfig.depends_on : checkConfig.depends_on ? [checkConfig.depends_on] : []
|
|
1129
360
|
};
|
|
1130
361
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
362
|
+
if (requestedChecks && requestedChecks.length > 0) {
|
|
363
|
+
for (const checkName of requestedChecks) {
|
|
364
|
+
if (!checks[checkName] && !clonedConfig.checks?.[checkName]) {
|
|
365
|
+
logger.debug(`[StateMachine] Synthesizing minimal config for legacy check: ${checkName}`);
|
|
366
|
+
if (!clonedConfig.checks) {
|
|
367
|
+
clonedConfig.checks = {};
|
|
368
|
+
}
|
|
369
|
+
clonedConfig.checks[checkName] = {
|
|
370
|
+
type: "ai",
|
|
371
|
+
prompt: `Perform ${checkName} analysis`
|
|
372
|
+
};
|
|
373
|
+
checks[checkName] = {
|
|
374
|
+
tags: [],
|
|
375
|
+
triggers: [],
|
|
376
|
+
group: "default",
|
|
377
|
+
providerType: "ai",
|
|
378
|
+
dependencies: []
|
|
379
|
+
};
|
|
380
|
+
}
|
|
1142
381
|
}
|
|
1143
|
-
return "local" /* LOCAL */;
|
|
1144
382
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
383
|
+
const journal = new ExecutionJournal();
|
|
384
|
+
const memory = MemoryStore.getInstance(clonedConfig.memory);
|
|
385
|
+
return {
|
|
386
|
+
mode: "state-machine",
|
|
387
|
+
config: clonedConfig,
|
|
388
|
+
checks,
|
|
389
|
+
journal,
|
|
390
|
+
memory,
|
|
391
|
+
workingDirectory,
|
|
392
|
+
originalWorkingDirectory: workingDirectory,
|
|
393
|
+
sessionId: uuidv4(),
|
|
394
|
+
event: prInfo.eventType,
|
|
395
|
+
debug,
|
|
396
|
+
maxParallelism,
|
|
397
|
+
failFast,
|
|
398
|
+
requestedChecks: requestedChecks && requestedChecks.length > 0 ? requestedChecks : void 0,
|
|
399
|
+
// Store prInfo for later access (e.g., in getOutputHistorySnapshot)
|
|
400
|
+
prInfo
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
async function initializeWorkspace(context) {
|
|
404
|
+
const workspaceConfig = context.config.workspace;
|
|
405
|
+
const isEnabled = workspaceConfig?.enabled !== false && process.env.VISOR_WORKSPACE_ENABLED !== "false";
|
|
406
|
+
if (!isEnabled) {
|
|
407
|
+
logger.debug("[Workspace] Workspace isolation is disabled");
|
|
408
|
+
return context;
|
|
409
|
+
}
|
|
410
|
+
const originalPath = context.workingDirectory || process.cwd();
|
|
411
|
+
try {
|
|
412
|
+
const keepWorkspace = process.env.VISOR_KEEP_WORKSPACE === "true";
|
|
413
|
+
const workspace = WorkspaceManager.getInstance(context.sessionId, originalPath, {
|
|
414
|
+
enabled: true,
|
|
415
|
+
basePath: workspaceConfig?.base_path || process.env.VISOR_WORKSPACE_PATH || "/tmp/visor-workspaces",
|
|
416
|
+
cleanupOnExit: keepWorkspace ? false : workspaceConfig?.cleanup_on_exit !== false
|
|
417
|
+
});
|
|
418
|
+
const info = await workspace.initialize();
|
|
419
|
+
context.workspace = workspace;
|
|
420
|
+
context.workingDirectory = info.mainProjectPath;
|
|
421
|
+
context.originalWorkingDirectory = originalPath;
|
|
422
|
+
logger.info(`[Workspace] Initialized workspace: ${info.workspacePath}`);
|
|
423
|
+
logger.debug(`[Workspace] Main project at: ${info.mainProjectPath}`);
|
|
424
|
+
if (keepWorkspace) {
|
|
425
|
+
logger.info(`[Workspace] Keeping workspace after execution (--keep-workspace)`);
|
|
426
|
+
}
|
|
427
|
+
return context;
|
|
428
|
+
} catch (error) {
|
|
429
|
+
logger.warn(`[Workspace] Failed to initialize workspace: ${error}`);
|
|
430
|
+
logger.debug("[Workspace] Continuing without workspace isolation");
|
|
431
|
+
return context;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
var init_build_engine_context = __esm({
|
|
435
|
+
"src/state-machine/context/build-engine-context.ts"() {
|
|
436
|
+
"use strict";
|
|
437
|
+
init_snapshot_store();
|
|
438
|
+
init_memory_store();
|
|
439
|
+
init_logger();
|
|
440
|
+
init_workspace_manager();
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// src/state-machine/execution/summary.ts
|
|
445
|
+
var summary_exports = {};
|
|
446
|
+
__export(summary_exports, {
|
|
447
|
+
convertToReviewSummary: () => convertToReviewSummary
|
|
448
|
+
});
|
|
449
|
+
function convertToReviewSummary(groupedResults, statistics) {
|
|
450
|
+
const allIssues = [];
|
|
451
|
+
for (const checkResults of Object.values(groupedResults)) {
|
|
452
|
+
for (const checkResult of checkResults) {
|
|
453
|
+
if (checkResult.issues && checkResult.issues.length > 0) {
|
|
454
|
+
allIssues.push(...checkResult.issues);
|
|
1177
455
|
}
|
|
1178
|
-
} finally {
|
|
1179
|
-
this.loadedConfigs.delete(normalizedSource);
|
|
1180
456
|
}
|
|
1181
457
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
458
|
+
if (statistics) {
|
|
459
|
+
for (const checkStats of statistics.checks) {
|
|
460
|
+
if (checkStats.errorMessage) {
|
|
461
|
+
allIssues.push({
|
|
462
|
+
file: "system",
|
|
463
|
+
line: 0,
|
|
464
|
+
endLine: void 0,
|
|
465
|
+
ruleId: "system/error",
|
|
466
|
+
message: checkStats.errorMessage,
|
|
467
|
+
severity: "error",
|
|
468
|
+
category: "logic",
|
|
469
|
+
suggestion: void 0,
|
|
470
|
+
replacement: void 0
|
|
471
|
+
});
|
|
472
|
+
}
|
|
1197
473
|
}
|
|
1198
474
|
}
|
|
475
|
+
return {
|
|
476
|
+
issues: allIssues
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
var init_summary = __esm({
|
|
480
|
+
"src/state-machine/execution/summary.ts"() {
|
|
481
|
+
"use strict";
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// src/state-machine-execution-engine.ts
|
|
486
|
+
init_runner();
|
|
487
|
+
init_logger();
|
|
488
|
+
import * as path2 from "path";
|
|
489
|
+
import * as fs from "fs";
|
|
490
|
+
var StateMachineExecutionEngine = class _StateMachineExecutionEngine {
|
|
491
|
+
workingDirectory;
|
|
492
|
+
executionContext;
|
|
493
|
+
debugServer;
|
|
494
|
+
_lastContext;
|
|
495
|
+
_lastRunner;
|
|
496
|
+
constructor(workingDirectory, octokit, debugServer) {
|
|
497
|
+
this.workingDirectory = workingDirectory || process.cwd();
|
|
498
|
+
this.debugServer = debugServer;
|
|
499
|
+
}
|
|
1199
500
|
/**
|
|
1200
|
-
*
|
|
501
|
+
* Execute checks using the state machine engine
|
|
502
|
+
*
|
|
503
|
+
* Converts CheckExecutionOptions -> executeGroupedChecks() -> AnalysisResult
|
|
1201
504
|
*/
|
|
1202
|
-
async
|
|
1203
|
-
const
|
|
1204
|
-
const
|
|
1205
|
-
this.validateLocalPath(resolvedPath);
|
|
1206
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
1207
|
-
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
1208
|
-
}
|
|
505
|
+
async executeChecks(options) {
|
|
506
|
+
const startTime = Date.now();
|
|
507
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1209
508
|
try {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
509
|
+
if (options.config?.memory) {
|
|
510
|
+
const { MemoryStore: MemoryStore2 } = await import("./memory-store-XGBB7LX7.mjs");
|
|
511
|
+
const memoryStore = MemoryStore2.getInstance(options.config.memory);
|
|
512
|
+
await memoryStore.initialize();
|
|
513
|
+
logger.debug("Memory store initialized");
|
|
1214
514
|
}
|
|
1215
|
-
const
|
|
1216
|
-
|
|
515
|
+
const { GitRepositoryAnalyzer } = await import("./git-repository-analyzer-HJC4MYW4.mjs");
|
|
516
|
+
const gitAnalyzer = new GitRepositoryAnalyzer(options.workingDirectory);
|
|
517
|
+
logger.info("Analyzing local git repository...");
|
|
518
|
+
const repositoryInfo = await gitAnalyzer.analyzeRepository();
|
|
519
|
+
if (!repositoryInfo.isGitRepository) {
|
|
520
|
+
return this.createErrorResult(
|
|
521
|
+
repositoryInfo,
|
|
522
|
+
"Not a git repository or no changes found",
|
|
523
|
+
startTime,
|
|
524
|
+
timestamp,
|
|
525
|
+
options.checks
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
const prInfo = gitAnalyzer.toPRInfo(repositoryInfo);
|
|
1217
529
|
try {
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
}
|
|
1222
|
-
return config;
|
|
1223
|
-
} finally {
|
|
1224
|
-
this.options.baseDir = previousBaseDir;
|
|
530
|
+
const evt = options.webhookContext?.eventType;
|
|
531
|
+
if (evt) prInfo.eventType = evt;
|
|
532
|
+
} catch {
|
|
1225
533
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
534
|
+
const filteredChecks = this.filterChecksByTags(
|
|
535
|
+
options.checks,
|
|
536
|
+
options.config,
|
|
537
|
+
options.tagFilter || options.config?.tag_filter
|
|
538
|
+
);
|
|
539
|
+
if (filteredChecks.length === 0) {
|
|
540
|
+
logger.warn("No checks match the tag filter criteria");
|
|
541
|
+
return this.createErrorResult(
|
|
542
|
+
repositoryInfo,
|
|
543
|
+
"No checks match the tag filter criteria",
|
|
544
|
+
startTime,
|
|
545
|
+
timestamp,
|
|
546
|
+
options.checks
|
|
547
|
+
);
|
|
1229
548
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}
|
|
1240
|
-
this.validateRemoteURL(url);
|
|
1241
|
-
const cacheEntry = this.cache.get(url);
|
|
1242
|
-
if (cacheEntry && Date.now() - cacheEntry.timestamp < cacheEntry.ttl) {
|
|
1243
|
-
const outputFormat2 = process.env.VISOR_OUTPUT_FORMAT;
|
|
1244
|
-
const logFn2 = outputFormat2 === "json" || outputFormat2 === "sarif" ? console.error : console.log;
|
|
1245
|
-
logFn2(`\u{1F4E6} Using cached configuration from: ${url}`);
|
|
1246
|
-
return cacheEntry.config;
|
|
1247
|
-
}
|
|
1248
|
-
const outputFormat = process.env.VISOR_OUTPUT_FORMAT;
|
|
1249
|
-
const logFn = outputFormat === "json" || outputFormat === "sarif" ? console.error : console.log;
|
|
1250
|
-
logFn(`\u2B07\uFE0F Fetching remote configuration from: ${url}`);
|
|
1251
|
-
const controller = new AbortController();
|
|
1252
|
-
const timeoutMs = this.options.timeout ?? 3e4;
|
|
1253
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
1254
|
-
try {
|
|
1255
|
-
const response = await fetch(url, {
|
|
1256
|
-
signal: controller.signal,
|
|
1257
|
-
headers: {
|
|
1258
|
-
"User-Agent": "Visor/1.0"
|
|
549
|
+
try {
|
|
550
|
+
const map = options?.webhookContext?.webhookData;
|
|
551
|
+
if (map) {
|
|
552
|
+
const { CheckProviderRegistry } = await import("./check-provider-registry-534KL5HT.mjs");
|
|
553
|
+
const reg = CheckProviderRegistry.getInstance();
|
|
554
|
+
const p = reg.getProvider("http_input");
|
|
555
|
+
if (p && typeof p.setWebhookContext === "function") p.setWebhookContext(map);
|
|
556
|
+
const prev = this.executionContext || {};
|
|
557
|
+
this.setExecutionContext({ ...prev, webhookContext: { webhookData: map } });
|
|
1259
558
|
}
|
|
1260
|
-
}
|
|
1261
|
-
if (!response.ok) {
|
|
1262
|
-
throw new Error(`Failed to fetch config: ${response.status} ${response.statusText}`);
|
|
559
|
+
} catch {
|
|
1263
560
|
}
|
|
1264
|
-
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
561
|
+
logger.info(`Executing checks: ${filteredChecks.join(", ")}`);
|
|
562
|
+
const executionResult = await this.executeGroupedChecks(
|
|
563
|
+
prInfo,
|
|
564
|
+
filteredChecks,
|
|
565
|
+
options.timeout,
|
|
566
|
+
options.config,
|
|
567
|
+
options.outputFormat,
|
|
568
|
+
options.debug,
|
|
569
|
+
options.maxParallelism,
|
|
570
|
+
options.failFast,
|
|
571
|
+
options.tagFilter
|
|
572
|
+
);
|
|
573
|
+
const executionTime = Date.now() - startTime;
|
|
574
|
+
const reviewSummary = this.convertGroupedResultsToReviewSummary(
|
|
575
|
+
executionResult.results,
|
|
576
|
+
executionResult.statistics
|
|
577
|
+
);
|
|
578
|
+
let debugInfo;
|
|
579
|
+
if (options.debug && reviewSummary.debug) {
|
|
580
|
+
debugInfo = {
|
|
581
|
+
provider: reviewSummary.debug.provider,
|
|
582
|
+
model: reviewSummary.debug.model,
|
|
583
|
+
processingTime: reviewSummary.debug.processingTime,
|
|
584
|
+
parallelExecution: options.checks.length > 1,
|
|
585
|
+
checksExecuted: options.checks,
|
|
586
|
+
totalApiCalls: reviewSummary.debug.totalApiCalls || options.checks.length,
|
|
587
|
+
apiCallDetails: reviewSummary.debug.apiCallDetails
|
|
588
|
+
};
|
|
1268
589
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
});
|
|
1274
|
-
if (config.extends) {
|
|
1275
|
-
return await this.processExtends(config);
|
|
590
|
+
try {
|
|
591
|
+
const histSnap = this.getOutputHistorySnapshot();
|
|
592
|
+
reviewSummary.history = histSnap;
|
|
593
|
+
} catch {
|
|
1276
594
|
}
|
|
1277
|
-
return
|
|
595
|
+
return {
|
|
596
|
+
repositoryInfo,
|
|
597
|
+
reviewSummary,
|
|
598
|
+
executionTime,
|
|
599
|
+
timestamp,
|
|
600
|
+
checksExecuted: filteredChecks,
|
|
601
|
+
executionStatistics: executionResult.statistics,
|
|
602
|
+
debug: debugInfo
|
|
603
|
+
};
|
|
1278
604
|
} catch (error) {
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
throw
|
|
1284
|
-
}
|
|
1285
|
-
throw error;
|
|
1286
|
-
} finally {
|
|
1287
|
-
clearTimeout(timeoutId);
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Load bundled default configuration
|
|
1292
|
-
*/
|
|
1293
|
-
async fetchDefaultConfig() {
|
|
1294
|
-
const possiblePaths = [
|
|
1295
|
-
// When running as GitHub Action (bundled in dist/)
|
|
1296
|
-
path.join(__dirname, "defaults", ".visor.yaml"),
|
|
1297
|
-
// When running from source
|
|
1298
|
-
path.join(__dirname, "..", "..", "defaults", ".visor.yaml"),
|
|
1299
|
-
// Try via package root
|
|
1300
|
-
this.findPackageRoot() ? path.join(this.findPackageRoot(), "defaults", ".visor.yaml") : "",
|
|
1301
|
-
// GitHub Action environment variable
|
|
1302
|
-
process.env.GITHUB_ACTION_PATH ? path.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml") : "",
|
|
1303
|
-
process.env.GITHUB_ACTION_PATH ? path.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml") : ""
|
|
1304
|
-
].filter((p) => p);
|
|
1305
|
-
let defaultConfigPath;
|
|
1306
|
-
for (const possiblePath of possiblePaths) {
|
|
1307
|
-
if (fs.existsSync(possiblePath)) {
|
|
1308
|
-
defaultConfigPath = possiblePath;
|
|
1309
|
-
break;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
if (defaultConfigPath && fs.existsSync(defaultConfigPath)) {
|
|
1313
|
-
console.error(`\u{1F4E6} Loading bundled default configuration from ${defaultConfigPath}`);
|
|
1314
|
-
const content = fs.readFileSync(defaultConfigPath, "utf8");
|
|
1315
|
-
let config = yaml.load(content);
|
|
1316
|
-
if (!config || typeof config !== "object") {
|
|
1317
|
-
throw new Error("Invalid default configuration");
|
|
1318
|
-
}
|
|
1319
|
-
config = this.normalizeStepsAndChecks(config);
|
|
1320
|
-
if (config.extends) {
|
|
1321
|
-
return await this.processExtends(config);
|
|
1322
|
-
}
|
|
1323
|
-
return config;
|
|
1324
|
-
}
|
|
1325
|
-
console.warn("\u26A0\uFE0F Bundled default configuration not found, using minimal defaults");
|
|
1326
|
-
return {
|
|
1327
|
-
version: "1.0",
|
|
1328
|
-
checks: {},
|
|
1329
|
-
output: {
|
|
1330
|
-
pr_comment: {
|
|
1331
|
-
format: "markdown",
|
|
1332
|
-
group_by: "check",
|
|
1333
|
-
collapse: true
|
|
1334
|
-
}
|
|
605
|
+
const message = error instanceof Error ? error.message : "Unknown error occurred";
|
|
606
|
+
logger.error("Error executing checks: " + message);
|
|
607
|
+
const strictEnv = process.env.VISOR_STRICT_ERRORS === "true";
|
|
608
|
+
if (strictEnv) {
|
|
609
|
+
throw error;
|
|
1335
610
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
for (const parentConfig of parentConfigs) {
|
|
1356
|
-
mergedParents = merger.merge(mergedParents, parentConfig);
|
|
611
|
+
const fallbackRepositoryInfo = {
|
|
612
|
+
title: "Error during analysis",
|
|
613
|
+
body: `Error: ${message || "Unknown error"}`,
|
|
614
|
+
author: "system",
|
|
615
|
+
base: "main",
|
|
616
|
+
head: "HEAD",
|
|
617
|
+
files: [],
|
|
618
|
+
totalAdditions: 0,
|
|
619
|
+
totalDeletions: 0,
|
|
620
|
+
isGitRepository: false,
|
|
621
|
+
workingDirectory: options.workingDirectory || process.cwd()
|
|
622
|
+
};
|
|
623
|
+
return this.createErrorResult(
|
|
624
|
+
fallbackRepositoryInfo,
|
|
625
|
+
message || "Unknown error occurred",
|
|
626
|
+
startTime,
|
|
627
|
+
timestamp,
|
|
628
|
+
options.checks
|
|
629
|
+
);
|
|
1357
630
|
}
|
|
1358
|
-
return merger.merge(mergedParents, configWithoutExtends);
|
|
1359
631
|
}
|
|
1360
632
|
/**
|
|
1361
|
-
*
|
|
633
|
+
* Get execution context (used by state machine to propagate hooks)
|
|
1362
634
|
*/
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
const { execSync } = __require("child_process");
|
|
1366
|
-
const gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
|
|
1367
|
-
if (gitRoot) return gitRoot;
|
|
1368
|
-
} catch {
|
|
1369
|
-
}
|
|
1370
|
-
const packageRoot = this.findPackageRoot();
|
|
1371
|
-
if (packageRoot) return packageRoot;
|
|
1372
|
-
return process.cwd();
|
|
635
|
+
getExecutionContext() {
|
|
636
|
+
return this.executionContext;
|
|
1373
637
|
}
|
|
1374
638
|
/**
|
|
1375
|
-
*
|
|
639
|
+
* Set execution context for external callers
|
|
1376
640
|
*/
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
if (allowedPatterns.length === 0) {
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
|
-
const isAllowed = allowedPatterns.some((pattern) => url.startsWith(pattern));
|
|
1383
|
-
if (!isAllowed) {
|
|
1384
|
-
throw new Error(
|
|
1385
|
-
`Security error: URL ${url} is not in the allowed list. Allowed patterns: ${allowedPatterns.join(", ")}`
|
|
1386
|
-
);
|
|
1387
|
-
}
|
|
641
|
+
setExecutionContext(context) {
|
|
642
|
+
this.executionContext = context;
|
|
1388
643
|
}
|
|
1389
644
|
/**
|
|
1390
|
-
*
|
|
645
|
+
* Reset per-run state (no-op for state machine engine)
|
|
646
|
+
*
|
|
647
|
+
* The state machine engine is stateless per-run by design.
|
|
648
|
+
* Each execution creates a fresh journal and context.
|
|
649
|
+
* This method exists only for backward compatibility with test framework.
|
|
650
|
+
*
|
|
651
|
+
* @deprecated This is a no-op. State machine engine doesn't maintain per-run state.
|
|
1391
652
|
*/
|
|
1392
|
-
|
|
1393
|
-
const projectRoot = this.options.projectRoot || process.cwd();
|
|
1394
|
-
const normalizedPath = path.normalize(resolvedPath);
|
|
1395
|
-
const normalizedRoot = path.normalize(projectRoot);
|
|
1396
|
-
if (!normalizedPath.startsWith(normalizedRoot)) {
|
|
1397
|
-
throw new Error(
|
|
1398
|
-
`Security error: Path traversal detected. Cannot access files outside project root: ${projectRoot}`
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
const sensitivePatterns = [
|
|
1402
|
-
"/etc/passwd",
|
|
1403
|
-
"/etc/shadow",
|
|
1404
|
-
"/.ssh/",
|
|
1405
|
-
"/.aws/",
|
|
1406
|
-
"/.env",
|
|
1407
|
-
"/private/"
|
|
1408
|
-
];
|
|
1409
|
-
const lowerPath = normalizedPath.toLowerCase();
|
|
1410
|
-
for (const pattern of sensitivePatterns) {
|
|
1411
|
-
if (lowerPath.includes(pattern)) {
|
|
1412
|
-
throw new Error(`Security error: Cannot access potentially sensitive file: ${pattern}`);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
653
|
+
resetPerRunState() {
|
|
1415
654
|
}
|
|
1416
655
|
/**
|
|
1417
|
-
*
|
|
656
|
+
* Execute grouped checks using the state machine engine
|
|
657
|
+
*
|
|
658
|
+
* M4: Production-ready with full telemetry and debug server support
|
|
1418
659
|
*/
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
660
|
+
async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter, _pauseGate) {
|
|
661
|
+
if (debug) {
|
|
662
|
+
logger.info("[StateMachine] Using state machine engine");
|
|
663
|
+
}
|
|
664
|
+
if (!config) {
|
|
665
|
+
const { ConfigManager: ConfigManager2 } = await import("./config-YNC2EOOT.mjs");
|
|
666
|
+
const configManager = new ConfigManager2();
|
|
667
|
+
config = await configManager.getDefaultConfig();
|
|
668
|
+
logger.debug("[StateMachine] Using default configuration (no config provided)");
|
|
669
|
+
}
|
|
670
|
+
const configWithTagFilter = tagFilter ? {
|
|
671
|
+
...config,
|
|
672
|
+
tag_filter: tagFilter
|
|
673
|
+
} : config;
|
|
674
|
+
const context = this.buildEngineContext(
|
|
675
|
+
configWithTagFilter,
|
|
676
|
+
prInfo,
|
|
677
|
+
debug,
|
|
678
|
+
maxParallelism,
|
|
679
|
+
failFast,
|
|
680
|
+
checks
|
|
681
|
+
// Pass the explicit checks list
|
|
682
|
+
);
|
|
683
|
+
const { initializeWorkspace: initializeWorkspace2 } = (init_build_engine_context(), __toCommonJS(build_engine_context_exports));
|
|
684
|
+
await initializeWorkspace2(context);
|
|
685
|
+
context.executionContext = this.getExecutionContext();
|
|
686
|
+
this._lastContext = context;
|
|
687
|
+
let frontendsHost;
|
|
688
|
+
if (Array.isArray(configWithTagFilter.frontends) && configWithTagFilter.frontends.length > 0) {
|
|
689
|
+
try {
|
|
690
|
+
const { EventBus } = await import("./event-bus-5BEVPQ6T.mjs");
|
|
691
|
+
const { FrontendsHost } = await import("./host-DXUYTNMU.mjs");
|
|
692
|
+
const bus = new EventBus();
|
|
693
|
+
context.eventBus = bus;
|
|
694
|
+
frontendsHost = new FrontendsHost(bus, logger);
|
|
695
|
+
if (process.env.VISOR_DEBUG === "true") {
|
|
696
|
+
try {
|
|
697
|
+
const fns = (configWithTagFilter.frontends || []).map((f) => ({
|
|
698
|
+
name: f?.name,
|
|
699
|
+
hasConfig: !!f?.config,
|
|
700
|
+
cfg: f?.config || void 0
|
|
701
|
+
}));
|
|
702
|
+
logger.info(`[Frontends] Loading specs: ${JSON.stringify(fns)}`);
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
await frontendsHost.load(configWithTagFilter.frontends);
|
|
707
|
+
let owner;
|
|
708
|
+
let name;
|
|
709
|
+
let prNum;
|
|
710
|
+
let headSha;
|
|
711
|
+
try {
|
|
712
|
+
const anyInfo = prInfo;
|
|
713
|
+
owner = anyInfo?.eventContext?.repository?.owner?.login || process.env.GITHUB_REPOSITORY?.split("/")?.[0];
|
|
714
|
+
name = anyInfo?.eventContext?.repository?.name || process.env.GITHUB_REPOSITORY?.split("/")?.[1];
|
|
715
|
+
prNum = typeof anyInfo?.number === "number" ? anyInfo.number : void 0;
|
|
716
|
+
headSha = anyInfo?.eventContext?.pull_request?.head?.sha || process.env.GITHUB_SHA;
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
const repoObj = owner && name ? { owner, name } : void 0;
|
|
720
|
+
const octokit = this.executionContext?.octokit;
|
|
721
|
+
if (!headSha && repoObj && prNum && octokit && typeof octokit.rest?.pulls?.get === "function") {
|
|
722
|
+
try {
|
|
723
|
+
const { data } = await octokit.rest.pulls.get({
|
|
724
|
+
owner: repoObj.owner,
|
|
725
|
+
repo: repoObj.name,
|
|
726
|
+
pull_number: prNum
|
|
727
|
+
});
|
|
728
|
+
headSha = data && data.head && data.head.sha || headSha;
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
}
|
|
1425
732
|
try {
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
733
|
+
const prev = this.getExecutionContext() || {};
|
|
734
|
+
this.setExecutionContext({ ...prev, eventBus: bus });
|
|
735
|
+
try {
|
|
736
|
+
context.executionContext = this.getExecutionContext();
|
|
737
|
+
} catch {
|
|
1429
738
|
}
|
|
1430
739
|
} catch {
|
|
1431
740
|
}
|
|
741
|
+
await frontendsHost.startAll(() => ({
|
|
742
|
+
eventBus: bus,
|
|
743
|
+
logger,
|
|
744
|
+
// Provide the active (possibly tag-filtered) config so frontends can read groups, etc.
|
|
745
|
+
config: configWithTagFilter,
|
|
746
|
+
run: {
|
|
747
|
+
runId: context.sessionId,
|
|
748
|
+
repo: repoObj,
|
|
749
|
+
pr: prNum,
|
|
750
|
+
headSha,
|
|
751
|
+
event: context.event || prInfo?.eventType,
|
|
752
|
+
actor: prInfo?.eventContext?.sender?.login || (typeof process.env.GITHUB_ACTOR === "string" ? process.env.GITHUB_ACTOR : void 0)
|
|
753
|
+
},
|
|
754
|
+
octokit,
|
|
755
|
+
webhookContext: this.executionContext?.webhookContext,
|
|
756
|
+
// Surface any injected test doubles for Slack as well
|
|
757
|
+
slack: this.executionContext?.slack || this.executionContext?.slackClient
|
|
758
|
+
}));
|
|
759
|
+
try {
|
|
760
|
+
bus.on("HumanInputRequested", async (envelope) => {
|
|
761
|
+
try {
|
|
762
|
+
const ev = envelope && envelope.payload || envelope;
|
|
763
|
+
let channel = ev?.channel;
|
|
764
|
+
let threadTs = ev?.threadTs;
|
|
765
|
+
if (!channel || !threadTs) {
|
|
766
|
+
try {
|
|
767
|
+
const anyCfg = configWithTagFilter || {};
|
|
768
|
+
const slackCfg = anyCfg.slack || {};
|
|
769
|
+
const endpoint = slackCfg.endpoint || "/bots/slack/support";
|
|
770
|
+
const map = this.executionContext?.webhookContext?.webhookData;
|
|
771
|
+
const payload = map?.get(endpoint);
|
|
772
|
+
const e = payload?.event;
|
|
773
|
+
const derivedTs = String(e?.thread_ts || e?.ts || e?.event_ts || "");
|
|
774
|
+
const derivedCh = String(e?.channel || "");
|
|
775
|
+
if (derivedCh && derivedTs) {
|
|
776
|
+
channel = channel || derivedCh;
|
|
777
|
+
threadTs = threadTs || derivedTs;
|
|
778
|
+
}
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const checkId = String(ev?.checkId || "unknown");
|
|
783
|
+
const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
|
|
784
|
+
const baseDir = process.env.VISOR_SNAPSHOT_DIR || path2.resolve(process.cwd(), ".visor", "snapshots");
|
|
785
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
786
|
+
const filePath = path2.join(baseDir, `${threadKey}-${checkId}.json`);
|
|
787
|
+
await this.saveSnapshotToFile(filePath);
|
|
788
|
+
logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
|
|
789
|
+
try {
|
|
790
|
+
await bus.emit({
|
|
791
|
+
type: "SnapshotSaved",
|
|
792
|
+
checkId: ev?.checkId || "unknown",
|
|
793
|
+
channel,
|
|
794
|
+
threadTs,
|
|
795
|
+
threadKey,
|
|
796
|
+
filePath
|
|
797
|
+
});
|
|
798
|
+
} catch {
|
|
799
|
+
}
|
|
800
|
+
} catch (e) {
|
|
801
|
+
logger.warn(
|
|
802
|
+
`[Snapshot] Failed to save snapshot on HumanInputRequested: ${e instanceof Error ? e.message : String(e)}`
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
} catch (err) {
|
|
809
|
+
logger.warn(
|
|
810
|
+
`[Frontends] Failed to initialize frontends: ${err instanceof Error ? err.message : String(err)}`
|
|
811
|
+
);
|
|
1432
812
|
}
|
|
1433
|
-
currentDir = path.dirname(currentDir);
|
|
1434
813
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
814
|
+
const runner = new StateMachineRunner(context, this.debugServer);
|
|
815
|
+
this._lastRunner = runner;
|
|
816
|
+
const result = await runner.run();
|
|
817
|
+
if (frontendsHost && typeof frontendsHost.stopAll === "function") {
|
|
818
|
+
try {
|
|
819
|
+
await frontendsHost.stopAll();
|
|
820
|
+
} catch {
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (debug) {
|
|
824
|
+
logger.info("[StateMachine] Execution complete");
|
|
825
|
+
}
|
|
826
|
+
try {
|
|
827
|
+
const { SessionRegistry } = await import("./session-registry-4E6YRQ77.mjs");
|
|
828
|
+
const sessionRegistry = SessionRegistry.getInstance();
|
|
829
|
+
sessionRegistry.clearAllSessions();
|
|
830
|
+
} catch (error) {
|
|
831
|
+
logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);
|
|
832
|
+
}
|
|
833
|
+
if (context.workspace) {
|
|
834
|
+
try {
|
|
835
|
+
await context.workspace.cleanup();
|
|
836
|
+
} catch (error) {
|
|
837
|
+
logger.debug(`[StateMachine] Failed to cleanup workspace: ${error}`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return result;
|
|
1449
841
|
}
|
|
1450
842
|
/**
|
|
1451
|
-
*
|
|
1452
|
-
* Ensures both keys are present and contain the same data
|
|
843
|
+
* Build the engine context for state machine execution
|
|
1453
844
|
*/
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
config
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
845
|
+
buildEngineContext(config, prInfo, debug, maxParallelism, failFast, requestedChecks) {
|
|
846
|
+
const { buildEngineContextForRun: buildEngineContextForRun2 } = (init_build_engine_context(), __toCommonJS(build_engine_context_exports));
|
|
847
|
+
return buildEngineContextForRun2(
|
|
848
|
+
this.workingDirectory,
|
|
849
|
+
config,
|
|
850
|
+
prInfo,
|
|
851
|
+
debug,
|
|
852
|
+
maxParallelism,
|
|
853
|
+
failFast,
|
|
854
|
+
requestedChecks
|
|
855
|
+
);
|
|
1463
856
|
}
|
|
1464
|
-
};
|
|
1465
|
-
|
|
1466
|
-
// src/config.ts
|
|
1467
|
-
import Ajv from "ajv";
|
|
1468
|
-
import addFormats from "ajv-formats";
|
|
1469
|
-
var VALID_EVENT_TRIGGERS = [
|
|
1470
|
-
"pr_opened",
|
|
1471
|
-
"pr_updated",
|
|
1472
|
-
"pr_closed",
|
|
1473
|
-
"issue_opened",
|
|
1474
|
-
"issue_comment",
|
|
1475
|
-
"manual",
|
|
1476
|
-
"schedule",
|
|
1477
|
-
"webhook_received"
|
|
1478
|
-
];
|
|
1479
|
-
var ConfigManager = class {
|
|
1480
|
-
validCheckTypes = [
|
|
1481
|
-
"ai",
|
|
1482
|
-
"claude-code",
|
|
1483
|
-
"mcp",
|
|
1484
|
-
"command",
|
|
1485
|
-
"http",
|
|
1486
|
-
"http_input",
|
|
1487
|
-
"http_client",
|
|
1488
|
-
"memory",
|
|
1489
|
-
"noop",
|
|
1490
|
-
"log",
|
|
1491
|
-
"memory",
|
|
1492
|
-
"github",
|
|
1493
|
-
"human-input"
|
|
1494
|
-
];
|
|
1495
|
-
validEventTriggers = [...VALID_EVENT_TRIGGERS];
|
|
1496
|
-
validOutputFormats = ["table", "json", "markdown", "sarif"];
|
|
1497
|
-
validGroupByOptions = ["check", "file", "severity", "group"];
|
|
1498
857
|
/**
|
|
1499
|
-
*
|
|
858
|
+
* Get output history snapshot for test framework compatibility
|
|
859
|
+
* Extracts output history from the journal
|
|
1500
860
|
*/
|
|
1501
|
-
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
861
|
+
getOutputHistorySnapshot() {
|
|
862
|
+
const journal = this._lastContext?.journal;
|
|
863
|
+
if (!journal) {
|
|
864
|
+
logger.debug("[StateMachine][DEBUG] getOutputHistorySnapshot: No journal found");
|
|
865
|
+
return {};
|
|
866
|
+
}
|
|
867
|
+
const sessionId = this._lastContext?.sessionId;
|
|
868
|
+
if (!sessionId) {
|
|
869
|
+
logger.debug("[StateMachine][DEBUG] getOutputHistorySnapshot: No sessionId found");
|
|
870
|
+
return {};
|
|
871
|
+
}
|
|
872
|
+
const snapshot = journal.beginSnapshot();
|
|
873
|
+
const allEntries = journal.readVisible(sessionId, snapshot, void 0);
|
|
874
|
+
logger.debug(
|
|
875
|
+
`[StateMachine][DEBUG] getOutputHistorySnapshot: Found ${allEntries.length} journal entries`
|
|
876
|
+
);
|
|
877
|
+
const outputHistory = {};
|
|
878
|
+
for (const entry of allEntries) {
|
|
879
|
+
const checkId = entry.checkId;
|
|
880
|
+
if (!outputHistory[checkId]) {
|
|
881
|
+
outputHistory[checkId] = [];
|
|
1507
882
|
}
|
|
1508
|
-
const configContent = fs2.readFileSync(resolvedPath, "utf8");
|
|
1509
|
-
let parsedConfig;
|
|
1510
883
|
try {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
const errorMessage = yamlError instanceof Error ? yamlError.message : String(yamlError);
|
|
1514
|
-
throw new Error(`Invalid YAML syntax in ${resolvedPath}: ${errorMessage}`);
|
|
1515
|
-
}
|
|
1516
|
-
if (!parsedConfig || typeof parsedConfig !== "object") {
|
|
1517
|
-
throw new Error("Configuration file must contain a valid YAML object");
|
|
1518
|
-
}
|
|
1519
|
-
if (parsedConfig.extends) {
|
|
1520
|
-
const loaderOptions = {
|
|
1521
|
-
baseDir: path2.dirname(resolvedPath),
|
|
1522
|
-
allowRemote: this.isRemoteExtendsAllowed(),
|
|
1523
|
-
maxDepth: 10,
|
|
1524
|
-
allowedRemotePatterns
|
|
1525
|
-
};
|
|
1526
|
-
const loader = new ConfigLoader(loaderOptions);
|
|
1527
|
-
const merger = new ConfigMerger();
|
|
1528
|
-
const extends_ = Array.isArray(parsedConfig.extends) ? parsedConfig.extends : [parsedConfig.extends];
|
|
1529
|
-
const { extends: _extendsField, ...configWithoutExtends } = parsedConfig;
|
|
1530
|
-
let mergedConfig = {};
|
|
1531
|
-
for (const source of extends_) {
|
|
1532
|
-
console.log(`\u{1F4E6} Extending from: ${source}`);
|
|
1533
|
-
const parentConfig = await loader.fetchConfig(source);
|
|
1534
|
-
mergedConfig = merger.merge(mergedConfig, parentConfig);
|
|
1535
|
-
}
|
|
1536
|
-
parsedConfig = merger.merge(mergedConfig, configWithoutExtends);
|
|
1537
|
-
parsedConfig = merger.removeDisabledChecks(parsedConfig);
|
|
1538
|
-
}
|
|
1539
|
-
parsedConfig = this.normalizeStepsAndChecks(parsedConfig);
|
|
1540
|
-
if (validate) {
|
|
1541
|
-
this.validateConfig(parsedConfig);
|
|
1542
|
-
}
|
|
1543
|
-
let finalConfig = parsedConfig;
|
|
1544
|
-
if (mergeDefaults) {
|
|
1545
|
-
finalConfig = this.mergeWithDefaults(parsedConfig);
|
|
1546
|
-
}
|
|
1547
|
-
return finalConfig;
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
if (error instanceof Error) {
|
|
1550
|
-
if (error.message.includes("not found") || error.message.includes("Invalid YAML") || error.message.includes("extends") || error.message.includes("EACCES") || error.message.includes("EISDIR")) {
|
|
1551
|
-
throw error;
|
|
884
|
+
if (entry && typeof entry.result === "object" && entry.result.__skipped) {
|
|
885
|
+
continue;
|
|
1552
886
|
}
|
|
1553
|
-
|
|
1554
|
-
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
1555
|
-
}
|
|
1556
|
-
if (error.message.includes("EPERM")) {
|
|
1557
|
-
throw new Error(`Permission denied reading configuration file: ${resolvedPath}`);
|
|
1558
|
-
}
|
|
1559
|
-
throw new Error(`Failed to read configuration file ${resolvedPath}: ${error.message}`);
|
|
887
|
+
} catch {
|
|
1560
888
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
* Find and load configuration from default locations
|
|
1566
|
-
*/
|
|
1567
|
-
async findAndLoadConfig(options = {}) {
|
|
1568
|
-
const gitRoot = await this.findGitRepositoryRoot();
|
|
1569
|
-
const searchDirs = [gitRoot, process.cwd()].filter(Boolean);
|
|
1570
|
-
for (const baseDir of searchDirs) {
|
|
1571
|
-
const possiblePaths = [path2.join(baseDir, ".visor.yaml"), path2.join(baseDir, ".visor.yml")];
|
|
1572
|
-
for (const configPath of possiblePaths) {
|
|
1573
|
-
if (fs2.existsSync(configPath)) {
|
|
1574
|
-
return this.loadConfig(configPath, options);
|
|
889
|
+
const payload = entry.result.output !== void 0 ? entry.result.output : entry.result;
|
|
890
|
+
try {
|
|
891
|
+
if (payload && typeof payload === "object" && payload.forEachItems && Array.isArray(payload.forEachItems)) {
|
|
892
|
+
continue;
|
|
1575
893
|
}
|
|
894
|
+
} catch {
|
|
1576
895
|
}
|
|
896
|
+
if (payload !== void 0) outputHistory[checkId].push(payload);
|
|
1577
897
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
898
|
+
logger.debug(
|
|
899
|
+
`[StateMachine][DEBUG] getOutputHistorySnapshot result: ${JSON.stringify(Object.keys(outputHistory))}`
|
|
900
|
+
);
|
|
901
|
+
for (const [checkId, outputs] of Object.entries(outputHistory)) {
|
|
902
|
+
logger.debug(`[StateMachine][DEBUG] ${checkId}: ${outputs.length} outputs`);
|
|
1581
903
|
}
|
|
1582
|
-
return
|
|
904
|
+
return outputHistory;
|
|
1583
905
|
}
|
|
1584
906
|
/**
|
|
1585
|
-
*
|
|
907
|
+
* Save a JSON snapshot of the last run's state and journal to a file (experimental).
|
|
908
|
+
* Does not include secrets. Intended for debugging and future resume support.
|
|
1586
909
|
*/
|
|
1587
|
-
async
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
910
|
+
async saveSnapshotToFile(filePath) {
|
|
911
|
+
const fs2 = await import("fs/promises");
|
|
912
|
+
const ctx = this._lastContext;
|
|
913
|
+
const runner = this._lastRunner;
|
|
914
|
+
if (!ctx || !runner) {
|
|
915
|
+
throw new Error("No prior execution context to snapshot");
|
|
916
|
+
}
|
|
917
|
+
const journal = ctx.journal;
|
|
918
|
+
const snapshotId = journal.beginSnapshot();
|
|
919
|
+
const entries = journal.readVisible(ctx.sessionId, snapshotId, void 0);
|
|
920
|
+
const state = runner.getState();
|
|
921
|
+
const serializableState = serializeRunState(state);
|
|
922
|
+
const payload = {
|
|
923
|
+
version: 1,
|
|
924
|
+
sessionId: ctx.sessionId,
|
|
925
|
+
event: ctx.event,
|
|
926
|
+
wave: state.wave,
|
|
927
|
+
state: serializableState,
|
|
928
|
+
journal: entries,
|
|
929
|
+
requestedChecks: ctx.requestedChecks || []
|
|
930
|
+
};
|
|
931
|
+
await fs2.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
1599
932
|
}
|
|
1600
933
|
/**
|
|
1601
|
-
*
|
|
934
|
+
* Load a snapshot JSON from file and return it. Resume support can build on this.
|
|
1602
935
|
*/
|
|
1603
|
-
async
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
checks: {},
|
|
1608
|
-
// Keep for backward compatibility
|
|
1609
|
-
max_parallelism: 3,
|
|
1610
|
-
output: {
|
|
1611
|
-
pr_comment: {
|
|
1612
|
-
format: "markdown",
|
|
1613
|
-
group_by: "check",
|
|
1614
|
-
collapse: true
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
};
|
|
936
|
+
async loadSnapshotFromFile(filePath) {
|
|
937
|
+
const fs2 = await import("fs/promises");
|
|
938
|
+
const raw = await fs2.readFile(filePath, "utf8");
|
|
939
|
+
return JSON.parse(raw);
|
|
1618
940
|
}
|
|
1619
941
|
/**
|
|
1620
|
-
*
|
|
942
|
+
* Filter checks by tag filter
|
|
1621
943
|
*/
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
const
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
1627
|
-
path2.join(__dirname, "defaults", ".visor.yaml"),
|
|
1628
|
-
path2.join(__dirname, "..", "defaults", ".visor.yaml")
|
|
1629
|
-
);
|
|
944
|
+
filterChecksByTags(checks, config, tagFilter) {
|
|
945
|
+
return checks.filter((checkName) => {
|
|
946
|
+
const checkConfig = config?.checks?.[checkName];
|
|
947
|
+
if (!checkConfig) {
|
|
948
|
+
return true;
|
|
1630
949
|
}
|
|
1631
|
-
const
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
950
|
+
const checkTags = checkConfig.tags || [];
|
|
951
|
+
if (!tagFilter || !tagFilter.include && !tagFilter.exclude) {
|
|
952
|
+
return checkTags.length === 0;
|
|
1634
953
|
}
|
|
1635
|
-
if (
|
|
1636
|
-
|
|
1637
|
-
path2.join(process.env.GITHUB_ACTION_PATH, "defaults", ".visor.yaml"),
|
|
1638
|
-
path2.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", ".visor.yaml")
|
|
1639
|
-
);
|
|
954
|
+
if (checkTags.length === 0) {
|
|
955
|
+
return true;
|
|
1640
956
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
if (
|
|
1644
|
-
bundledConfigPath = possiblePath;
|
|
1645
|
-
break;
|
|
1646
|
-
}
|
|
957
|
+
if (tagFilter.exclude && tagFilter.exclude.length > 0) {
|
|
958
|
+
const hasExcludedTag = tagFilter.exclude.some((tag) => checkTags.includes(tag));
|
|
959
|
+
if (hasExcludedTag) return false;
|
|
1647
960
|
}
|
|
1648
|
-
if (
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
let parsedConfig = yaml2.load(configContent);
|
|
1652
|
-
if (!parsedConfig || typeof parsedConfig !== "object") {
|
|
1653
|
-
return null;
|
|
1654
|
-
}
|
|
1655
|
-
parsedConfig = this.normalizeStepsAndChecks(parsedConfig);
|
|
1656
|
-
this.validateConfig(parsedConfig);
|
|
1657
|
-
return this.mergeWithDefaults(parsedConfig);
|
|
961
|
+
if (tagFilter.include && tagFilter.include.length > 0) {
|
|
962
|
+
const hasIncludedTag = tagFilter.include.some((tag) => checkTags.includes(tag));
|
|
963
|
+
if (!hasIncludedTag) return false;
|
|
1658
964
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
"Failed to load bundled default config:",
|
|
1662
|
-
error instanceof Error ? error.message : String(error)
|
|
1663
|
-
);
|
|
1664
|
-
}
|
|
1665
|
-
return null;
|
|
965
|
+
return true;
|
|
966
|
+
});
|
|
1666
967
|
}
|
|
1667
968
|
/**
|
|
1668
|
-
*
|
|
969
|
+
* Create an error result in AnalysisResult format
|
|
1669
970
|
*/
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
971
|
+
createErrorResult(repositoryInfo, errorMessage, startTime, timestamp, checksExecuted) {
|
|
972
|
+
const executionTime = Date.now() - startTime;
|
|
973
|
+
return {
|
|
974
|
+
repositoryInfo,
|
|
975
|
+
reviewSummary: {
|
|
976
|
+
issues: [
|
|
977
|
+
{
|
|
978
|
+
file: "system",
|
|
979
|
+
line: 0,
|
|
980
|
+
endLine: void 0,
|
|
981
|
+
ruleId: "system/error",
|
|
982
|
+
message: errorMessage,
|
|
983
|
+
severity: "error",
|
|
984
|
+
category: "logic",
|
|
985
|
+
suggestion: void 0,
|
|
986
|
+
replacement: void 0
|
|
1679
987
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
}
|
|
1687
|
-
/**
|
|
1688
|
-
* Normalize 'checks' and 'steps' keys for backward compatibility
|
|
1689
|
-
* Ensures both keys are present and contain the same data
|
|
1690
|
-
*/
|
|
1691
|
-
normalizeStepsAndChecks(config) {
|
|
1692
|
-
if (config.steps && config.checks) {
|
|
1693
|
-
config.checks = config.steps;
|
|
1694
|
-
} else if (config.steps && !config.checks) {
|
|
1695
|
-
config.checks = config.steps;
|
|
1696
|
-
} else if (config.checks && !config.steps) {
|
|
1697
|
-
config.steps = config.checks;
|
|
1698
|
-
}
|
|
1699
|
-
return config;
|
|
988
|
+
]
|
|
989
|
+
},
|
|
990
|
+
executionTime,
|
|
991
|
+
timestamp,
|
|
992
|
+
checksExecuted
|
|
993
|
+
};
|
|
1700
994
|
}
|
|
1701
995
|
/**
|
|
1702
|
-
*
|
|
996
|
+
* Convert GroupedCheckResults to ReviewSummary
|
|
997
|
+
* Aggregates all check results into a single ReviewSummary
|
|
1703
998
|
*/
|
|
1704
|
-
|
|
1705
|
-
const
|
|
1706
|
-
|
|
1707
|
-
mergedConfig.max_parallelism = cliOptions.maxParallelism;
|
|
1708
|
-
}
|
|
1709
|
-
if (cliOptions.failFast !== void 0) {
|
|
1710
|
-
mergedConfig.fail_fast = cliOptions.failFast;
|
|
1711
|
-
}
|
|
1712
|
-
return {
|
|
1713
|
-
config: mergedConfig,
|
|
1714
|
-
cliChecks: cliOptions.checks || [],
|
|
1715
|
-
cliOutput: cliOptions.output || "table"
|
|
1716
|
-
};
|
|
999
|
+
convertGroupedResultsToReviewSummary(groupedResults, statistics) {
|
|
1000
|
+
const { convertToReviewSummary: convertToReviewSummary2 } = (init_summary(), __toCommonJS(summary_exports));
|
|
1001
|
+
return convertToReviewSummary2(groupedResults, statistics);
|
|
1717
1002
|
}
|
|
1718
1003
|
/**
|
|
1719
|
-
*
|
|
1004
|
+
* Evaluate failure conditions for a check result
|
|
1005
|
+
*
|
|
1006
|
+
* This method provides backward compatibility with the legacy engine by
|
|
1007
|
+
* delegating to the FailureConditionEvaluator.
|
|
1008
|
+
*
|
|
1009
|
+
* @param checkName - The name of the check being evaluated
|
|
1010
|
+
* @param reviewSummary - The review summary containing check results
|
|
1011
|
+
* @param config - The Visor configuration containing failure conditions
|
|
1012
|
+
* @param previousOutputs - Optional previous check outputs for cross-check conditions
|
|
1013
|
+
* @param authorAssociation - Optional GitHub author association for permission checks
|
|
1014
|
+
* @returns Array of failure condition evaluation results
|
|
1720
1015
|
*/
|
|
1721
|
-
async
|
|
1722
|
-
const
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1016
|
+
async evaluateFailureConditions(checkName, reviewSummary, config, previousOutputs, authorAssociation) {
|
|
1017
|
+
const { FailureConditionEvaluator } = await import("./failure-condition-evaluator-YGTF2GHG.mjs");
|
|
1018
|
+
const evaluator = new FailureConditionEvaluator();
|
|
1019
|
+
const { addEvent } = await import("./trace-helpers-VP6QYVBX.mjs");
|
|
1020
|
+
const { addFailIfTriggered } = await import("./metrics-7PP3EJUH.mjs");
|
|
1021
|
+
const checkConfig = config.checks?.[checkName];
|
|
1022
|
+
if (!checkConfig) {
|
|
1023
|
+
return [];
|
|
1024
|
+
}
|
|
1025
|
+
const rawSchema = checkConfig.schema || "code-review";
|
|
1026
|
+
const checkSchema = typeof rawSchema === "string" ? rawSchema : "code-review";
|
|
1027
|
+
const checkGroup = checkConfig.group || "default";
|
|
1028
|
+
const results = [];
|
|
1029
|
+
if (config.fail_if) {
|
|
1030
|
+
const failed = await evaluator.evaluateSimpleCondition(
|
|
1031
|
+
checkName,
|
|
1032
|
+
checkSchema,
|
|
1033
|
+
checkGroup,
|
|
1034
|
+
reviewSummary,
|
|
1035
|
+
config.fail_if,
|
|
1036
|
+
previousOutputs || {}
|
|
1037
|
+
);
|
|
1731
1038
|
try {
|
|
1732
|
-
|
|
1039
|
+
addEvent("fail_if.evaluated", {
|
|
1040
|
+
"visor.check.id": checkName,
|
|
1041
|
+
scope: "global",
|
|
1042
|
+
expression: String(config.fail_if),
|
|
1043
|
+
result: failed ? "triggered" : "not_triggered"
|
|
1044
|
+
});
|
|
1045
|
+
if (failed) {
|
|
1046
|
+
addEvent("fail_if.triggered", {
|
|
1047
|
+
"visor.check.id": checkName,
|
|
1048
|
+
scope: "global",
|
|
1049
|
+
expression: String(config.fail_if)
|
|
1050
|
+
});
|
|
1051
|
+
addFailIfTriggered(checkName, "global");
|
|
1052
|
+
}
|
|
1733
1053
|
} catch {
|
|
1734
|
-
config = await this.findAndLoadConfig();
|
|
1735
1054
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
* @param config The config to validate
|
|
1744
|
-
* @param strict If true, treat warnings as errors (default: false)
|
|
1745
|
-
*/
|
|
1746
|
-
validateConfig(config, strict = false) {
|
|
1747
|
-
const errors = [];
|
|
1748
|
-
const warnings = [];
|
|
1749
|
-
this.validateWithAjvSchema(config, errors, warnings);
|
|
1750
|
-
if (!config.version) {
|
|
1751
|
-
errors.push({
|
|
1752
|
-
field: "version",
|
|
1753
|
-
message: "Missing required field: version"
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
if (!config.checks && !config.steps) {
|
|
1757
|
-
errors.push({
|
|
1758
|
-
field: "checks/steps",
|
|
1759
|
-
message: 'Missing required field: either "checks" or "steps" must be defined. "steps" is recommended for new configurations.'
|
|
1055
|
+
results.push({
|
|
1056
|
+
conditionName: "global_fail_if",
|
|
1057
|
+
failed,
|
|
1058
|
+
expression: config.fail_if,
|
|
1059
|
+
message: failed ? `Global failure condition met: ${config.fail_if}` : void 0,
|
|
1060
|
+
severity: "error",
|
|
1061
|
+
haltExecution: false
|
|
1760
1062
|
});
|
|
1761
1063
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
)
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
);
|
|
1784
|
-
}
|
|
1785
|
-
if (checkConfig.ai_mcp_servers && checkConfig.ai?.mcpServers) {
|
|
1786
|
-
const lower = Object.keys(checkConfig.ai_mcp_servers);
|
|
1787
|
-
const higher = Object.keys(checkConfig.ai.mcpServers);
|
|
1788
|
-
const overridden = lower.filter((k) => higher.includes(k));
|
|
1789
|
-
warnings.push({
|
|
1790
|
-
field: `checks.${checkName}.ai.mcpServers`,
|
|
1791
|
-
message: overridden.length > 0 ? `Both ai_mcp_servers and ai.mcpServers are set; ai.mcpServers overrides these servers: ${overridden.join(
|
|
1792
|
-
", "
|
|
1793
|
-
)}` : "Both ai_mcp_servers and ai.mcpServers are set; ai.mcpServers takes precedence for this check."
|
|
1064
|
+
if (checkConfig.fail_if) {
|
|
1065
|
+
const failed = await evaluator.evaluateSimpleCondition(
|
|
1066
|
+
checkName,
|
|
1067
|
+
checkSchema,
|
|
1068
|
+
checkGroup,
|
|
1069
|
+
reviewSummary,
|
|
1070
|
+
checkConfig.fail_if,
|
|
1071
|
+
previousOutputs || {}
|
|
1072
|
+
);
|
|
1073
|
+
try {
|
|
1074
|
+
addEvent("fail_if.evaluated", {
|
|
1075
|
+
"visor.check.id": checkName,
|
|
1076
|
+
scope: "check",
|
|
1077
|
+
expression: String(checkConfig.fail_if),
|
|
1078
|
+
result: failed ? "triggered" : "not_triggered"
|
|
1079
|
+
});
|
|
1080
|
+
if (failed) {
|
|
1081
|
+
addEvent("fail_if.triggered", {
|
|
1082
|
+
"visor.check.id": checkName,
|
|
1083
|
+
scope: "check",
|
|
1084
|
+
expression: String(checkConfig.fail_if)
|
|
1794
1085
|
});
|
|
1086
|
+
addFailIfTriggered(checkName, "check");
|
|
1795
1087
|
}
|
|
1796
|
-
|
|
1797
|
-
const anyCheck = checkConfig;
|
|
1798
|
-
const aiObj = anyCheck.ai || void 0;
|
|
1799
|
-
const hasBareMcpAtCheck = Object.prototype.hasOwnProperty.call(anyCheck, "mcpServers");
|
|
1800
|
-
const hasAiMcp = aiObj && Object.prototype.hasOwnProperty.call(aiObj, "mcpServers");
|
|
1801
|
-
const hasClaudeCodeMcp = anyCheck.claude_code && typeof anyCheck.claude_code === "object" && Object.prototype.hasOwnProperty.call(
|
|
1802
|
-
anyCheck.claude_code,
|
|
1803
|
-
"mcpServers"
|
|
1804
|
-
);
|
|
1805
|
-
if (checkConfig.type === "ai") {
|
|
1806
|
-
if (hasBareMcpAtCheck) {
|
|
1807
|
-
warnings.push({
|
|
1808
|
-
field: `checks.${checkName}.mcpServers`,
|
|
1809
|
-
message: "'mcpServers' at the check root is ignored for type 'ai'. Use 'ai.mcpServers' or 'ai_mcp_servers' instead.",
|
|
1810
|
-
value: anyCheck.mcpServers
|
|
1811
|
-
});
|
|
1812
|
-
}
|
|
1813
|
-
if (hasClaudeCodeMcp) {
|
|
1814
|
-
warnings.push({
|
|
1815
|
-
field: `checks.${checkName}.claude_code.mcpServers`,
|
|
1816
|
-
message: "'claude_code.mcpServers' is ignored for type 'ai'. Use 'ai.mcpServers' or 'ai_mcp_servers' instead."
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
if (checkConfig.type === "claude-code") {
|
|
1821
|
-
if (hasAiMcp || checkConfig.ai_mcp_servers) {
|
|
1822
|
-
warnings.push({
|
|
1823
|
-
field: hasAiMcp ? `checks.${checkName}.ai.mcpServers` : `checks.${checkName}.ai_mcp_servers`,
|
|
1824
|
-
message: "For type 'claude-code', MCP must be configured under 'claude_code.mcpServers'. 'ai.mcpServers' and 'ai_mcp_servers' are ignored for this check."
|
|
1825
|
-
});
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
} catch {
|
|
1829
|
-
}
|
|
1088
|
+
} catch {
|
|
1830
1089
|
}
|
|
1090
|
+
results.push({
|
|
1091
|
+
conditionName: `${checkName}_fail_if`,
|
|
1092
|
+
failed,
|
|
1093
|
+
expression: checkConfig.fail_if,
|
|
1094
|
+
message: failed ? `Check failure condition met: ${checkConfig.fail_if}` : void 0,
|
|
1095
|
+
severity: "error",
|
|
1096
|
+
haltExecution: false
|
|
1097
|
+
});
|
|
1831
1098
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1099
|
+
const globalConditions = config.failure_conditions;
|
|
1100
|
+
const checkConditions = checkConfig.failure_conditions;
|
|
1101
|
+
if (globalConditions || checkConditions) {
|
|
1102
|
+
const legacyResults = await evaluator.evaluateConditions(
|
|
1103
|
+
checkName,
|
|
1104
|
+
checkSchema,
|
|
1105
|
+
checkGroup,
|
|
1106
|
+
reviewSummary,
|
|
1107
|
+
globalConditions,
|
|
1108
|
+
checkConditions,
|
|
1109
|
+
previousOutputs,
|
|
1110
|
+
authorAssociation
|
|
1842
1111
|
);
|
|
1112
|
+
results.push(...legacyResults);
|
|
1843
1113
|
}
|
|
1844
|
-
|
|
1845
|
-
if (typeof config.max_parallelism !== "number" || config.max_parallelism < 1 || !Number.isInteger(config.max_parallelism)) {
|
|
1846
|
-
errors.push({
|
|
1847
|
-
field: "max_parallelism",
|
|
1848
|
-
message: "max_parallelism must be a positive integer (minimum 1)",
|
|
1849
|
-
value: config.max_parallelism
|
|
1850
|
-
});
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
if (config.tag_filter) {
|
|
1854
|
-
this.validateTagFilter(config.tag_filter, errors);
|
|
1855
|
-
}
|
|
1856
|
-
if (strict && warnings.length > 0) {
|
|
1857
|
-
errors.push(...warnings);
|
|
1858
|
-
}
|
|
1859
|
-
if (errors.length > 0) {
|
|
1860
|
-
throw new Error(errors[0].message);
|
|
1861
|
-
}
|
|
1862
|
-
if (!strict && warnings.length > 0) {
|
|
1863
|
-
for (const w of warnings) {
|
|
1864
|
-
logger.warn(`\u26A0\uFE0F Config warning [${w.field}]: ${w.message}`);
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1114
|
+
return results;
|
|
1867
1115
|
}
|
|
1868
1116
|
/**
|
|
1869
|
-
*
|
|
1117
|
+
* Get repository status
|
|
1118
|
+
* @returns Repository status information
|
|
1870
1119
|
*/
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
message: `Invalid check configuration for "${checkName}": missing prompt (required for AI checks)`
|
|
1889
|
-
});
|
|
1890
|
-
}
|
|
1891
|
-
if (checkConfig.type === "command" && !checkConfig.exec) {
|
|
1892
|
-
errors.push({
|
|
1893
|
-
field: `checks.${checkName}.exec`,
|
|
1894
|
-
message: `Invalid check configuration for "${checkName}": missing exec field (required for command checks)`
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1897
|
-
if (checkConfig.type === "http") {
|
|
1898
|
-
if (!checkConfig.url) {
|
|
1899
|
-
errors.push({
|
|
1900
|
-
field: `checks.${checkName}.url`,
|
|
1901
|
-
message: `Invalid check configuration for "${checkName}": missing url field (required for http checks)`
|
|
1902
|
-
});
|
|
1903
|
-
}
|
|
1904
|
-
if (!checkConfig.body) {
|
|
1905
|
-
errors.push({
|
|
1906
|
-
field: `checks.${checkName}.body`,
|
|
1907
|
-
message: `Invalid check configuration for "${checkName}": missing body field (required for http checks)`
|
|
1908
|
-
});
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
if (checkConfig.type === "http_input" && !checkConfig.endpoint) {
|
|
1912
|
-
errors.push({
|
|
1913
|
-
field: `checks.${checkName}.endpoint`,
|
|
1914
|
-
message: `Invalid check configuration for "${checkName}": missing endpoint field (required for http_input checks)`
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
|
-
if (checkConfig.type === "http_client" && !checkConfig.url) {
|
|
1918
|
-
errors.push({
|
|
1919
|
-
field: `checks.${checkName}.url`,
|
|
1920
|
-
message: `Invalid check configuration for "${checkName}": missing url field (required for http_client checks)`
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
if (checkConfig.schedule) {
|
|
1924
|
-
const cronParts = checkConfig.schedule.split(" ");
|
|
1925
|
-
if (cronParts.length < 5 || cronParts.length > 6) {
|
|
1926
|
-
errors.push({
|
|
1927
|
-
field: `checks.${checkName}.schedule`,
|
|
1928
|
-
message: `Invalid cron expression for "${checkName}": ${checkConfig.schedule}`,
|
|
1929
|
-
value: checkConfig.schedule
|
|
1930
|
-
});
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
if (checkConfig.on) {
|
|
1934
|
-
if (!Array.isArray(checkConfig.on)) {
|
|
1935
|
-
errors.push({
|
|
1936
|
-
field: `checks.${checkName}.on`,
|
|
1937
|
-
message: `Invalid check configuration for "${checkName}": 'on' field must be an array`
|
|
1938
|
-
});
|
|
1939
|
-
} else {
|
|
1940
|
-
for (const event of checkConfig.on) {
|
|
1941
|
-
if (!this.validEventTriggers.includes(event)) {
|
|
1942
|
-
errors.push({
|
|
1943
|
-
field: `checks.${checkName}.on`,
|
|
1944
|
-
message: `Invalid event "${event}". Must be one of: ${this.validEventTriggers.join(", ")}`,
|
|
1945
|
-
value: event
|
|
1946
|
-
});
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
if (checkConfig.reuse_ai_session !== void 0) {
|
|
1952
|
-
const isString = typeof checkConfig.reuse_ai_session === "string";
|
|
1953
|
-
const isBoolean = typeof checkConfig.reuse_ai_session === "boolean";
|
|
1954
|
-
if (!isString && !isBoolean) {
|
|
1955
|
-
errors.push({
|
|
1956
|
-
field: `checks.${checkName}.reuse_ai_session`,
|
|
1957
|
-
message: `Invalid reuse_ai_session value for "${checkName}": must be string (check name) or boolean`,
|
|
1958
|
-
value: checkConfig.reuse_ai_session
|
|
1959
|
-
});
|
|
1960
|
-
} else if (isString) {
|
|
1961
|
-
const targetCheckName = checkConfig.reuse_ai_session;
|
|
1962
|
-
if (!config?.checks || !config.checks[targetCheckName]) {
|
|
1963
|
-
errors.push({
|
|
1964
|
-
field: `checks.${checkName}.reuse_ai_session`,
|
|
1965
|
-
message: `Check "${checkName}" references non-existent check "${targetCheckName}" for session reuse`,
|
|
1966
|
-
value: checkConfig.reuse_ai_session
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
|
-
} else if (checkConfig.reuse_ai_session === true) {
|
|
1970
|
-
if (!checkConfig.depends_on || !Array.isArray(checkConfig.depends_on) || checkConfig.depends_on.length === 0) {
|
|
1971
|
-
errors.push({
|
|
1972
|
-
field: `checks.${checkName}.reuse_ai_session`,
|
|
1973
|
-
message: `Check "${checkName}" has reuse_ai_session=true but missing or empty depends_on. Session reuse requires dependency on another check.`,
|
|
1974
|
-
value: checkConfig.reuse_ai_session
|
|
1975
|
-
});
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
if (checkConfig.session_mode !== void 0) {
|
|
1980
|
-
if (checkConfig.session_mode !== "clone" && checkConfig.session_mode !== "append") {
|
|
1981
|
-
errors.push({
|
|
1982
|
-
field: `checks.${checkName}.session_mode`,
|
|
1983
|
-
message: `Invalid session_mode value for "${checkName}": must be 'clone' or 'append'`,
|
|
1984
|
-
value: checkConfig.session_mode
|
|
1985
|
-
});
|
|
1986
|
-
}
|
|
1987
|
-
if (!checkConfig.reuse_ai_session) {
|
|
1988
|
-
errors.push({
|
|
1989
|
-
field: `checks.${checkName}.session_mode`,
|
|
1990
|
-
message: `Check "${checkName}" has session_mode but no reuse_ai_session. session_mode requires reuse_ai_session to be set.`,
|
|
1991
|
-
value: checkConfig.session_mode
|
|
1992
|
-
});
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
if (checkConfig.tags !== void 0) {
|
|
1996
|
-
if (!Array.isArray(checkConfig.tags)) {
|
|
1997
|
-
errors.push({
|
|
1998
|
-
field: `checks.${checkName}.tags`,
|
|
1999
|
-
message: `Invalid tags value for "${checkName}": must be an array of strings`,
|
|
2000
|
-
value: checkConfig.tags
|
|
2001
|
-
});
|
|
2002
|
-
} else {
|
|
2003
|
-
const validTagPattern = /^[a-zA-Z0-9][a-zA-Z0-9-_]*$/;
|
|
2004
|
-
checkConfig.tags.forEach((tag, index) => {
|
|
2005
|
-
if (typeof tag !== "string") {
|
|
2006
|
-
errors.push({
|
|
2007
|
-
field: `checks.${checkName}.tags[${index}]`,
|
|
2008
|
-
message: `Invalid tag at index ${index} for "${checkName}": must be a string`,
|
|
2009
|
-
value: tag
|
|
2010
|
-
});
|
|
2011
|
-
} else if (!validTagPattern.test(tag)) {
|
|
2012
|
-
errors.push({
|
|
2013
|
-
field: `checks.${checkName}.tags[${index}]`,
|
|
2014
|
-
message: `Invalid tag "${tag}" for "${checkName}": tags must be alphanumeric with hyphens or underscores (start with alphanumeric)`,
|
|
2015
|
-
value: tag
|
|
2016
|
-
});
|
|
2017
|
-
}
|
|
2018
|
-
});
|
|
2019
|
-
}
|
|
1120
|
+
async getRepositoryStatus() {
|
|
1121
|
+
try {
|
|
1122
|
+
const { GitRepositoryAnalyzer } = await import("./git-repository-analyzer-HJC4MYW4.mjs");
|
|
1123
|
+
const analyzer = new GitRepositoryAnalyzer(this.workingDirectory);
|
|
1124
|
+
const info = await analyzer.analyzeRepository();
|
|
1125
|
+
return {
|
|
1126
|
+
isGitRepository: info.isGitRepository,
|
|
1127
|
+
branch: info.head,
|
|
1128
|
+
// Use head as branch name
|
|
1129
|
+
hasChanges: info.isGitRepository && (info.files?.length > 0 || false),
|
|
1130
|
+
filesChanged: info.isGitRepository ? info.files?.length || 0 : 0
|
|
1131
|
+
};
|
|
1132
|
+
} catch {
|
|
1133
|
+
return {
|
|
1134
|
+
isGitRepository: false,
|
|
1135
|
+
hasChanges: false
|
|
1136
|
+
};
|
|
2020
1137
|
}
|
|
2021
1138
|
}
|
|
2022
1139
|
/**
|
|
2023
|
-
*
|
|
1140
|
+
* Check if current directory is a git repository
|
|
1141
|
+
* @returns True if git repository, false otherwise
|
|
2024
1142
|
*/
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
field: fieldPrefix,
|
|
2029
|
-
message: `${fieldPrefix} must be an object mapping server names to { command, args?, env? }`,
|
|
2030
|
-
value: mcpServers
|
|
2031
|
-
});
|
|
2032
|
-
return;
|
|
2033
|
-
}
|
|
2034
|
-
for (const [serverName, cfg] of Object.entries(mcpServers)) {
|
|
2035
|
-
const pathStr = `${fieldPrefix}.${serverName}`;
|
|
2036
|
-
if (!cfg || typeof cfg !== "object") {
|
|
2037
|
-
errors.push({ field: pathStr, message: `${pathStr} must be an object`, value: cfg });
|
|
2038
|
-
continue;
|
|
2039
|
-
}
|
|
2040
|
-
const { command, args, env } = cfg;
|
|
2041
|
-
if (typeof command !== "string" || command.trim() === "") {
|
|
2042
|
-
errors.push({
|
|
2043
|
-
field: `${pathStr}.command`,
|
|
2044
|
-
message: `${pathStr}.command must be a non-empty string`,
|
|
2045
|
-
value: command
|
|
2046
|
-
});
|
|
2047
|
-
}
|
|
2048
|
-
if (args !== void 0 && !Array.isArray(args)) {
|
|
2049
|
-
errors.push({
|
|
2050
|
-
field: `${pathStr}.args`,
|
|
2051
|
-
message: `${pathStr}.args must be an array of strings`,
|
|
2052
|
-
value: args
|
|
2053
|
-
});
|
|
2054
|
-
}
|
|
2055
|
-
if (env !== void 0) {
|
|
2056
|
-
if (typeof env !== "object" || env === null) {
|
|
2057
|
-
errors.push({
|
|
2058
|
-
field: `${pathStr}.env`,
|
|
2059
|
-
message: `${pathStr}.env must be an object of string values`,
|
|
2060
|
-
value: env
|
|
2061
|
-
});
|
|
2062
|
-
} else {
|
|
2063
|
-
for (const [k, v] of Object.entries(env)) {
|
|
2064
|
-
if (typeof v !== "string") {
|
|
2065
|
-
errors.push({
|
|
2066
|
-
field: `${pathStr}.env.${k}`,
|
|
2067
|
-
message: `${pathStr}.env.${k} must be a string`,
|
|
2068
|
-
value: v
|
|
2069
|
-
});
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
1143
|
+
async isGitRepository() {
|
|
1144
|
+
const status = await this.getRepositoryStatus();
|
|
1145
|
+
return status.isGitRepository;
|
|
2075
1146
|
}
|
|
2076
1147
|
/**
|
|
2077
|
-
*
|
|
2078
|
-
*
|
|
1148
|
+
* Get list of available check types
|
|
1149
|
+
* @returns Array of check type names
|
|
2079
1150
|
*/
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
const jsonPath = path2.resolve(__dirname, "generated", "config-schema.json");
|
|
2085
|
-
const jsonSchema = __require(jsonPath);
|
|
2086
|
-
if (jsonSchema) {
|
|
2087
|
-
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true, strict: false });
|
|
2088
|
-
addFormats(ajv);
|
|
2089
|
-
const validate = ajv.compile(jsonSchema);
|
|
2090
|
-
__ajvValidate = (data) => validate(data);
|
|
2091
|
-
__ajvErrors = () => validate.errors;
|
|
2092
|
-
}
|
|
2093
|
-
} catch {
|
|
2094
|
-
}
|
|
2095
|
-
if (!__ajvValidate) {
|
|
2096
|
-
try {
|
|
2097
|
-
const mod = (init_config_schema(), __toCommonJS(config_schema_exports));
|
|
2098
|
-
const schema = mod?.configSchema || mod?.default || mod;
|
|
2099
|
-
if (schema) {
|
|
2100
|
-
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true, strict: false });
|
|
2101
|
-
addFormats(ajv);
|
|
2102
|
-
const validate = ajv.compile(schema);
|
|
2103
|
-
__ajvValidate = (data) => validate(data);
|
|
2104
|
-
__ajvErrors = () => validate.errors;
|
|
2105
|
-
} else {
|
|
2106
|
-
return;
|
|
2107
|
-
}
|
|
2108
|
-
} catch {
|
|
2109
|
-
return;
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
const ok = __ajvValidate(config);
|
|
2114
|
-
const errs = __ajvErrors ? __ajvErrors() : null;
|
|
2115
|
-
if (!ok && Array.isArray(errs)) {
|
|
2116
|
-
for (const e of errs) {
|
|
2117
|
-
const pathStr = e.instancePath ? e.instancePath.replace(/^\//, "").replace(/\//g, ".") : "";
|
|
2118
|
-
const msg = e.message || "Invalid configuration";
|
|
2119
|
-
if (e.keyword === "additionalProperties") {
|
|
2120
|
-
const addl = e.params && e.params.additionalProperty || "unknown";
|
|
2121
|
-
const fullField = pathStr ? `${pathStr}.${addl}` : addl;
|
|
2122
|
-
const topLevel = !pathStr;
|
|
2123
|
-
warnings.push({
|
|
2124
|
-
field: fullField || "config",
|
|
2125
|
-
message: topLevel ? `Unknown top-level key '${addl}' will be ignored.` : `Unknown key '${addl}' will be ignored`
|
|
2126
|
-
});
|
|
2127
|
-
} else {
|
|
2128
|
-
logger.debug(`Ajv note [${pathStr || "config"}]: ${msg}`);
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
} catch (err) {
|
|
2133
|
-
logger.debug(`Ajv validation skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
2134
|
-
}
|
|
1151
|
+
static getAvailableCheckTypes() {
|
|
1152
|
+
const { CheckProviderRegistry } = (init_check_provider_registry(), __toCommonJS(check_provider_registry_exports));
|
|
1153
|
+
const registry = CheckProviderRegistry.getInstance();
|
|
1154
|
+
return registry.getAvailableProviders();
|
|
2135
1155
|
}
|
|
2136
|
-
// Unknown-key warnings are fully handled by Ajv using the generated schema
|
|
2137
|
-
// Unknown-key hints are produced by Ajv (additionalProperties=false)
|
|
2138
1156
|
/**
|
|
2139
|
-
* Validate
|
|
1157
|
+
* Validate check types and return valid/invalid lists
|
|
1158
|
+
* @param checks - Array of check type names to validate
|
|
1159
|
+
* @returns Object with valid and invalid check types
|
|
2140
1160
|
*/
|
|
2141
|
-
|
|
2142
|
-
const
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
value: tagFilter.include
|
|
2149
|
-
});
|
|
2150
|
-
} else {
|
|
2151
|
-
tagFilter.include.forEach((tag, index) => {
|
|
2152
|
-
if (typeof tag !== "string") {
|
|
2153
|
-
errors.push({
|
|
2154
|
-
field: `tag_filter.include[${index}]`,
|
|
2155
|
-
message: `Invalid tag at index ${index}: must be a string`,
|
|
2156
|
-
value: tag
|
|
2157
|
-
});
|
|
2158
|
-
} else if (!validTagPattern.test(tag)) {
|
|
2159
|
-
errors.push({
|
|
2160
|
-
field: `tag_filter.include[${index}]`,
|
|
2161
|
-
message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
|
|
2162
|
-
value: tag
|
|
2163
|
-
});
|
|
2164
|
-
}
|
|
2165
|
-
});
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
if (tagFilter.exclude !== void 0) {
|
|
2169
|
-
if (!Array.isArray(tagFilter.exclude)) {
|
|
2170
|
-
errors.push({
|
|
2171
|
-
field: "tag_filter.exclude",
|
|
2172
|
-
message: "tag_filter.exclude must be an array of strings",
|
|
2173
|
-
value: tagFilter.exclude
|
|
2174
|
-
});
|
|
1161
|
+
static validateCheckTypes(checks) {
|
|
1162
|
+
const availableTypes = _StateMachineExecutionEngine.getAvailableCheckTypes();
|
|
1163
|
+
const valid = [];
|
|
1164
|
+
const invalid = [];
|
|
1165
|
+
for (const check of checks) {
|
|
1166
|
+
if (availableTypes.includes(check)) {
|
|
1167
|
+
valid.push(check);
|
|
2175
1168
|
} else {
|
|
2176
|
-
|
|
2177
|
-
if (typeof tag !== "string") {
|
|
2178
|
-
errors.push({
|
|
2179
|
-
field: `tag_filter.exclude[${index}]`,
|
|
2180
|
-
message: `Invalid tag at index ${index}: must be a string`,
|
|
2181
|
-
value: tag
|
|
2182
|
-
});
|
|
2183
|
-
} else if (!validTagPattern.test(tag)) {
|
|
2184
|
-
errors.push({
|
|
2185
|
-
field: `tag_filter.exclude[${index}]`,
|
|
2186
|
-
message: `Invalid tag "${tag}": tags must be alphanumeric with hyphens or underscores`,
|
|
2187
|
-
value: tag
|
|
2188
|
-
});
|
|
2189
|
-
}
|
|
2190
|
-
});
|
|
1169
|
+
invalid.push(check);
|
|
2191
1170
|
}
|
|
2192
1171
|
}
|
|
1172
|
+
return { valid, invalid };
|
|
2193
1173
|
}
|
|
2194
1174
|
/**
|
|
2195
|
-
*
|
|
1175
|
+
* Format the status column for execution statistics
|
|
1176
|
+
* Used by execution-statistics-formatting tests
|
|
2196
1177
|
*/
|
|
2197
|
-
|
|
2198
|
-
if (
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
})
|
|
2204
|
-
|
|
2205
|
-
if (httpServerConfig.enabled === true) {
|
|
2206
|
-
if (typeof httpServerConfig.port !== "number" || httpServerConfig.port < 1 || httpServerConfig.port > 65535) {
|
|
2207
|
-
errors.push({
|
|
2208
|
-
field: "http_server.port",
|
|
2209
|
-
message: "http_server.port must be a number between 1 and 65535",
|
|
2210
|
-
value: httpServerConfig.port
|
|
2211
|
-
});
|
|
2212
|
-
}
|
|
2213
|
-
if (httpServerConfig.auth) {
|
|
2214
|
-
const auth = httpServerConfig.auth;
|
|
2215
|
-
const validAuthTypes = ["bearer_token", "hmac", "basic", "none"];
|
|
2216
|
-
if (!auth.type || !validAuthTypes.includes(auth.type)) {
|
|
2217
|
-
errors.push({
|
|
2218
|
-
field: "http_server.auth.type",
|
|
2219
|
-
message: `Invalid auth type. Must be one of: ${validAuthTypes.join(", ")}`,
|
|
2220
|
-
value: auth.type
|
|
2221
|
-
});
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
if (httpServerConfig.tls && typeof httpServerConfig.tls === "object") {
|
|
2225
|
-
const tls = httpServerConfig.tls;
|
|
2226
|
-
if (tls.enabled === true) {
|
|
2227
|
-
if (!tls.cert) {
|
|
2228
|
-
errors.push({
|
|
2229
|
-
field: "http_server.tls.cert",
|
|
2230
|
-
message: "TLS certificate is required when TLS is enabled"
|
|
2231
|
-
});
|
|
2232
|
-
}
|
|
2233
|
-
if (!tls.key) {
|
|
2234
|
-
errors.push({
|
|
2235
|
-
field: "http_server.tls.key",
|
|
2236
|
-
message: "TLS key is required when TLS is enabled"
|
|
2237
|
-
});
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
if (httpServerConfig.endpoints && Array.isArray(httpServerConfig.endpoints)) {
|
|
2242
|
-
for (let i = 0; i < httpServerConfig.endpoints.length; i++) {
|
|
2243
|
-
const endpoint = httpServerConfig.endpoints[i];
|
|
2244
|
-
if (!endpoint.path || typeof endpoint.path !== "string") {
|
|
2245
|
-
errors.push({
|
|
2246
|
-
field: `http_server.endpoints[${i}].path`,
|
|
2247
|
-
message: "Endpoint path must be a string",
|
|
2248
|
-
value: endpoint.path
|
|
2249
|
-
});
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
1178
|
+
formatStatusColumn(stats) {
|
|
1179
|
+
if (stats.skipped) {
|
|
1180
|
+
if (stats.skipReason === "if_condition") {
|
|
1181
|
+
return "\u23ED if";
|
|
1182
|
+
} else if (stats.skipReason === "fail_fast") {
|
|
1183
|
+
return "\u23ED ff";
|
|
1184
|
+
} else if (stats.skipReason === "dependency_failed") {
|
|
1185
|
+
return "\u23ED dep";
|
|
2252
1186
|
}
|
|
1187
|
+
return "\u23ED";
|
|
1188
|
+
}
|
|
1189
|
+
const totalRuns = stats.totalRuns;
|
|
1190
|
+
const successfulRuns = stats.successfulRuns;
|
|
1191
|
+
const failedRuns = stats.failedRuns;
|
|
1192
|
+
if (failedRuns > 0 && successfulRuns > 0) {
|
|
1193
|
+
return `\u2714/\u2716 ${successfulRuns}/${totalRuns}`;
|
|
1194
|
+
} else if (failedRuns > 0) {
|
|
1195
|
+
return totalRuns === 1 ? "\u2716" : `\u2716 \xD7${totalRuns}`;
|
|
1196
|
+
} else {
|
|
1197
|
+
return totalRuns === 1 ? "\u2714" : `\u2714 \xD7${totalRuns}`;
|
|
2253
1198
|
}
|
|
2254
1199
|
}
|
|
2255
1200
|
/**
|
|
2256
|
-
*
|
|
1201
|
+
* Format the details column for execution statistics
|
|
1202
|
+
* Used by execution-statistics-formatting tests
|
|
2257
1203
|
*/
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
errors.push({
|
|
2263
|
-
field: "output.pr_comment.format",
|
|
2264
|
-
message: `Invalid output format "${prComment.format}". Must be one of: ${this.validOutputFormats.join(", ")}`,
|
|
2265
|
-
value: prComment.format
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
if (typeof prComment.group_by === "string" && !this.validGroupByOptions.includes(prComment.group_by)) {
|
|
2269
|
-
errors.push({
|
|
2270
|
-
field: "output.pr_comment.group_by",
|
|
2271
|
-
message: `Invalid group_by option "${prComment.group_by}". Must be one of: ${this.validGroupByOptions.join(", ")}`,
|
|
2272
|
-
value: prComment.group_by
|
|
2273
|
-
});
|
|
2274
|
-
}
|
|
1204
|
+
formatDetailsColumn(stats) {
|
|
1205
|
+
const parts = [];
|
|
1206
|
+
if (stats.outputsProduced !== void 0 && stats.outputsProduced > 0) {
|
|
1207
|
+
parts.push(`\u2192${stats.outputsProduced}`);
|
|
2275
1208
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
if (process.env.VISOR_NO_REMOTE_EXTENDS === "true" || process.env.VISOR_NO_REMOTE_EXTENDS === "1") {
|
|
2282
|
-
return false;
|
|
1209
|
+
if (stats.issuesBySeverity.critical > 0) {
|
|
1210
|
+
parts.push(`${stats.issuesBySeverity.critical}\u{1F534}`);
|
|
1211
|
+
}
|
|
1212
|
+
if (stats.issuesBySeverity.error > 0 && stats.issuesBySeverity.critical === 0) {
|
|
1213
|
+
parts.push(`${stats.issuesBySeverity.error}\u274C`);
|
|
2283
1214
|
}
|
|
2284
|
-
|
|
1215
|
+
if (stats.issuesBySeverity.warning > 0) {
|
|
1216
|
+
parts.push(`${stats.issuesBySeverity.warning}\u26A0\uFE0F`);
|
|
1217
|
+
}
|
|
1218
|
+
if (stats.issuesBySeverity.info > 0 && stats.issuesBySeverity.critical === 0 && stats.issuesBySeverity.error === 0 && stats.issuesBySeverity.warning === 0) {
|
|
1219
|
+
parts.push(`${stats.issuesBySeverity.info}\u{1F4A1}`);
|
|
1220
|
+
}
|
|
1221
|
+
if (stats.errorMessage) {
|
|
1222
|
+
parts.push(this.truncate(stats.errorMessage, 40));
|
|
1223
|
+
}
|
|
1224
|
+
if (stats.skipCondition) {
|
|
1225
|
+
parts.push(this.truncate(stats.skipCondition, 40));
|
|
1226
|
+
}
|
|
1227
|
+
return parts.join(" ");
|
|
2285
1228
|
}
|
|
2286
1229
|
/**
|
|
2287
|
-
*
|
|
1230
|
+
* Truncate a string to a maximum length
|
|
1231
|
+
* Used by formatDetailsColumn
|
|
2288
1232
|
*/
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
checks: {},
|
|
2293
|
-
max_parallelism: 3,
|
|
2294
|
-
output: {
|
|
2295
|
-
pr_comment: {
|
|
2296
|
-
format: "markdown",
|
|
2297
|
-
group_by: "check",
|
|
2298
|
-
collapse: true
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
};
|
|
2302
|
-
const merged = { ...defaultConfig, ...config };
|
|
2303
|
-
if (merged.output) {
|
|
2304
|
-
merged.output.pr_comment = {
|
|
2305
|
-
...defaultConfig.output.pr_comment,
|
|
2306
|
-
...merged.output.pr_comment
|
|
2307
|
-
};
|
|
2308
|
-
} else {
|
|
2309
|
-
merged.output = defaultConfig.output;
|
|
1233
|
+
truncate(str, maxLength) {
|
|
1234
|
+
if (str.length <= maxLength) {
|
|
1235
|
+
return str;
|
|
2310
1236
|
}
|
|
2311
|
-
return
|
|
1237
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
2312
1238
|
}
|
|
2313
1239
|
};
|
|
2314
|
-
|
|
2315
|
-
|
|
1240
|
+
function serializeRunState(state) {
|
|
1241
|
+
return {
|
|
1242
|
+
...state,
|
|
1243
|
+
levelQueue: state.levelQueue,
|
|
1244
|
+
eventQueue: state.eventQueue,
|
|
1245
|
+
activeDispatches: Array.from(state.activeDispatches.entries()),
|
|
1246
|
+
completedChecks: Array.from(state.completedChecks.values()),
|
|
1247
|
+
stats: Array.from(state.stats.entries()),
|
|
1248
|
+
historyLog: state.historyLog,
|
|
1249
|
+
forwardRunGuards: Array.from(state.forwardRunGuards.values()),
|
|
1250
|
+
currentLevelChecks: Array.from(state.currentLevelChecks.values()),
|
|
1251
|
+
currentWaveCompletions: Array.from(
|
|
1252
|
+
state.currentWaveCompletions || []
|
|
1253
|
+
),
|
|
1254
|
+
// failedChecks is an internal Set added by stats/dispatch layers; keep it if present
|
|
1255
|
+
failedChecks: Array.from(state.failedChecks || []),
|
|
1256
|
+
pendingRunScopes: Array.from((state.pendingRunScopes || /* @__PURE__ */ new Map()).entries()).map(([k, v]) => [
|
|
1257
|
+
k,
|
|
1258
|
+
v
|
|
1259
|
+
])
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
2316
1262
|
|
|
2317
1263
|
// src/sdk.ts
|
|
1264
|
+
init_config();
|
|
2318
1265
|
async function loadConfig(configOrPath, options) {
|
|
2319
1266
|
const cm = new ConfigManager();
|
|
2320
1267
|
if (typeof configOrPath === "object" && configOrPath !== null) {
|
|
@@ -2369,7 +1316,7 @@ async function runChecks(opts = {}) {
|
|
|
2369
1316
|
config = await cm.findAndLoadConfig();
|
|
2370
1317
|
}
|
|
2371
1318
|
const checks = opts.checks && opts.checks.length > 0 ? resolveChecks(opts.checks, config) : Object.keys(config.checks || {});
|
|
2372
|
-
const engine = new
|
|
1319
|
+
const engine = new StateMachineExecutionEngine(opts.cwd);
|
|
2373
1320
|
if (opts.executionContext) {
|
|
2374
1321
|
engine.setExecutionContext(opts.executionContext);
|
|
2375
1322
|
}
|