@yasserkhanorg/impact-gate 2.0.0
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/LICENSE +168 -0
- package/README.md +520 -0
- package/dist/adapters/cypress.d.ts +10 -0
- package/dist/adapters/cypress.d.ts.map +1 -0
- package/dist/adapters/cypress.js +86 -0
- package/dist/adapters/framework_adapter.d.ts +41 -0
- package/dist/adapters/framework_adapter.d.ts.map +1 -0
- package/dist/adapters/framework_adapter.js +152 -0
- package/dist/adapters/playwright.d.ts +10 -0
- package/dist/adapters/playwright.d.ts.map +1 -0
- package/dist/adapters/playwright.js +86 -0
- package/dist/adapters/pytest.d.ts +10 -0
- package/dist/adapters/pytest.d.ts.map +1 -0
- package/dist/adapters/pytest.js +96 -0
- package/dist/adapters/supertest.d.ts +12 -0
- package/dist/adapters/supertest.d.ts.map +1 -0
- package/dist/adapters/supertest.js +85 -0
- package/dist/agent/api_catalog.d.ts +11 -0
- package/dist/agent/api_catalog.d.ts.map +1 -0
- package/dist/agent/api_catalog.js +210 -0
- package/dist/agent/config.d.ts +193 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +875 -0
- package/dist/agent/feedback.d.ts +91 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +323 -0
- package/dist/agent/git.d.ts +19 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +257 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/llm_agents_flow.d.ts +15 -0
- package/dist/agent/llm_agents_flow.d.ts.map +1 -0
- package/dist/agent/llm_agents_flow.js +434 -0
- package/dist/agent/native_flow.d.ts +6 -0
- package/dist/agent/native_flow.d.ts.map +1 -0
- package/dist/agent/native_flow.js +179 -0
- package/dist/agent/pipeline.d.ts +7 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +260 -0
- package/dist/agent/pipeline_types.d.ts +54 -0
- package/dist/agent/pipeline_types.d.ts.map +1 -0
- package/dist/agent/pipeline_types.js +4 -0
- package/dist/agent/pipeline_utils.d.ts +12 -0
- package/dist/agent/pipeline_utils.d.ts.map +1 -0
- package/dist/agent/pipeline_utils.js +156 -0
- package/dist/agent/plan.d.ts +170 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +86 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/process_runner.d.ts +10 -0
- package/dist/agent/process_runner.d.ts.map +1 -0
- package/dist/agent/process_runner.js +92 -0
- package/dist/agent/spec_generator.d.ts +5 -0
- package/dist/agent/spec_generator.d.ts.map +1 -0
- package/dist/agent/spec_generator.js +253 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/types.d.ts +42 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +4 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validation_runner.d.ts +5 -0
- package/dist/agent/validation_runner.d.ts.map +1 -0
- package/dist/agent/validation_runner.js +77 -0
- package/dist/agentic/fix_loop.d.ts +26 -0
- package/dist/agentic/fix_loop.d.ts.map +1 -0
- package/dist/agentic/fix_loop.js +96 -0
- package/dist/agentic/playwright_runner.d.ts +43 -0
- package/dist/agentic/playwright_runner.d.ts.map +1 -0
- package/dist/agentic/playwright_runner.js +165 -0
- package/dist/agentic/runner.d.ts +27 -0
- package/dist/agentic/runner.d.ts.map +1 -0
- package/dist/agentic/runner.js +210 -0
- package/dist/agentic/types.d.ts +62 -0
- package/dist/agentic/types.d.ts.map +1 -0
- package/dist/agentic/types.js +4 -0
- package/dist/agents/coverage-evaluator.d.ts +8 -0
- package/dist/agents/coverage-evaluator.d.ts.map +1 -0
- package/dist/agents/coverage-evaluator.js +41 -0
- package/dist/agents/cross-impact.d.ts +13 -0
- package/dist/agents/cross-impact.d.ts.map +1 -0
- package/dist/agents/cross-impact.js +140 -0
- package/dist/agents/executor.d.ts +8 -0
- package/dist/agents/executor.d.ts.map +1 -0
- package/dist/agents/executor.js +75 -0
- package/dist/agents/explorer.d.ts +12 -0
- package/dist/agents/explorer.d.ts.map +1 -0
- package/dist/agents/explorer.js +43 -0
- package/dist/agents/generator.d.ts +8 -0
- package/dist/agents/generator.d.ts.map +1 -0
- package/dist/agents/generator.js +77 -0
- package/dist/agents/healer.d.ts +8 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +31 -0
- package/dist/agents/impact-analyst.d.ts +8 -0
- package/dist/agents/impact-analyst.d.ts.map +1 -0
- package/dist/agents/impact-analyst.js +38 -0
- package/dist/agents/regression-advisor.d.ts +8 -0
- package/dist/agents/regression-advisor.d.ts.map +1 -0
- package/dist/agents/regression-advisor.js +116 -0
- package/dist/agents/strategist.d.ts +9 -0
- package/dist/agents/strategist.d.ts.map +1 -0
- package/dist/agents/strategist.js +92 -0
- package/dist/agents/test-designer.d.ts +8 -0
- package/dist/agents/test-designer.d.ts.map +1 -0
- package/dist/agents/test-designer.js +111 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +334 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +151 -0
- package/dist/base_provider.d.ts +109 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +203 -0
- package/dist/budget_ledger.d.ts +28 -0
- package/dist/budget_ledger.d.ts.map +1 -0
- package/dist/budget_ledger.js +62 -0
- package/dist/cache/cached_provider.d.ts +49 -0
- package/dist/cache/cached_provider.d.ts.map +1 -0
- package/dist/cache/cached_provider.js +91 -0
- package/dist/cache/response_cache.d.ts +79 -0
- package/dist/cache/response_cache.d.ts.map +1 -0
- package/dist/cache/response_cache.js +177 -0
- package/dist/cli/commands/analyze.d.ts +3 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +77 -0
- package/dist/cli/commands/bootstrap.d.ts +3 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +109 -0
- package/dist/cli/commands/cost_report.d.ts +3 -0
- package/dist/cli/commands/cost_report.d.ts.map +1 -0
- package/dist/cli/commands/cost_report.js +115 -0
- package/dist/cli/commands/crew.d.ts +3 -0
- package/dist/cli/commands/crew.d.ts.map +1 -0
- package/dist/cli/commands/crew.js +255 -0
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +39 -0
- package/dist/cli/commands/finalize.d.ts +3 -0
- package/dist/cli/commands/finalize.d.ts.map +1 -0
- package/dist/cli/commands/finalize.js +41 -0
- package/dist/cli/commands/gate.d.ts +3 -0
- package/dist/cli/commands/gate.d.ts.map +1 -0
- package/dist/cli/commands/gate.js +89 -0
- package/dist/cli/commands/generate.d.ts +4 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +108 -0
- package/dist/cli/commands/heal.d.ts +3 -0
- package/dist/cli/commands/heal.d.ts.map +1 -0
- package/dist/cli/commands/heal.js +60 -0
- package/dist/cli/commands/impact.d.ts +4 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +33 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +169 -0
- package/dist/cli/commands/llm_health.d.ts +2 -0
- package/dist/cli/commands/llm_health.d.ts.map +1 -0
- package/dist/cli/commands/llm_health.js +22 -0
- package/dist/cli/commands/plan.d.ts +4 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +120 -0
- package/dist/cli/commands/plan_crew.d.ts +17 -0
- package/dist/cli/commands/plan_crew.d.ts.map +1 -0
- package/dist/cli/commands/plan_crew.js +316 -0
- package/dist/cli/commands/traceability.d.ts +4 -0
- package/dist/cli/commands/traceability.d.ts.map +1 -0
- package/dist/cli/commands/traceability.js +77 -0
- package/dist/cli/commands/train.d.ts +3 -0
- package/dist/cli/commands/train.d.ts.map +1 -0
- package/dist/cli/commands/train.js +391 -0
- package/dist/cli/defaults.d.ts +35 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +172 -0
- package/dist/cli/errors.d.ts +27 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +57 -0
- package/dist/cli/parse_args.d.ts +6 -0
- package/dist/cli/parse_args.d.ts.map +1 -0
- package/dist/cli/parse_args.js +257 -0
- package/dist/cli/types.d.ts +87 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/usage.d.ts +2 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +109 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +194 -0
- package/dist/crew/context.d.ts +55 -0
- package/dist/crew/context.d.ts.map +1 -0
- package/dist/crew/context.js +36 -0
- package/dist/crew/orchestrator.d.ts +50 -0
- package/dist/crew/orchestrator.d.ts.map +1 -0
- package/dist/crew/orchestrator.js +329 -0
- package/dist/crew/protocol.d.ts +46 -0
- package/dist/crew/protocol.d.ts.map +1 -0
- package/dist/crew/protocol.js +4 -0
- package/dist/crew/provider.d.ts +17 -0
- package/dist/crew/provider.d.ts.map +1 -0
- package/dist/crew/provider.js +36 -0
- package/dist/crew/sanitize.d.ts +3 -0
- package/dist/crew/sanitize.d.ts.map +1 -0
- package/dist/crew/sanitize.js +31 -0
- package/dist/crew/types.d.ts +52 -0
- package/dist/crew/types.d.ts.map +1 -0
- package/dist/crew/types.js +4 -0
- package/dist/crew/workflows.d.ts +52 -0
- package/dist/crew/workflows.d.ts.map +1 -0
- package/dist/crew/workflows.js +36 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +277 -0
- package/dist/engine/ai_enrichment.d.ts +44 -0
- package/dist/engine/ai_enrichment.d.ts.map +1 -0
- package/dist/engine/ai_enrichment.js +267 -0
- package/dist/engine/diff_loader.d.ts +11 -0
- package/dist/engine/diff_loader.d.ts.map +1 -0
- package/dist/engine/diff_loader.js +63 -0
- package/dist/engine/impact_engine.d.ts +72 -0
- package/dist/engine/impact_engine.d.ts.map +1 -0
- package/dist/engine/impact_engine.js +298 -0
- package/dist/engine/plan_builder.d.ts +11 -0
- package/dist/engine/plan_builder.d.ts.map +1 -0
- package/dist/engine/plan_builder.js +599 -0
- package/dist/esm/adapters/cypress.js +49 -0
- package/dist/esm/adapters/framework_adapter.js +114 -0
- package/dist/esm/adapters/playwright.js +49 -0
- package/dist/esm/adapters/pytest.js +59 -0
- package/dist/esm/adapters/supertest.js +48 -0
- package/dist/esm/agent/api_catalog.js +199 -0
- package/dist/esm/agent/config.js +872 -0
- package/dist/esm/agent/feedback.js +317 -0
- package/dist/esm/agent/git.js +252 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/llm_agents_flow.js +421 -0
- package/dist/esm/agent/native_flow.js +175 -0
- package/dist/esm/agent/pipeline.js +256 -0
- package/dist/esm/agent/pipeline_types.js +3 -0
- package/dist/esm/agent/pipeline_utils.js +146 -0
- package/dist/esm/agent/plan.js +83 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/process_runner.js +83 -0
- package/dist/esm/agent/spec_generator.js +249 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/types.js +3 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validation_runner.js +73 -0
- package/dist/esm/agentic/fix_loop.js +91 -0
- package/dist/esm/agentic/playwright_runner.js +161 -0
- package/dist/esm/agentic/runner.js +207 -0
- package/dist/esm/agentic/types.js +3 -0
- package/dist/esm/agents/coverage-evaluator.js +37 -0
- package/dist/esm/agents/cross-impact.js +136 -0
- package/dist/esm/agents/executor.js +71 -0
- package/dist/esm/agents/explorer.js +39 -0
- package/dist/esm/agents/generator.js +73 -0
- package/dist/esm/agents/healer.js +27 -0
- package/dist/esm/agents/impact-analyst.js +34 -0
- package/dist/esm/agents/regression-advisor.js +112 -0
- package/dist/esm/agents/strategist.js +88 -0
- package/dist/esm/agents/test-designer.js +107 -0
- package/dist/esm/anthropic_provider.js +326 -0
- package/dist/esm/api.js +143 -0
- package/dist/esm/base_provider.js +198 -0
- package/dist/esm/budget_ledger.js +58 -0
- package/dist/esm/cache/cached_provider.js +85 -0
- package/dist/esm/cache/response_cache.js +140 -0
- package/dist/esm/cli/commands/analyze.js +74 -0
- package/dist/esm/cli/commands/bootstrap.js +106 -0
- package/dist/esm/cli/commands/cost_report.js +112 -0
- package/dist/esm/cli/commands/crew.js +252 -0
- package/dist/esm/cli/commands/feedback.js +36 -0
- package/dist/esm/cli/commands/finalize.js +38 -0
- package/dist/esm/cli/commands/gate.js +86 -0
- package/dist/esm/cli/commands/generate.js +105 -0
- package/dist/esm/cli/commands/heal.js +57 -0
- package/dist/esm/cli/commands/impact.js +30 -0
- package/dist/esm/cli/commands/init.js +133 -0
- package/dist/esm/cli/commands/llm_health.js +19 -0
- package/dist/esm/cli/commands/plan.js +117 -0
- package/dist/esm/cli/commands/plan_crew.js +309 -0
- package/dist/esm/cli/commands/traceability.js +73 -0
- package/dist/esm/cli/commands/train.js +355 -0
- package/dist/esm/cli/defaults.js +165 -0
- package/dist/esm/cli/errors.js +52 -0
- package/dist/esm/cli/parse_args.js +251 -0
- package/dist/esm/cli/types.js +3 -0
- package/dist/esm/cli/usage.js +106 -0
- package/dist/esm/cli.js +192 -0
- package/dist/esm/crew/context.js +32 -0
- package/dist/esm/crew/orchestrator.js +325 -0
- package/dist/esm/crew/protocol.js +3 -0
- package/dist/esm/crew/provider.js +33 -0
- package/dist/esm/crew/sanitize.js +27 -0
- package/dist/esm/crew/types.js +3 -0
- package/dist/esm/crew/workflows.js +33 -0
- package/dist/esm/custom_provider.js +273 -0
- package/dist/esm/engine/ai_enrichment.js +264 -0
- package/dist/esm/engine/diff_loader.js +59 -0
- package/dist/esm/engine/impact_engine.js +291 -0
- package/dist/esm/engine/plan_builder.js +593 -0
- package/dist/esm/index.js +72 -0
- package/dist/esm/knowledge/api_surface.js +408 -0
- package/dist/esm/knowledge/cluster_utils.js +60 -0
- package/dist/esm/knowledge/context_loader.js +85 -0
- package/dist/esm/knowledge/failure_history.js +121 -0
- package/dist/esm/knowledge/kg_bridge.js +381 -0
- package/dist/esm/knowledge/kg_types.js +3 -0
- package/dist/esm/knowledge/route_families.js +393 -0
- package/dist/esm/knowledge/spec_index.js +122 -0
- package/dist/esm/logger.js +115 -0
- package/dist/esm/mcp-server.js +621 -0
- package/dist/esm/metrics/prometheus.js +149 -0
- package/dist/esm/model_router.js +59 -0
- package/dist/esm/ollama_provider.js +301 -0
- package/dist/esm/openai_provider.js +243 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/pipeline/orchestrator.js +228 -0
- package/dist/esm/pipeline/spec_verifier.js +75 -0
- package/dist/esm/pipeline/stage0_preprocess.js +102 -0
- package/dist/esm/pipeline/stage1_impact.js +140 -0
- package/dist/esm/pipeline/stage2_coverage.js +153 -0
- package/dist/esm/pipeline/stage3_generation.js +284 -0
- package/dist/esm/pipeline/stage4_heal.js +288 -0
- package/dist/esm/progress.js +112 -0
- package/dist/esm/prompts/coverage.js +57 -0
- package/dist/esm/prompts/cross-impact.js +53 -0
- package/dist/esm/prompts/generation.js +297 -0
- package/dist/esm/prompts/generation_profile.js +147 -0
- package/dist/esm/prompts/heal.js +91 -0
- package/dist/esm/prompts/impact.js +63 -0
- package/dist/esm/prompts/json_extract.js +36 -0
- package/dist/esm/prompts/strategist.js +61 -0
- package/dist/esm/prompts/test-designer.js +92 -0
- package/dist/esm/provider_factory.js +366 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/esm/qa-agent/cli.js +205 -0
- package/dist/esm/qa-agent/orchestrator.js +120 -0
- package/dist/esm/qa-agent/phase1/runner.js +139 -0
- package/dist/esm/qa-agent/phase1/scope.js +126 -0
- package/dist/esm/qa-agent/phase2/agent_browser.js +95 -0
- package/dist/esm/qa-agent/phase2/agent_loop.js +351 -0
- package/dist/esm/qa-agent/phase2/exploration_state.js +97 -0
- package/dist/esm/qa-agent/phase2/tools.js +386 -0
- package/dist/esm/qa-agent/phase2/vision.js +75 -0
- package/dist/esm/qa-agent/phase3/feedback.js +34 -0
- package/dist/esm/qa-agent/phase3/reporter.js +145 -0
- package/dist/esm/qa-agent/phase3/spec_generator.js +62 -0
- package/dist/esm/qa-agent/phase3/verdict.js +66 -0
- package/dist/esm/qa-agent/safe_env.js +23 -0
- package/dist/esm/qa-agent/types.js +3 -0
- package/dist/esm/reporters/junit.js +86 -0
- package/dist/esm/reporters/reporter.js +3 -0
- package/dist/esm/reporters/sarif.js +132 -0
- package/dist/esm/resilience/circuit_breaker.js +78 -0
- package/dist/esm/resilience/retry.js +56 -0
- package/dist/esm/sanitize.js +66 -0
- package/dist/esm/training/enricher.js +345 -0
- package/dist/esm/training/kg_scanner.js +115 -0
- package/dist/esm/training/merger.js +204 -0
- package/dist/esm/training/scanner.js +923 -0
- package/dist/esm/training/types.js +6 -0
- package/dist/esm/training/validator.js +254 -0
- package/dist/esm/validation/guardrails.js +101 -0
- package/dist/esm/validation/output_schema.js +80 -0
- package/dist/esm/version.js +33 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/knowledge/api_surface.d.ts +37 -0
- package/dist/knowledge/api_surface.d.ts.map +1 -0
- package/dist/knowledge/api_surface.js +418 -0
- package/dist/knowledge/cluster_utils.d.ts +28 -0
- package/dist/knowledge/cluster_utils.d.ts.map +1 -0
- package/dist/knowledge/cluster_utils.js +67 -0
- package/dist/knowledge/context_loader.d.ts +13 -0
- package/dist/knowledge/context_loader.d.ts.map +1 -0
- package/dist/knowledge/context_loader.js +90 -0
- package/dist/knowledge/failure_history.d.ts +39 -0
- package/dist/knowledge/failure_history.d.ts.map +1 -0
- package/dist/knowledge/failure_history.js +128 -0
- package/dist/knowledge/kg_bridge.d.ts +31 -0
- package/dist/knowledge/kg_bridge.d.ts.map +1 -0
- package/dist/knowledge/kg_bridge.js +388 -0
- package/dist/knowledge/kg_types.d.ts +75 -0
- package/dist/knowledge/kg_types.d.ts.map +1 -0
- package/dist/knowledge/kg_types.js +4 -0
- package/dist/knowledge/route_families.d.ts +98 -0
- package/dist/knowledge/route_families.d.ts.map +1 -0
- package/dist/knowledge/route_families.js +410 -0
- package/dist/knowledge/spec_index.d.ts +18 -0
- package/dist/knowledge/spec_index.d.ts.map +1 -0
- package/dist/knowledge/spec_index.js +128 -0
- package/dist/logger.d.ts +31 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +119 -0
- package/dist/mcp-server.d.ts +68 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +629 -0
- package/dist/metrics/prometheus.d.ts +37 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +153 -0
- package/dist/model_router.d.ts +28 -0
- package/dist/model_router.d.ts.map +1 -0
- package/dist/model_router.js +63 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +309 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +251 -0
- package/dist/pipeline/orchestrator.d.ts +33 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +231 -0
- package/dist/pipeline/spec_verifier.d.ts +20 -0
- package/dist/pipeline/spec_verifier.d.ts.map +1 -0
- package/dist/pipeline/spec_verifier.js +79 -0
- package/dist/pipeline/stage0_preprocess.d.ts +31 -0
- package/dist/pipeline/stage0_preprocess.d.ts.map +1 -0
- package/dist/pipeline/stage0_preprocess.js +105 -0
- package/dist/pipeline/stage1_impact.d.ts +19 -0
- package/dist/pipeline/stage1_impact.d.ts.map +1 -0
- package/dist/pipeline/stage1_impact.js +143 -0
- package/dist/pipeline/stage2_coverage.d.ts +19 -0
- package/dist/pipeline/stage2_coverage.d.ts.map +1 -0
- package/dist/pipeline/stage2_coverage.js +156 -0
- package/dist/pipeline/stage3_generation.d.ts +43 -0
- package/dist/pipeline/stage3_generation.d.ts.map +1 -0
- package/dist/pipeline/stage3_generation.js +287 -0
- package/dist/pipeline/stage4_heal.d.ts +62 -0
- package/dist/pipeline/stage4_heal.d.ts.map +1 -0
- package/dist/pipeline/stage4_heal.js +294 -0
- package/dist/progress.d.ts +22 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +116 -0
- package/dist/prompts/coverage.d.ts +39 -0
- package/dist/prompts/coverage.d.ts.map +1 -0
- package/dist/prompts/coverage.js +61 -0
- package/dist/prompts/cross-impact.d.ts +23 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -0
- package/dist/prompts/cross-impact.js +57 -0
- package/dist/prompts/generation.d.ts +25 -0
- package/dist/prompts/generation.d.ts.map +1 -0
- package/dist/prompts/generation.js +302 -0
- package/dist/prompts/generation_profile.d.ts +29 -0
- package/dist/prompts/generation_profile.d.ts.map +1 -0
- package/dist/prompts/generation_profile.js +151 -0
- package/dist/prompts/heal.d.ts +23 -0
- package/dist/prompts/heal.d.ts.map +1 -0
- package/dist/prompts/heal.js +95 -0
- package/dist/prompts/impact.d.ts +31 -0
- package/dist/prompts/impact.d.ts.map +1 -0
- package/dist/prompts/impact.js +67 -0
- package/dist/prompts/json_extract.d.ts +14 -0
- package/dist/prompts/json_extract.d.ts.map +1 -0
- package/dist/prompts/json_extract.js +39 -0
- package/dist/prompts/strategist.d.ts +25 -0
- package/dist/prompts/strategist.d.ts.map +1 -0
- package/dist/prompts/strategist.js +65 -0
- package/dist/prompts/test-designer.d.ts +35 -0
- package/dist/prompts/test-designer.d.ts.map +1 -0
- package/dist/prompts/test-designer.js +96 -0
- package/dist/provider_factory.d.ts +104 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +371 -0
- package/dist/provider_interface.d.ts +365 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/dist/qa-agent/cli.d.ts +3 -0
- package/dist/qa-agent/cli.d.ts.map +1 -0
- package/dist/qa-agent/cli.js +207 -0
- package/dist/qa-agent/orchestrator.d.ts +3 -0
- package/dist/qa-agent/orchestrator.d.ts.map +1 -0
- package/dist/qa-agent/orchestrator.js +123 -0
- package/dist/qa-agent/phase1/runner.d.ts +3 -0
- package/dist/qa-agent/phase1/runner.d.ts.map +1 -0
- package/dist/qa-agent/phase1/runner.js +142 -0
- package/dist/qa-agent/phase1/scope.d.ts +6 -0
- package/dist/qa-agent/phase1/scope.d.ts.map +1 -0
- package/dist/qa-agent/phase1/scope.js +129 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts +35 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_browser.js +99 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts +3 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_loop.js +357 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts +12 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts.map +1 -0
- package/dist/qa-agent/phase2/exploration_state.js +109 -0
- package/dist/qa-agent/phase2/tools.d.ts +28 -0
- package/dist/qa-agent/phase2/tools.d.ts.map +1 -0
- package/dist/qa-agent/phase2/tools.js +390 -0
- package/dist/qa-agent/phase2/vision.d.ts +3 -0
- package/dist/qa-agent/phase2/vision.d.ts.map +1 -0
- package/dist/qa-agent/phase2/vision.js +78 -0
- package/dist/qa-agent/phase3/feedback.d.ts +3 -0
- package/dist/qa-agent/phase3/feedback.d.ts.map +1 -0
- package/dist/qa-agent/phase3/feedback.js +37 -0
- package/dist/qa-agent/phase3/reporter.d.ts +3 -0
- package/dist/qa-agent/phase3/reporter.d.ts.map +1 -0
- package/dist/qa-agent/phase3/reporter.js +148 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts +3 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts.map +1 -0
- package/dist/qa-agent/phase3/spec_generator.js +65 -0
- package/dist/qa-agent/phase3/verdict.d.ts +3 -0
- package/dist/qa-agent/phase3/verdict.d.ts.map +1 -0
- package/dist/qa-agent/phase3/verdict.js +69 -0
- package/dist/qa-agent/safe_env.d.ts +3 -0
- package/dist/qa-agent/safe_env.d.ts.map +1 -0
- package/dist/qa-agent/safe_env.js +26 -0
- package/dist/qa-agent/types.d.ts +130 -0
- package/dist/qa-agent/types.d.ts.map +1 -0
- package/dist/qa-agent/types.js +4 -0
- package/dist/reporters/junit.d.ts +6 -0
- package/dist/reporters/junit.d.ts.map +1 -0
- package/dist/reporters/junit.js +89 -0
- package/dist/reporters/reporter.d.ts +42 -0
- package/dist/reporters/reporter.d.ts.map +1 -0
- package/dist/reporters/reporter.js +4 -0
- package/dist/reporters/sarif.d.ts +7 -0
- package/dist/reporters/sarif.d.ts.map +1 -0
- package/dist/reporters/sarif.js +135 -0
- package/dist/resilience/circuit_breaker.d.ts +36 -0
- package/dist/resilience/circuit_breaker.d.ts.map +1 -0
- package/dist/resilience/circuit_breaker.js +82 -0
- package/dist/resilience/retry.d.ts +11 -0
- package/dist/resilience/retry.d.ts.map +1 -0
- package/dist/resilience/retry.js +59 -0
- package/dist/sanitize.d.ts +15 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +71 -0
- package/dist/training/enricher.d.ts +17 -0
- package/dist/training/enricher.d.ts.map +1 -0
- package/dist/training/enricher.js +350 -0
- package/dist/training/kg_scanner.d.ts +13 -0
- package/dist/training/kg_scanner.d.ts.map +1 -0
- package/dist/training/kg_scanner.js +118 -0
- package/dist/training/merger.d.ts +15 -0
- package/dist/training/merger.d.ts.map +1 -0
- package/dist/training/merger.js +208 -0
- package/dist/training/scanner.d.ts +36 -0
- package/dist/training/scanner.d.ts.map +1 -0
- package/dist/training/scanner.js +932 -0
- package/dist/training/types.d.ts +117 -0
- package/dist/training/types.d.ts.map +1 -0
- package/dist/training/types.js +9 -0
- package/dist/training/validator.d.ts +21 -0
- package/dist/training/validator.d.ts.map +1 -0
- package/dist/training/validator.js +262 -0
- package/dist/validation/guardrails.d.ts +31 -0
- package/dist/validation/guardrails.d.ts.map +1 -0
- package/dist/validation/guardrails.js +112 -0
- package/dist/validation/output_schema.d.ts +67 -0
- package/dist/validation/output_schema.d.ts.map +1 -0
- package/dist/validation/output_schema.js +84 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +36 -0
- package/package.json +126 -0
- package/schemas/flow-decision.schema.json +83 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +455 -0
- package/schemas/plan.schema.json +491 -0
- package/schemas/route-families.schema.json +137 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { logger } from '../../logger.js';
|
|
7
|
+
import { safeEnv } from '../safe_env.js';
|
|
8
|
+
export function generateSpecsForFindings(findings, config) {
|
|
9
|
+
// Only generate specs for bugs and gaps (not visual/UX issues)
|
|
10
|
+
const actionable = findings.filter((f) => f.type === 'bug' || f.type === 'gap');
|
|
11
|
+
if (actionable.length === 0) {
|
|
12
|
+
logger.info('No actionable findings for spec generation');
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const scenarios = actionable.map((f) => ({
|
|
16
|
+
title: `Verify: ${f.summary}`,
|
|
17
|
+
flow: f.flow,
|
|
18
|
+
steps: f.evidence.reproSteps,
|
|
19
|
+
expected: `The issue "${f.summary}" should not occur`,
|
|
20
|
+
priority: f.severity === 'critical' || f.severity === 'high' ? 'P0' : 'P1',
|
|
21
|
+
}));
|
|
22
|
+
// Write scenarios to a temp file
|
|
23
|
+
const outputDir = config.outputDir || '.e2e-ai-agents';
|
|
24
|
+
mkdirSync(outputDir, { recursive: true });
|
|
25
|
+
const scenariosPath = join(outputDir, 'qa-findings-scenarios.json');
|
|
26
|
+
writeFileSync(scenariosPath, JSON.stringify(scenarios, null, 2), 'utf-8');
|
|
27
|
+
// Call impact-gate generate with the scenarios
|
|
28
|
+
const args = [
|
|
29
|
+
'impact-gate', 'generate',
|
|
30
|
+
'--scenarios', scenariosPath,
|
|
31
|
+
];
|
|
32
|
+
if (config.testsRoot) {
|
|
33
|
+
args.push('--tests-root', config.testsRoot);
|
|
34
|
+
}
|
|
35
|
+
if (config.baseUrl) {
|
|
36
|
+
args.push('--pipeline-base-url', config.baseUrl);
|
|
37
|
+
}
|
|
38
|
+
logger.info('Generating specs for findings', { count: scenarios.length });
|
|
39
|
+
const result = spawnSync('npx', args, {
|
|
40
|
+
cwd: config.testsRoot || process.cwd(),
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
timeout: 300000,
|
|
43
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
44
|
+
env: safeEnv(),
|
|
45
|
+
});
|
|
46
|
+
if (result.status !== 0) {
|
|
47
|
+
logger.warn('Spec generation exited with non-zero', {
|
|
48
|
+
status: result.status,
|
|
49
|
+
stderr: (result.stderr || '').slice(0, 500),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Parse generated spec paths from output
|
|
53
|
+
const generatedPaths = [];
|
|
54
|
+
const lines = (result.stdout || '').split('\n');
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
const match = line.match(/generated.*?:\s*(.+\.spec\.ts)/i);
|
|
57
|
+
if (match) {
|
|
58
|
+
generatedPaths.push(match[1].trim());
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return generatedPaths;
|
|
62
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
export function computeVerdict(phase1, phase2) {
|
|
4
|
+
const findings = phase2.findings;
|
|
5
|
+
const critical = findings.filter((f) => f.severity === 'critical').length;
|
|
6
|
+
const high = findings.filter((f) => f.severity === 'high').length;
|
|
7
|
+
const medium = findings.filter((f) => f.severity === 'medium').length;
|
|
8
|
+
const low = findings.filter((f) => f.severity === 'low' || f.severity === 'info').length;
|
|
9
|
+
// Flow sign-offs
|
|
10
|
+
const flowSignoffs = buildFlowSignoffs(phase1.flows, phase2);
|
|
11
|
+
// Decision logic
|
|
12
|
+
let decision;
|
|
13
|
+
let reason;
|
|
14
|
+
if (critical > 0) {
|
|
15
|
+
decision = 'no-go';
|
|
16
|
+
reason = `${critical} critical finding(s) — must fix before release.`;
|
|
17
|
+
}
|
|
18
|
+
else if (high > 0) {
|
|
19
|
+
decision = 'no-go';
|
|
20
|
+
reason = `${high} high-severity finding(s) — requires triage before release.`;
|
|
21
|
+
}
|
|
22
|
+
else if (medium > 0) {
|
|
23
|
+
decision = 'conditional';
|
|
24
|
+
reason = `${medium} medium-severity finding(s) — review and decide if acceptable.`;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
decision = 'go';
|
|
28
|
+
reason = findings.length === 0
|
|
29
|
+
? 'No issues found across all tested flows.'
|
|
30
|
+
: `Only low/info findings (${low}). Safe to proceed.`;
|
|
31
|
+
}
|
|
32
|
+
// Check for untested flows (P0/P1 not tested → downgrade to conditional)
|
|
33
|
+
const untestedP0P1 = flowSignoffs.filter((s) => s.status === 'not-tested' && phase1.flows.find((f) => f.id === s.flowId && (f.priority === 'P0' || f.priority === 'P1')));
|
|
34
|
+
if (untestedP0P1.length > 0 && decision === 'go') {
|
|
35
|
+
decision = 'conditional';
|
|
36
|
+
reason += ` ${untestedP0P1.length} P0/P1 flow(s) were not tested.`;
|
|
37
|
+
}
|
|
38
|
+
// Check Phase 1 spec failures
|
|
39
|
+
const specFailures = phase1.specResults.reduce((sum, r) => sum + r.failed, 0);
|
|
40
|
+
if (specFailures > 0 && decision === 'go') {
|
|
41
|
+
decision = 'conditional';
|
|
42
|
+
reason += ` ${specFailures} existing spec(s) failed in Phase 1.`;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
decision,
|
|
46
|
+
reason,
|
|
47
|
+
flowSignoffs,
|
|
48
|
+
criticalFindings: critical,
|
|
49
|
+
highFindings: high,
|
|
50
|
+
mediumFindings: medium,
|
|
51
|
+
lowFindings: low,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function buildFlowSignoffs(flows, phase2) {
|
|
55
|
+
return flows.map((flow) => {
|
|
56
|
+
const explored = phase2.flowsExplored.includes(flow.id);
|
|
57
|
+
const flowFindings = phase2.findings.filter((f) => f.flow === flow.id);
|
|
58
|
+
const hasIssues = flowFindings.some((f) => f.type === 'bug' || f.severity === 'critical' || f.severity === 'high');
|
|
59
|
+
return {
|
|
60
|
+
flowId: flow.id,
|
|
61
|
+
flowName: flow.name,
|
|
62
|
+
status: explored ? (hasIssues ? 'failed' : 'passed') : 'not-tested',
|
|
63
|
+
findings: flowFindings.map((f) => f.id),
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/** Build a minimal env for child processes — only forward what's needed. */
|
|
4
|
+
export function safeEnv(extra) {
|
|
5
|
+
const env = {
|
|
6
|
+
PATH: process.env.PATH,
|
|
7
|
+
HOME: process.env.HOME,
|
|
8
|
+
NODE_PATH: process.env.NODE_PATH,
|
|
9
|
+
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
|
|
10
|
+
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
|
11
|
+
LLM_PROVIDER: process.env.LLM_PROVIDER,
|
|
12
|
+
LOG_LEVEL: process.env.LOG_LEVEL,
|
|
13
|
+
// Node needs LANG/LC_ALL for proper string handling
|
|
14
|
+
LANG: process.env.LANG,
|
|
15
|
+
// npm/npx need these
|
|
16
|
+
npm_config_prefix: process.env.npm_config_prefix,
|
|
17
|
+
NVM_DIR: process.env.NVM_DIR,
|
|
18
|
+
NVM_BIN: process.env.NVM_BIN,
|
|
19
|
+
};
|
|
20
|
+
if (extra)
|
|
21
|
+
Object.assign(env, extra);
|
|
22
|
+
return env;
|
|
23
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
function escapeXml(str) {
|
|
4
|
+
return str
|
|
5
|
+
.replace(/&/g, '&')
|
|
6
|
+
.replace(/</g, '<')
|
|
7
|
+
.replace(/>/g, '>')
|
|
8
|
+
.replace(/"/g, '"')
|
|
9
|
+
.replace(/'/g, ''');
|
|
10
|
+
}
|
|
11
|
+
function buildTestCase(tc, flowName) {
|
|
12
|
+
const className = escapeXml(flowName.replace(/\s+/g, '.'));
|
|
13
|
+
const testName = escapeXml(tc.name);
|
|
14
|
+
return ` <testcase classname="${className}" name="${testName}" status="${escapeXml(tc.priority)}">\n` +
|
|
15
|
+
` <properties>\n` +
|
|
16
|
+
` <property name="type" value="${escapeXml(tc.type)}" />\n` +
|
|
17
|
+
` <property name="priority" value="${escapeXml(tc.priority)}" />\n` +
|
|
18
|
+
` </properties>\n` +
|
|
19
|
+
` </testcase>`;
|
|
20
|
+
}
|
|
21
|
+
function buildFailureCase(finding) {
|
|
22
|
+
const name = escapeXml(finding.title);
|
|
23
|
+
return ` <testcase classname="findings" name="${name}">\n` +
|
|
24
|
+
` <failure message="${escapeXml(finding.title)}" type="${escapeXml(finding.severity)}">${escapeXml(finding.description)}</failure>\n` +
|
|
25
|
+
` </testcase>`;
|
|
26
|
+
}
|
|
27
|
+
export const junitReporter = {
|
|
28
|
+
name: 'junit',
|
|
29
|
+
extension: '.xml',
|
|
30
|
+
format(results) {
|
|
31
|
+
const suites = [];
|
|
32
|
+
// Build a lookup from flowName -> test cases
|
|
33
|
+
const designsByFlow = new Map();
|
|
34
|
+
for (const design of results.testDesigns) {
|
|
35
|
+
designsByFlow.set(design.flowName, design.testCases);
|
|
36
|
+
}
|
|
37
|
+
// High-severity findings as failure cases
|
|
38
|
+
const highFindings = results.findings.filter((f) => f.severity === 'high');
|
|
39
|
+
// Each strategy entry becomes a test suite
|
|
40
|
+
for (const entry of results.strategyEntries) {
|
|
41
|
+
const testCases = designsByFlow.get(entry.flowName) ?? [];
|
|
42
|
+
const failures = highFindings.filter((f) => f.title.includes(entry.flowName));
|
|
43
|
+
const totalTests = testCases.length + failures.length;
|
|
44
|
+
const casesXml = testCases.map((tc) => buildTestCase(tc, entry.flowName)).join('\n');
|
|
45
|
+
const failuresXml = failures.map((f) => buildFailureCase(f)).join('\n');
|
|
46
|
+
const allCases = [casesXml, failuresXml].filter(Boolean).join('\n');
|
|
47
|
+
// Warnings as system-out
|
|
48
|
+
const warningsText = results.warnings.length > 0
|
|
49
|
+
? ` <system-out>${escapeXml(results.warnings.join('\n'))}</system-out>`
|
|
50
|
+
: '';
|
|
51
|
+
suites.push(` <testsuite name="${escapeXml(entry.flowName)}" tests="${totalTests}" failures="${failures.length}" ` +
|
|
52
|
+
`id="${escapeXml(entry.flowId)}">\n` +
|
|
53
|
+
` <properties>\n` +
|
|
54
|
+
` <property name="priority" value="${escapeXml(entry.priority)}" />\n` +
|
|
55
|
+
` <property name="approach" value="${escapeXml(entry.approach)}" />\n` +
|
|
56
|
+
` <property name="rationale" value="${escapeXml(entry.rationale)}" />\n` +
|
|
57
|
+
` </properties>\n` +
|
|
58
|
+
(allCases ? allCases + '\n' : '') +
|
|
59
|
+
(warningsText ? warningsText + '\n' : '') +
|
|
60
|
+
` </testsuite>`);
|
|
61
|
+
}
|
|
62
|
+
// Remaining high findings not tied to a strategy entry
|
|
63
|
+
const coveredFlowNames = new Set(results.strategyEntries.map((e) => e.flowName));
|
|
64
|
+
const uncoveredFindings = highFindings.filter((f) => !Array.from(coveredFlowNames).some((name) => f.title.includes(name)));
|
|
65
|
+
if (uncoveredFindings.length > 0) {
|
|
66
|
+
const failureCases = uncoveredFindings.map((f) => buildFailureCase(f)).join('\n');
|
|
67
|
+
suites.push(` <testsuite name="findings" tests="${uncoveredFindings.length}" failures="${uncoveredFindings.length}">\n` +
|
|
68
|
+
failureCases + '\n' +
|
|
69
|
+
` </testsuite>`);
|
|
70
|
+
}
|
|
71
|
+
const totalTests = results.testDesigns.reduce((sum, d) => sum + d.testCases.length, 0) + highFindings.length;
|
|
72
|
+
const totalFailures = highFindings.length;
|
|
73
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n` +
|
|
74
|
+
`<testsuites name="impact-gate: ${escapeXml(results.workflow)}" ` +
|
|
75
|
+
`tests="${totalTests}" failures="${totalFailures}" ` +
|
|
76
|
+
`time="0">\n` +
|
|
77
|
+
` <properties>\n` +
|
|
78
|
+
` <property name="changedFiles" value="${results.changedFiles}" />\n` +
|
|
79
|
+
` <property name="impactedFlows" value="${results.impactedFlows}" />\n` +
|
|
80
|
+
` <property name="cost" value="${results.cost}" />\n` +
|
|
81
|
+
` <property name="tokens" value="${results.tokens}" />\n` +
|
|
82
|
+
` </properties>\n` +
|
|
83
|
+
suites.join('\n') + '\n' +
|
|
84
|
+
`</testsuites>\n`;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { getVersion } from '../version.js';
|
|
4
|
+
function severityToLevel(severity) {
|
|
5
|
+
switch (severity.toLowerCase()) {
|
|
6
|
+
case 'high':
|
|
7
|
+
case 'critical':
|
|
8
|
+
return 'error';
|
|
9
|
+
case 'medium':
|
|
10
|
+
return 'warning';
|
|
11
|
+
case 'low':
|
|
12
|
+
case 'info':
|
|
13
|
+
return 'note';
|
|
14
|
+
default:
|
|
15
|
+
return 'note';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function riskToLevel(risk) {
|
|
19
|
+
switch (risk.toLowerCase()) {
|
|
20
|
+
case 'high':
|
|
21
|
+
return 'warning';
|
|
22
|
+
case 'medium':
|
|
23
|
+
return 'note';
|
|
24
|
+
default:
|
|
25
|
+
return 'none';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const sarifReporter = {
|
|
29
|
+
name: 'sarif',
|
|
30
|
+
extension: '.sarif',
|
|
31
|
+
format(results) {
|
|
32
|
+
const rules = [];
|
|
33
|
+
const sarifResults = [];
|
|
34
|
+
const ruleIds = new Set();
|
|
35
|
+
function ensureRule(id, description, level) {
|
|
36
|
+
if (!ruleIds.has(id)) {
|
|
37
|
+
ruleIds.add(id);
|
|
38
|
+
rules.push({
|
|
39
|
+
id,
|
|
40
|
+
shortDescription: { text: description },
|
|
41
|
+
defaultConfiguration: { level },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Findings -> results
|
|
46
|
+
for (const finding of results.findings) {
|
|
47
|
+
const level = severityToLevel(finding.severity);
|
|
48
|
+
const ruleId = `finding/${finding.severity}`;
|
|
49
|
+
ensureRule(ruleId, `Finding (${finding.severity})`, level);
|
|
50
|
+
sarifResults.push({
|
|
51
|
+
ruleId,
|
|
52
|
+
level,
|
|
53
|
+
message: { text: `${finding.title}: ${finding.description}` },
|
|
54
|
+
properties: {
|
|
55
|
+
severity: finding.severity,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Strategy entries without matching test designs -> coverage gap results
|
|
60
|
+
const designedFlows = new Set(results.testDesigns.map((d) => d.flowName));
|
|
61
|
+
for (const entry of results.strategyEntries) {
|
|
62
|
+
if (!designedFlows.has(entry.flowName)) {
|
|
63
|
+
const ruleId = 'coverage/gap';
|
|
64
|
+
ensureRule(ruleId, 'Missing test coverage for impacted flow', 'warning');
|
|
65
|
+
sarifResults.push({
|
|
66
|
+
ruleId,
|
|
67
|
+
level: 'warning',
|
|
68
|
+
message: {
|
|
69
|
+
text: `Flow "${entry.flowName}" (${entry.flowId}) has strategy but no test design. ` +
|
|
70
|
+
`Priority: ${entry.priority}, approach: ${entry.approach}.`,
|
|
71
|
+
},
|
|
72
|
+
properties: {
|
|
73
|
+
flowId: entry.flowId,
|
|
74
|
+
priority: entry.priority,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// High-risk cross-impacts -> warning results
|
|
80
|
+
for (const impact of results.crossImpacts) {
|
|
81
|
+
const level = riskToLevel(impact.riskLevel);
|
|
82
|
+
if (level === 'none') {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const ruleId = `cross-impact/${impact.riskLevel}`;
|
|
86
|
+
ensureRule(ruleId, `Cross-impact (${impact.riskLevel} risk)`, level);
|
|
87
|
+
sarifResults.push({
|
|
88
|
+
ruleId,
|
|
89
|
+
level,
|
|
90
|
+
message: {
|
|
91
|
+
text: `Cross-impact: "${impact.sourceFamily}" affects "${impact.affectedFamily}" ` +
|
|
92
|
+
`with ${impact.riskLevel} risk.`,
|
|
93
|
+
},
|
|
94
|
+
properties: {
|
|
95
|
+
sourceFamily: impact.sourceFamily,
|
|
96
|
+
affectedFamily: impact.affectedFamily,
|
|
97
|
+
riskLevel: impact.riskLevel,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const run = {
|
|
102
|
+
tool: {
|
|
103
|
+
driver: {
|
|
104
|
+
name: 'impact-gate',
|
|
105
|
+
version: getVersion(),
|
|
106
|
+
informationUri: 'https://github.com/yasserfaraazkhan/impact-gate',
|
|
107
|
+
rules,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
results: sarifResults,
|
|
111
|
+
invocations: [
|
|
112
|
+
{
|
|
113
|
+
executionSuccessful: true,
|
|
114
|
+
properties: {
|
|
115
|
+
workflow: results.workflow,
|
|
116
|
+
changedFiles: results.changedFiles,
|
|
117
|
+
impactedFlows: results.impactedFlows,
|
|
118
|
+
cost: results.cost,
|
|
119
|
+
tokens: results.tokens,
|
|
120
|
+
warnings: results.warnings,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
const sarif = {
|
|
126
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json',
|
|
127
|
+
version: '2.1.0',
|
|
128
|
+
runs: [run],
|
|
129
|
+
};
|
|
130
|
+
return JSON.stringify(sarif, null, 2) + '\n';
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
const DEFAULT_CONFIG = {
|
|
4
|
+
failureThreshold: 3,
|
|
5
|
+
cooldownMs: 60000,
|
|
6
|
+
};
|
|
7
|
+
export class CircuitBreaker {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
this.state = 'closed';
|
|
10
|
+
this.failures = 0;
|
|
11
|
+
this.lastFailureTime = 0;
|
|
12
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
13
|
+
}
|
|
14
|
+
/** Returns the derived state without mutating internal state. */
|
|
15
|
+
get currentState() {
|
|
16
|
+
if (this.state === 'open' && Date.now() - this.lastFailureTime >= this.config.cooldownMs) {
|
|
17
|
+
return 'half-open';
|
|
18
|
+
}
|
|
19
|
+
return this.state;
|
|
20
|
+
}
|
|
21
|
+
get isOpen() {
|
|
22
|
+
return this.currentState === 'open';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Execute a function with circuit breaker protection.
|
|
26
|
+
* If the circuit is open, the fallback is called instead.
|
|
27
|
+
*/
|
|
28
|
+
async call(fn, fallback) {
|
|
29
|
+
// Transition from open to half-open if cooldown has elapsed
|
|
30
|
+
if (this.state === 'open') {
|
|
31
|
+
if (Date.now() - this.lastFailureTime >= this.config.cooldownMs) {
|
|
32
|
+
this.state = 'half-open';
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return fallback();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// At this point state is 'closed' or 'half-open'
|
|
39
|
+
const stateBeforeCall = this.state;
|
|
40
|
+
try {
|
|
41
|
+
const result = await fn();
|
|
42
|
+
this.onSuccess();
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const shouldCount = !this.config.shouldCount || this.config.shouldCount(error);
|
|
47
|
+
if (shouldCount) {
|
|
48
|
+
this.onFailure();
|
|
49
|
+
}
|
|
50
|
+
// In half-open state, a failure re-opens the circuit
|
|
51
|
+
if (stateBeforeCall === 'half-open') {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
// In closed state, if failures hit threshold the circuit opened
|
|
55
|
+
if (shouldCount && this.failures >= this.config.failureThreshold) {
|
|
56
|
+
return fallback();
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
onSuccess() {
|
|
62
|
+
this.failures = 0;
|
|
63
|
+
this.state = 'closed';
|
|
64
|
+
}
|
|
65
|
+
onFailure() {
|
|
66
|
+
this.failures++;
|
|
67
|
+
this.lastFailureTime = Date.now();
|
|
68
|
+
if (this.failures >= this.config.failureThreshold) {
|
|
69
|
+
this.state = 'open';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Reset the circuit breaker to closed state */
|
|
73
|
+
reset() {
|
|
74
|
+
this.state = 'closed';
|
|
75
|
+
this.failures = 0;
|
|
76
|
+
this.lastFailureTime = 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
const DEFAULT_RETRY_CONFIG = {
|
|
4
|
+
maxRetries: 2,
|
|
5
|
+
baseDelayMs: 1000,
|
|
6
|
+
maxDelayMs: 10000,
|
|
7
|
+
jitter: true,
|
|
8
|
+
};
|
|
9
|
+
/** Errors that should be retried (transient failures) */
|
|
10
|
+
function isRetryable(error) {
|
|
11
|
+
if (!(error instanceof Error))
|
|
12
|
+
return false;
|
|
13
|
+
const msg = error.message.toLowerCase();
|
|
14
|
+
// Rate limits
|
|
15
|
+
if (msg.includes('rate limit') || msg.includes('429') || msg.includes('too many requests'))
|
|
16
|
+
return true;
|
|
17
|
+
// Server errors
|
|
18
|
+
if (msg.includes('500') || msg.includes('502') || msg.includes('503') || msg.includes('504'))
|
|
19
|
+
return true;
|
|
20
|
+
if (msg.includes('internal server error') || msg.includes('bad gateway') || msg.includes('service unavailable'))
|
|
21
|
+
return true;
|
|
22
|
+
// Network errors
|
|
23
|
+
if (msg.includes('econnreset') || msg.includes('econnrefused') || msg.includes('etimedout'))
|
|
24
|
+
return true;
|
|
25
|
+
if (msg.includes('socket hang up') || msg.includes('network error'))
|
|
26
|
+
return true;
|
|
27
|
+
// Overloaded
|
|
28
|
+
if (msg.includes('overloaded') || msg.includes('capacity'))
|
|
29
|
+
return true;
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
function computeDelay(attempt, config) {
|
|
33
|
+
const exponential = Math.min(config.baseDelayMs * Math.pow(2, attempt), config.maxDelayMs);
|
|
34
|
+
if (!config.jitter)
|
|
35
|
+
return exponential;
|
|
36
|
+
// Full jitter: random between 0 and exponential
|
|
37
|
+
return Math.floor(Math.random() * exponential);
|
|
38
|
+
}
|
|
39
|
+
export async function withRetry(fn, config = {}) {
|
|
40
|
+
const cfg = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
41
|
+
let lastError;
|
|
42
|
+
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
|
|
43
|
+
try {
|
|
44
|
+
return await fn();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
lastError = error;
|
|
48
|
+
if (attempt >= cfg.maxRetries || !isRetryable(error)) {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
const delay = computeDelay(attempt, cfg);
|
|
52
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw lastError;
|
|
56
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Secret scanning and sanitization utilities.
|
|
5
|
+
* Prevents API keys and credentials from leaking into artifacts, logs, and output.
|
|
6
|
+
*
|
|
7
|
+
* Patterns are stored WITHOUT the global flag to avoid shared mutable lastIndex state.
|
|
8
|
+
* New RegExp instances with /g are created per call for safe concurrent usage.
|
|
9
|
+
*/
|
|
10
|
+
const SECRET_PATTERNS = [
|
|
11
|
+
// Anthropic API keys (must be checked before generic sk- pattern)
|
|
12
|
+
/sk-ant-[a-zA-Z0-9_-]{20,}/,
|
|
13
|
+
// OpenAI API keys (negative lookahead to avoid matching Anthropic keys)
|
|
14
|
+
/sk-(?!ant-)[a-zA-Z0-9]{20,}/,
|
|
15
|
+
// Generic API key patterns
|
|
16
|
+
/(?:api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token)['":\s=]+['"]?([a-zA-Z0-9_\-./]{20,})['"]?/i,
|
|
17
|
+
// Bearer tokens
|
|
18
|
+
/Bearer\s+[a-zA-Z0-9_\-./]{20,}/,
|
|
19
|
+
// AWS keys
|
|
20
|
+
/AKIA[0-9A-Z]{16}/,
|
|
21
|
+
// GitHub tokens
|
|
22
|
+
/gh[ps]_[a-zA-Z0-9]{36,}/,
|
|
23
|
+
/github_pat_[a-zA-Z0-9_]{22,}/,
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize a string by replacing detected secrets with [REDACTED].
|
|
27
|
+
*/
|
|
28
|
+
export function sanitizeSecrets(text) {
|
|
29
|
+
let result = text;
|
|
30
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
31
|
+
result = result.replace(new RegExp(pattern, 'gi'), '[REDACTED]');
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if a string contains any detectable secrets.
|
|
37
|
+
*/
|
|
38
|
+
export function containsSecrets(text) {
|
|
39
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
40
|
+
if (new RegExp(pattern, 'i').test(text))
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Deep-sanitize a JSON-serializable object.
|
|
47
|
+
* Recursively walks all string values and sanitizes them.
|
|
48
|
+
* Tracks seen objects to prevent stack overflow on circular references.
|
|
49
|
+
*/
|
|
50
|
+
export function sanitizeObject(obj, _seen) {
|
|
51
|
+
if (typeof obj === 'string')
|
|
52
|
+
return sanitizeSecrets(obj);
|
|
53
|
+
if (obj === null || typeof obj !== 'object')
|
|
54
|
+
return obj;
|
|
55
|
+
const seen = _seen ?? new WeakSet();
|
|
56
|
+
if (seen.has(obj))
|
|
57
|
+
return '[Circular]';
|
|
58
|
+
seen.add(obj);
|
|
59
|
+
if (Array.isArray(obj))
|
|
60
|
+
return obj.map((item) => sanitizeObject(item, seen));
|
|
61
|
+
const result = {};
|
|
62
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
63
|
+
result[key] = sanitizeObject(value, seen);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|