@yasserkhanorg/e2e-agents 1.8.5 → 1.10.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/README.md +95 -8
- 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/config.d.ts +1 -1
- package/dist/agent/config.d.ts.map +1 -1
- package/dist/agent/git.d.ts +1 -0
- package/dist/agent/git.d.ts.map +1 -1
- package/dist/agent/git.js +3 -0
- package/dist/agentic/fix_loop.d.ts.map +1 -1
- package/dist/agentic/fix_loop.js +5 -4
- package/dist/agentic/runner.d.ts +2 -0
- package/dist/agentic/runner.d.ts.map +1 -1
- package/dist/agentic/runner.js +15 -12
- package/dist/agents/cross-impact.d.ts.map +1 -1
- package/dist/agents/cross-impact.js +6 -1
- package/dist/agents/executor.d.ts.map +1 -1
- package/dist/agents/executor.js +6 -1
- package/dist/agents/strategist.d.ts.map +1 -1
- package/dist/agents/strategist.js +6 -1
- package/dist/agents/test-designer.d.ts.map +1 -1
- package/dist/agents/test-designer.js +6 -1
- package/dist/anthropic_provider.d.ts.map +1 -1
- package/dist/anthropic_provider.js +1 -0
- package/dist/base_provider.d.ts +56 -0
- package/dist/base_provider.d.ts.map +1 -1
- package/dist/base_provider.js +123 -1
- 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 +45 -0
- package/dist/cache/cached_provider.d.ts.map +1 -0
- package/dist/cache/cached_provider.js +88 -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/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.map +1 -1
- package/dist/cli/commands/crew.js +118 -1
- 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 +86 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -62
- package/dist/cli/commands/train.d.ts.map +1 -1
- package/dist/cli/commands/train.js +16 -21
- package/dist/cli/defaults.d.ts +35 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +125 -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.map +1 -1
- package/dist/cli/parse_args.js +24 -2
- package/dist/cli/types.d.ts +7 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +47 -2
- package/dist/crew/context.d.ts +15 -0
- package/dist/crew/context.d.ts.map +1 -1
- package/dist/crew/orchestrator.d.ts +14 -0
- package/dist/crew/orchestrator.d.ts.map +1 -1
- package/dist/crew/orchestrator.js +162 -4
- package/dist/crew/protocol.d.ts +13 -0
- package/dist/crew/protocol.d.ts.map +1 -1
- package/dist/crew/provider.d.ts +15 -1
- package/dist/crew/provider.d.ts.map +1 -1
- package/dist/crew/provider.js +24 -4
- package/dist/custom_provider.d.ts.map +1 -1
- package/dist/custom_provider.js +1 -0
- package/dist/engine/diff_loader.d.ts.map +1 -1
- package/dist/engine/diff_loader.js +3 -14
- package/dist/engine/impact_engine.d.ts.map +1 -1
- package/dist/engine/impact_engine.js +9 -23
- 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/git.js +3 -1
- package/dist/esm/agentic/fix_loop.js +5 -4
- package/dist/esm/agentic/runner.js +15 -12
- package/dist/esm/agents/cross-impact.js +6 -1
- package/dist/esm/agents/executor.js +6 -1
- package/dist/esm/agents/strategist.js +6 -1
- package/dist/esm/agents/test-designer.js +6 -1
- package/dist/esm/anthropic_provider.js +1 -0
- package/dist/esm/base_provider.js +121 -0
- package/dist/esm/budget_ledger.js +58 -0
- package/dist/esm/cache/cached_provider.js +82 -0
- package/dist/esm/cache/response_cache.js +140 -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 +118 -1
- package/dist/esm/cli/commands/gate.js +83 -0
- package/dist/esm/cli/commands/init.js +3 -58
- package/dist/esm/cli/commands/train.js +16 -21
- package/dist/esm/cli/defaults.js +118 -0
- package/dist/esm/cli/errors.js +52 -0
- package/dist/esm/cli/parse_args.js +24 -2
- package/dist/esm/cli.js +47 -2
- package/dist/esm/crew/orchestrator.js +162 -4
- package/dist/esm/crew/provider.js +24 -4
- package/dist/esm/custom_provider.js +1 -0
- package/dist/esm/engine/diff_loader.js +1 -12
- package/dist/esm/engine/impact_engine.js +9 -23
- package/dist/esm/index.js +21 -0
- package/dist/esm/knowledge/api_surface.js +265 -34
- package/dist/esm/knowledge/cluster_utils.js +60 -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 +119 -0
- package/dist/esm/mcp-server.js +2 -4
- package/dist/esm/metrics/prometheus.js +149 -0
- package/dist/esm/model_router.js +59 -0
- package/dist/esm/ollama_provider.js +1 -0
- package/dist/esm/openai_provider.js +1 -0
- package/dist/esm/pipeline/orchestrator.js +6 -12
- package/dist/esm/pipeline/stage0_preprocess.js +12 -19
- package/dist/esm/pipeline/stage1_impact.js +19 -3
- package/dist/esm/pipeline/stage2_coverage.js +29 -7
- package/dist/esm/pipeline/stage3_generation.js +21 -1
- package/dist/esm/progress.js +112 -0
- package/dist/esm/prompts/coverage.js +17 -24
- package/dist/esm/prompts/cross-impact.js +3 -21
- package/dist/esm/prompts/generation.js +201 -45
- package/dist/esm/prompts/generation_profile.js +147 -0
- package/dist/esm/prompts/heal.js +33 -15
- package/dist/esm/prompts/impact.js +3 -22
- package/dist/esm/prompts/json_extract.js +36 -0
- package/dist/esm/prompts/strategist.js +2 -20
- package/dist/esm/prompts/test-designer.js +6 -21
- package/dist/esm/provider_factory.js +6 -4
- package/dist/esm/reporters/junit.js +86 -0
- package/dist/esm/reporters/reporter.js +3 -0
- package/dist/esm/reporters/sarif.js +131 -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/kg_scanner.js +115 -0
- package/dist/esm/training/scanner.js +27 -34
- package/dist/esm/validation/guardrails.js +5 -0
- package/dist/esm/version.js +33 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -1
- package/dist/knowledge/api_surface.d.ts +12 -0
- package/dist/knowledge/api_surface.d.ts.map +1 -1
- package/dist/knowledge/api_surface.js +268 -34
- 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/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 +29 -0
- package/dist/knowledge/route_families.d.ts.map +1 -1
- package/dist/knowledge/route_families.js +122 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +2 -4
- 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.map +1 -1
- package/dist/ollama_provider.js +1 -0
- package/dist/openai_provider.d.ts.map +1 -1
- package/dist/openai_provider.js +1 -0
- package/dist/pipeline/orchestrator.d.ts +2 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +6 -12
- package/dist/pipeline/stage0_preprocess.d.ts.map +1 -1
- package/dist/pipeline/stage0_preprocess.js +11 -18
- package/dist/pipeline/stage1_impact.d.ts +1 -1
- package/dist/pipeline/stage1_impact.d.ts.map +1 -1
- package/dist/pipeline/stage1_impact.js +18 -2
- package/dist/pipeline/stage2_coverage.d.ts +2 -0
- package/dist/pipeline/stage2_coverage.d.ts.map +1 -1
- package/dist/pipeline/stage2_coverage.js +29 -7
- package/dist/pipeline/stage3_generation.d.ts +2 -0
- package/dist/pipeline/stage3_generation.d.ts.map +1 -1
- package/dist/pipeline/stage3_generation.js +21 -1
- package/dist/pipeline/stage4_heal.d.ts +2 -0
- package/dist/pipeline/stage4_heal.d.ts.map +1 -1
- 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 +2 -0
- package/dist/prompts/coverage.d.ts.map +1 -1
- package/dist/prompts/coverage.js +17 -24
- package/dist/prompts/cross-impact.d.ts +1 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -1
- package/dist/prompts/cross-impact.js +3 -21
- package/dist/prompts/generation.d.ts +4 -2
- package/dist/prompts/generation.d.ts.map +1 -1
- package/dist/prompts/generation.js +201 -45
- 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 +3 -1
- package/dist/prompts/heal.d.ts.map +1 -1
- package/dist/prompts/heal.js +33 -15
- package/dist/prompts/impact.d.ts +1 -0
- package/dist/prompts/impact.d.ts.map +1 -1
- package/dist/prompts/impact.js +3 -22
- 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.map +1 -1
- package/dist/prompts/strategist.js +2 -20
- package/dist/prompts/test-designer.d.ts +2 -0
- package/dist/prompts/test-designer.d.ts.map +1 -1
- package/dist/prompts/test-designer.js +6 -21
- package/dist/provider_factory.d.ts.map +1 -1
- package/dist/provider_factory.js +6 -4
- 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 +134 -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/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/scanner.d.ts +7 -2
- package/dist/training/scanner.d.ts.map +1 -1
- package/dist/training/scanner.js +27 -34
- package/dist/validation/guardrails.d.ts +2 -0
- package/dist/validation/guardrails.d.ts.map +1 -1
- package/dist/validation/guardrails.js +5 -0
- package/dist/validation/output_schema.d.ts +3 -0
- package/dist/validation/output_schema.d.ts.map +1 -1
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +36 -0
- package/package.json +7 -2
- package/schemas/route-families.schema.json +31 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.CircuitBreaker = void 0;
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
failureThreshold: 3,
|
|
8
|
+
cooldownMs: 60000,
|
|
9
|
+
};
|
|
10
|
+
class CircuitBreaker {
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
this.state = 'closed';
|
|
13
|
+
this.failures = 0;
|
|
14
|
+
this.lastFailureTime = 0;
|
|
15
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
16
|
+
}
|
|
17
|
+
/** Returns the derived state without mutating internal state. */
|
|
18
|
+
get currentState() {
|
|
19
|
+
if (this.state === 'open' && Date.now() - this.lastFailureTime >= this.config.cooldownMs) {
|
|
20
|
+
return 'half-open';
|
|
21
|
+
}
|
|
22
|
+
return this.state;
|
|
23
|
+
}
|
|
24
|
+
get isOpen() {
|
|
25
|
+
return this.currentState === 'open';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Execute a function with circuit breaker protection.
|
|
29
|
+
* If the circuit is open, the fallback is called instead.
|
|
30
|
+
*/
|
|
31
|
+
async call(fn, fallback) {
|
|
32
|
+
// Transition from open to half-open if cooldown has elapsed
|
|
33
|
+
if (this.state === 'open') {
|
|
34
|
+
if (Date.now() - this.lastFailureTime >= this.config.cooldownMs) {
|
|
35
|
+
this.state = 'half-open';
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return fallback();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// At this point state is 'closed' or 'half-open'
|
|
42
|
+
const stateBeforeCall = this.state;
|
|
43
|
+
try {
|
|
44
|
+
const result = await fn();
|
|
45
|
+
this.onSuccess();
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const shouldCount = !this.config.shouldCount || this.config.shouldCount(error);
|
|
50
|
+
if (shouldCount) {
|
|
51
|
+
this.onFailure();
|
|
52
|
+
}
|
|
53
|
+
// In half-open state, a failure re-opens the circuit
|
|
54
|
+
if (stateBeforeCall === 'half-open') {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
// In closed state, if failures hit threshold the circuit opened
|
|
58
|
+
if (shouldCount && this.failures >= this.config.failureThreshold) {
|
|
59
|
+
return fallback();
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
onSuccess() {
|
|
65
|
+
this.failures = 0;
|
|
66
|
+
this.state = 'closed';
|
|
67
|
+
}
|
|
68
|
+
onFailure() {
|
|
69
|
+
this.failures++;
|
|
70
|
+
this.lastFailureTime = Date.now();
|
|
71
|
+
if (this.failures >= this.config.failureThreshold) {
|
|
72
|
+
this.state = 'open';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Reset the circuit breaker to closed state */
|
|
76
|
+
reset() {
|
|
77
|
+
this.state = 'closed';
|
|
78
|
+
this.failures = 0;
|
|
79
|
+
this.lastFailureTime = 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry with exponential backoff and jitter for LLM provider calls.
|
|
3
|
+
*/
|
|
4
|
+
export interface RetryConfig {
|
|
5
|
+
maxRetries: number;
|
|
6
|
+
baseDelayMs: number;
|
|
7
|
+
maxDelayMs: number;
|
|
8
|
+
jitter: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function withRetry<T>(fn: () => Promise<T>, config?: Partial<RetryConfig>): Promise<T>;
|
|
11
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/resilience/retry.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,MAAM,WAAW,WAAW;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACnB;AAsCD,wBAAsB,SAAS,CAAC,CAAC,EAC7B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAClC,OAAO,CAAC,CAAC,CAAC,CAkBZ"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.withRetry = withRetry;
|
|
6
|
+
const DEFAULT_RETRY_CONFIG = {
|
|
7
|
+
maxRetries: 2,
|
|
8
|
+
baseDelayMs: 1000,
|
|
9
|
+
maxDelayMs: 10000,
|
|
10
|
+
jitter: true,
|
|
11
|
+
};
|
|
12
|
+
/** Errors that should be retried (transient failures) */
|
|
13
|
+
function isRetryable(error) {
|
|
14
|
+
if (!(error instanceof Error))
|
|
15
|
+
return false;
|
|
16
|
+
const msg = error.message.toLowerCase();
|
|
17
|
+
// Rate limits
|
|
18
|
+
if (msg.includes('rate limit') || msg.includes('429') || msg.includes('too many requests'))
|
|
19
|
+
return true;
|
|
20
|
+
// Server errors
|
|
21
|
+
if (msg.includes('500') || msg.includes('502') || msg.includes('503') || msg.includes('504'))
|
|
22
|
+
return true;
|
|
23
|
+
if (msg.includes('internal server error') || msg.includes('bad gateway') || msg.includes('service unavailable'))
|
|
24
|
+
return true;
|
|
25
|
+
// Network errors
|
|
26
|
+
if (msg.includes('econnreset') || msg.includes('econnrefused') || msg.includes('etimedout'))
|
|
27
|
+
return true;
|
|
28
|
+
if (msg.includes('socket hang up') || msg.includes('network error'))
|
|
29
|
+
return true;
|
|
30
|
+
// Overloaded
|
|
31
|
+
if (msg.includes('overloaded') || msg.includes('capacity'))
|
|
32
|
+
return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function computeDelay(attempt, config) {
|
|
36
|
+
const exponential = Math.min(config.baseDelayMs * Math.pow(2, attempt), config.maxDelayMs);
|
|
37
|
+
if (!config.jitter)
|
|
38
|
+
return exponential;
|
|
39
|
+
// Full jitter: random between 0 and exponential
|
|
40
|
+
return Math.floor(Math.random() * exponential);
|
|
41
|
+
}
|
|
42
|
+
async function withRetry(fn, config = {}) {
|
|
43
|
+
const cfg = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
44
|
+
let lastError;
|
|
45
|
+
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
|
|
46
|
+
try {
|
|
47
|
+
return await fn();
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
lastError = error;
|
|
51
|
+
if (attempt >= cfg.maxRetries || !isRetryable(error)) {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
const delay = computeDelay(attempt, cfg);
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw lastError;
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize a string by replacing detected secrets with [REDACTED].
|
|
3
|
+
*/
|
|
4
|
+
export declare function sanitizeSecrets(text: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Check if a string contains any detectable secrets.
|
|
7
|
+
*/
|
|
8
|
+
export declare function containsSecrets(text: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Deep-sanitize a JSON-serializable object.
|
|
11
|
+
* Recursively walks all string values and sanitizes them.
|
|
12
|
+
* Tracks seen objects to prevent stack overflow on circular references.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sanitizeObject<T>(obj: T, _seen?: WeakSet<object>): T;
|
|
15
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AA2BA;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKrD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAepE"}
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.sanitizeSecrets = sanitizeSecrets;
|
|
6
|
+
exports.containsSecrets = containsSecrets;
|
|
7
|
+
exports.sanitizeObject = sanitizeObject;
|
|
8
|
+
/**
|
|
9
|
+
* Secret scanning and sanitization utilities.
|
|
10
|
+
* Prevents API keys and credentials from leaking into artifacts, logs, and output.
|
|
11
|
+
*
|
|
12
|
+
* Patterns are stored WITHOUT the global flag to avoid shared mutable lastIndex state.
|
|
13
|
+
* New RegExp instances with /g are created per call for safe concurrent usage.
|
|
14
|
+
*/
|
|
15
|
+
const SECRET_PATTERNS = [
|
|
16
|
+
// Anthropic API keys (must be checked before generic sk- pattern)
|
|
17
|
+
/sk-ant-[a-zA-Z0-9_-]{20,}/,
|
|
18
|
+
// OpenAI API keys (negative lookahead to avoid matching Anthropic keys)
|
|
19
|
+
/sk-(?!ant-)[a-zA-Z0-9]{20,}/,
|
|
20
|
+
// Generic API key patterns
|
|
21
|
+
/(?:api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token)['":\s=]+['"]?([a-zA-Z0-9_\-./]{20,})['"]?/i,
|
|
22
|
+
// Bearer tokens
|
|
23
|
+
/Bearer\s+[a-zA-Z0-9_\-./]{20,}/,
|
|
24
|
+
// AWS keys
|
|
25
|
+
/AKIA[0-9A-Z]{16}/,
|
|
26
|
+
// GitHub tokens
|
|
27
|
+
/gh[ps]_[a-zA-Z0-9]{36,}/,
|
|
28
|
+
/github_pat_[a-zA-Z0-9_]{22,}/,
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Sanitize a string by replacing detected secrets with [REDACTED].
|
|
32
|
+
*/
|
|
33
|
+
function sanitizeSecrets(text) {
|
|
34
|
+
let result = text;
|
|
35
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
36
|
+
result = result.replace(new RegExp(pattern, 'gi'), '[REDACTED]');
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a string contains any detectable secrets.
|
|
42
|
+
*/
|
|
43
|
+
function containsSecrets(text) {
|
|
44
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
45
|
+
if (new RegExp(pattern, 'i').test(text))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Deep-sanitize a JSON-serializable object.
|
|
52
|
+
* Recursively walks all string values and sanitizes them.
|
|
53
|
+
* Tracks seen objects to prevent stack overflow on circular references.
|
|
54
|
+
*/
|
|
55
|
+
function sanitizeObject(obj, _seen) {
|
|
56
|
+
if (typeof obj === 'string')
|
|
57
|
+
return sanitizeSecrets(obj);
|
|
58
|
+
if (obj === null || typeof obj !== 'object')
|
|
59
|
+
return obj;
|
|
60
|
+
const seen = _seen ?? new WeakSet();
|
|
61
|
+
if (seen.has(obj))
|
|
62
|
+
return '[Circular]';
|
|
63
|
+
seen.add(obj);
|
|
64
|
+
if (Array.isArray(obj))
|
|
65
|
+
return obj.map((item) => sanitizeObject(item, seen));
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
68
|
+
result[key] = sanitizeObject(value, seen);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts an Understand-Anything knowledge graph into the same ScanResult
|
|
3
|
+
* format produced by the filesystem scanner. This allows the existing
|
|
4
|
+
* merge/enrich/validate pipeline to work unchanged with KG input.
|
|
5
|
+
*/
|
|
6
|
+
import type { KnowledgeGraph } from '../knowledge/kg_types.js';
|
|
7
|
+
import type { ScanResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Converts KG nodes/edges into a ScanResult compatible with the filesystem scanner output.
|
|
10
|
+
* Groups nodes by their containing module/directory to form families.
|
|
11
|
+
*/
|
|
12
|
+
export declare function scanFromKnowledgeGraph(kg: KnowledgeGraph): ScanResult;
|
|
13
|
+
//# sourceMappingURL=kg_scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kg_scanner.d.ts","sourceRoot":"","sources":["../../src/training/kg_scanner.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAS,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAC,UAAU,EAAgC,MAAM,YAAY,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,cAAc,GAAG,UAAU,CA4FrE"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.scanFromKnowledgeGraph = scanFromKnowledgeGraph;
|
|
6
|
+
const cluster_utils_js_1 = require("../knowledge/cluster_utils.js");
|
|
7
|
+
/**
|
|
8
|
+
* Converts KG nodes/edges into a ScanResult compatible with the filesystem scanner output.
|
|
9
|
+
* Groups nodes by their containing module/directory to form families.
|
|
10
|
+
*/
|
|
11
|
+
function scanFromKnowledgeGraph(kg) {
|
|
12
|
+
const clusters = new Map();
|
|
13
|
+
// Group nodes into clusters by directory/module
|
|
14
|
+
for (const node of kg.nodes) {
|
|
15
|
+
if (node.layer === 'infra')
|
|
16
|
+
continue; // skip infrastructure nodes
|
|
17
|
+
const clusterId = (0, cluster_utils_js_1.deriveClusterId)(node, cluster_utils_js_1.SKIP_DIRS_WITH_TESTS);
|
|
18
|
+
if (!clusterId)
|
|
19
|
+
continue;
|
|
20
|
+
if (!clusters.has(clusterId)) {
|
|
21
|
+
clusters.set(clusterId, []);
|
|
22
|
+
}
|
|
23
|
+
clusters.get(clusterId).push(node);
|
|
24
|
+
}
|
|
25
|
+
let totalSourceFiles = 0;
|
|
26
|
+
let totalTestFiles = 0;
|
|
27
|
+
const families = [];
|
|
28
|
+
for (const [id, nodes] of clusters) {
|
|
29
|
+
const webappPaths = [];
|
|
30
|
+
const serverPaths = [];
|
|
31
|
+
const specDirs = [];
|
|
32
|
+
const tags = [];
|
|
33
|
+
const seenDirs = new Set();
|
|
34
|
+
for (const node of nodes) {
|
|
35
|
+
if (!node.filePath)
|
|
36
|
+
continue;
|
|
37
|
+
const normalized = node.filePath.replace(/\\/g, '/');
|
|
38
|
+
if (node.layer === 'test') {
|
|
39
|
+
totalTestFiles++;
|
|
40
|
+
const dir = normalized.split('/').slice(0, -1).join('/');
|
|
41
|
+
if (dir && !seenDirs.has(dir)) {
|
|
42
|
+
seenDirs.add(dir);
|
|
43
|
+
specDirs.push(dir + '/');
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
totalSourceFiles++;
|
|
48
|
+
const glob = buildGlobFromPath(normalized);
|
|
49
|
+
if (node.layer === 'api' || node.layer === 'service' || node.layer === 'data') {
|
|
50
|
+
serverPaths.push(glob);
|
|
51
|
+
}
|
|
52
|
+
else if (node.layer === 'ui') {
|
|
53
|
+
webappPaths.push(glob);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Default assignment based on file path heuristics
|
|
57
|
+
if (isLikelyServerPath(normalized)) {
|
|
58
|
+
serverPaths.push(glob);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
webappPaths.push(glob);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Extract tags from node metadata
|
|
65
|
+
if (node.tags) {
|
|
66
|
+
tags.push(...node.tags);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (webappPaths.length === 0 && serverPaths.length === 0 && specDirs.length === 0) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
families.push({
|
|
73
|
+
id,
|
|
74
|
+
routes: [`/${id}`],
|
|
75
|
+
webappPaths: [...new Set(webappPaths)],
|
|
76
|
+
serverPaths: [...new Set(serverPaths)],
|
|
77
|
+
specDirs: [...new Set(specDirs)],
|
|
78
|
+
cypressSpecDirs: [],
|
|
79
|
+
tags: [...new Set(tags)],
|
|
80
|
+
features: [],
|
|
81
|
+
routesGuessed: true,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
families,
|
|
86
|
+
unmatchedSourceDirs: [],
|
|
87
|
+
unmatchedTestDirs: [],
|
|
88
|
+
stats: {
|
|
89
|
+
totalSourceFiles,
|
|
90
|
+
totalTestFiles,
|
|
91
|
+
familyCount: families.length,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Internal helpers
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// deriveClusterId and deriveClusterIdFromPath imported from cluster_utils.ts
|
|
99
|
+
function buildGlobFromPath(filePath) {
|
|
100
|
+
// Reject paths with traversal or null bytes
|
|
101
|
+
if (filePath.includes('..') || filePath.includes('\0')) {
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
// Convert a file path to a glob pattern matching the directory
|
|
105
|
+
const dir = filePath.split('/').slice(0, -1).join('/');
|
|
106
|
+
return dir ? `${dir}/*` : `${filePath}*`;
|
|
107
|
+
}
|
|
108
|
+
function isLikelyServerPath(filePath) {
|
|
109
|
+
const lower = filePath.toLowerCase();
|
|
110
|
+
return lower.includes('/server/') ||
|
|
111
|
+
lower.includes('/api/') ||
|
|
112
|
+
lower.includes('/routes/') ||
|
|
113
|
+
lower.includes('/controllers/') ||
|
|
114
|
+
lower.includes('/services/') ||
|
|
115
|
+
lower.includes('/models/') ||
|
|
116
|
+
lower.endsWith('.go') ||
|
|
117
|
+
lower.endsWith('.py');
|
|
118
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { DiscoveredDir, ScannedFamily, ScanResult } from './types.js';
|
|
2
|
+
/** Configurable scanner options. All fields optional — defaults to Mattermost conventions. */
|
|
3
|
+
export interface ScannerConfig {
|
|
4
|
+
serverInfraFiles?: Set<string>;
|
|
5
|
+
serverTiers?: readonly string[];
|
|
6
|
+
}
|
|
2
7
|
export declare function discoverSourceDirs(projectRoot: string): DiscoveredDir[];
|
|
3
8
|
export declare function discoverTestDirs(projectRoot: string): DiscoveredDir[];
|
|
4
9
|
/**
|
|
@@ -12,7 +17,7 @@ export declare function discoverTestDirs(projectRoot: string): DiscoveredDir[];
|
|
|
12
17
|
*
|
|
13
18
|
* Each domain becomes a candidate family with precise serverPaths.
|
|
14
19
|
*/
|
|
15
|
-
export declare function discoverServerDerivedFamilies(serverRoot: string): {
|
|
20
|
+
export declare function discoverServerDerivedFamilies(serverRoot: string, infraFiles?: Set<string>, tiers?: readonly string[]): {
|
|
16
21
|
multiTierFamilies: ScannedFamily[];
|
|
17
22
|
singleTierFamilies: ScannedFamily[];
|
|
18
23
|
};
|
|
@@ -27,5 +32,5 @@ export declare function discoverTestLibPaths(testsRoot: string): Map<string, str
|
|
|
27
32
|
* maps directly to a family ID.
|
|
28
33
|
*/
|
|
29
34
|
export declare function discoverNameMatchedPaths(appPath: string, gitRepoRoot?: string): Map<string, string[]>;
|
|
30
|
-
export declare function scanProject(projectRoot: string, testsRoot?: string, serverRoot?: string, gitRepoRoot?: string): ScanResult;
|
|
35
|
+
export declare function scanProject(projectRoot: string, testsRoot?: string, serverRoot?: string, gitRepoRoot?: string, config?: ScannerConfig): ScanResult;
|
|
31
36
|
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/training/scanner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAkB,UAAU,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/training/scanner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAkB,UAAU,EAAC,MAAM,YAAY,CAAC;AAoEzF,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC1B,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAqED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA+BvE;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA6DrE;AAuLD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CACzC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,GAAG,CAAC,MAAM,CAA8B,EACpD,KAAK,GAAE,SAAS,MAAM,EAAyB,GAChD;IAAC,iBAAiB,EAAE,aAAa,EAAE,CAAC;IAAC,kBAAkB,EAAE,aAAa,EAAE,CAAA;CAAC,CAgI3E;AAED,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAiG9E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CA8C7E;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACrB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAyDvB;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,UAAU,CA8LlJ"}
|
package/dist/training/scanner.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.discoverNameMatchedPaths = discoverNameMatchedPaths;
|
|
|
11
11
|
exports.scanProject = scanProject;
|
|
12
12
|
const fs_1 = require("fs");
|
|
13
13
|
const path_1 = require("path");
|
|
14
|
+
const cluster_utils_js_1 = require("../knowledge/cluster_utils.js");
|
|
14
15
|
const SOURCE_MAX_DEPTH = 3;
|
|
15
16
|
// One deeper than source to account for test framework wrapper dirs (e2e/, integration/)
|
|
16
17
|
const TEST_MAX_DEPTH = 5;
|
|
@@ -44,10 +45,11 @@ const STRUCTURAL_DIRS = new Set([
|
|
|
44
45
|
'middleware', 'contexts', 'providers', 'layouts', 'templates',
|
|
45
46
|
]);
|
|
46
47
|
/**
|
|
47
|
-
*
|
|
48
|
+
* Default server Go files that are infrastructure / cross-cutting concerns,
|
|
48
49
|
* not feature-specific domains. Matched after stripping _local/_store suffixes.
|
|
50
|
+
* Override via ScannerConfig.serverInfraFiles.
|
|
49
51
|
*/
|
|
50
|
-
const
|
|
52
|
+
const DEFAULT_SERVER_INFRA_FILES = new Set([
|
|
51
53
|
'api', 'apitestlib', 'context', 'helpers', 'params', 'swagger',
|
|
52
54
|
'app', 'server', 'enterprise', 'product_service', 'security_update_check',
|
|
53
55
|
'store', 'adapters', 'errors', 'integrity', 'migrate', 'doc',
|
|
@@ -57,10 +59,11 @@ const SERVER_INFRA_FILES = new Set([
|
|
|
57
59
|
'manifest', 'permission', 'log', 'utils',
|
|
58
60
|
]);
|
|
59
61
|
/**
|
|
60
|
-
*
|
|
62
|
+
* Default server tier directories to scan for Go domain files.
|
|
61
63
|
* Each tier represents a layer of the backend architecture.
|
|
64
|
+
* Override via ScannerConfig.serverTiers.
|
|
62
65
|
*/
|
|
63
|
-
const
|
|
66
|
+
const DEFAULT_SERVER_TIERS = [
|
|
64
67
|
'channels/api4',
|
|
65
68
|
'channels/app',
|
|
66
69
|
'channels/store/sqlstore',
|
|
@@ -73,13 +76,7 @@ const includes = (arr, v) => arr.includes(v);
|
|
|
73
76
|
function isSkipped(name) {
|
|
74
77
|
return name.startsWith('.') || SKIP_DIRS.has(name);
|
|
75
78
|
}
|
|
76
|
-
|
|
77
|
-
return name
|
|
78
|
-
.replace(/[A-Z]/g, (c, idx) => (idx > 0 ? `_${c.toLowerCase()}` : c.toLowerCase()))
|
|
79
|
-
.replace(/[^a-z0-9_]/g, '_')
|
|
80
|
-
.replace(/_+/g, '_')
|
|
81
|
-
.replace(/^_|_$/g, '');
|
|
82
|
-
}
|
|
79
|
+
const normalizeId = cluster_utils_js_1.normalizeToClusterId;
|
|
83
80
|
function extractFamilyHint(dirPath, projectRoot) {
|
|
84
81
|
const rel = (0, path_1.relative)(projectRoot, dirPath).replace(/\\/g, '/');
|
|
85
82
|
const parts = rel.split('/').filter(Boolean);
|
|
@@ -97,32 +94,25 @@ function walkDirs(root, projectRoot, category, maxDepth, results, depth = 0) {
|
|
|
97
94
|
if (depth > maxDepth || !(0, fs_1.existsSync)(root)) {
|
|
98
95
|
return;
|
|
99
96
|
}
|
|
100
|
-
let
|
|
97
|
+
let dirents;
|
|
101
98
|
try {
|
|
102
|
-
|
|
99
|
+
dirents = (0, fs_1.readdirSync)(root, { withFileTypes: true });
|
|
103
100
|
}
|
|
104
101
|
catch {
|
|
105
102
|
// ENOENT or EACCES — skip inaccessible entries
|
|
106
103
|
return;
|
|
107
104
|
}
|
|
108
|
-
const hasSourceFiles =
|
|
109
|
-
|
|
105
|
+
const hasSourceFiles = dirents.some((d) => {
|
|
106
|
+
if (!d.isFile())
|
|
107
|
+
return false;
|
|
108
|
+
const ext = d.name.slice(d.name.lastIndexOf('.'));
|
|
110
109
|
return ['.ts', '.tsx', '.js', '.jsx', '.go', '.py', '.rs'].includes(ext);
|
|
111
110
|
});
|
|
112
|
-
const subdirs =
|
|
113
|
-
if (
|
|
111
|
+
const subdirs = dirents.filter((d) => {
|
|
112
|
+
if (!d.isDirectory() || d.isSymbolicLink())
|
|
114
113
|
return false;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (stat.isSymbolicLink())
|
|
118
|
-
return false;
|
|
119
|
-
return stat.isDirectory();
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// ENOENT or EACCES — skip inaccessible entries
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
});
|
|
114
|
+
return !isSkipped(d.name);
|
|
115
|
+
}).map((d) => d.name);
|
|
126
116
|
if (hasSourceFiles && depth >= 1) {
|
|
127
117
|
results.push({
|
|
128
118
|
path: (0, path_1.resolve)(root),
|
|
@@ -419,7 +409,7 @@ function findParentDomain(name, allDomains) {
|
|
|
419
409
|
*
|
|
420
410
|
* Each domain becomes a candidate family with precise serverPaths.
|
|
421
411
|
*/
|
|
422
|
-
function discoverServerDerivedFamilies(serverRoot) {
|
|
412
|
+
function discoverServerDerivedFamilies(serverRoot, infraFiles = DEFAULT_SERVER_INFRA_FILES, tiers = DEFAULT_SERVER_TIERS) {
|
|
423
413
|
const resolved = (0, path_1.resolve)(serverRoot);
|
|
424
414
|
// First pass: collect all raw domain names across tiers
|
|
425
415
|
const allRawDomains = new Set();
|
|
@@ -430,7 +420,7 @@ function discoverServerDerivedFamilies(serverRoot) {
|
|
|
430
420
|
return;
|
|
431
421
|
const baseName = entry.replace('.go', '');
|
|
432
422
|
const domain = normalizeServerDomain(baseName);
|
|
433
|
-
if (!domain ||
|
|
423
|
+
if (!domain || infraFiles.has(domain))
|
|
434
424
|
return;
|
|
435
425
|
allRawDomains.add(domain);
|
|
436
426
|
if (!domainTierFiles.has(domain))
|
|
@@ -440,7 +430,7 @@ function discoverServerDerivedFamilies(serverRoot) {
|
|
|
440
430
|
tierMap.set(tierRelPath, new Set());
|
|
441
431
|
tierMap.get(tierRelPath).add(baseName);
|
|
442
432
|
}
|
|
443
|
-
for (const tier of
|
|
433
|
+
for (const tier of tiers) {
|
|
444
434
|
const tierPath = (0, path_1.join)(resolved, tier);
|
|
445
435
|
if (!(0, fs_1.existsSync)(tierPath))
|
|
446
436
|
continue;
|
|
@@ -477,7 +467,7 @@ function discoverServerDerivedFamilies(serverRoot) {
|
|
|
477
467
|
if (!(0, fs_1.lstatSync)(jobPath).isDirectory() || isSkipped(entry))
|
|
478
468
|
continue;
|
|
479
469
|
const domain = normalizeId(entry);
|
|
480
|
-
if (
|
|
470
|
+
if (infraFiles.has(domain))
|
|
481
471
|
continue;
|
|
482
472
|
allRawDomains.add(domain);
|
|
483
473
|
const jobFiles = (0, fs_1.readdirSync)(jobPath);
|
|
@@ -764,7 +754,10 @@ function discoverNameMatchedPaths(appPath, gitRepoRoot) {
|
|
|
764
754
|
}
|
|
765
755
|
return result;
|
|
766
756
|
}
|
|
767
|
-
function scanProject(projectRoot, testsRoot, serverRoot, gitRepoRoot) {
|
|
757
|
+
function scanProject(projectRoot, testsRoot, serverRoot, gitRepoRoot, config) {
|
|
758
|
+
// Resolve scanner config for this run (passed to functions, no module-level mutation)
|
|
759
|
+
const resolvedInfraFiles = config?.serverInfraFiles || DEFAULT_SERVER_INFRA_FILES;
|
|
760
|
+
const resolvedTiers = config?.serverTiers || DEFAULT_SERVER_TIERS;
|
|
768
761
|
const resolved = (0, path_1.resolve)(projectRoot);
|
|
769
762
|
const resolvedTestsRoot = testsRoot ? (0, path_1.resolve)(testsRoot) : resolved;
|
|
770
763
|
const sourceDirs = discoverSourceDirs(resolved);
|
|
@@ -827,7 +820,7 @@ function scanProject(projectRoot, testsRoot, serverRoot, gitRepoRoot) {
|
|
|
827
820
|
// When a separate serverRoot is provided, discover families from Go source
|
|
828
821
|
// filenames across the three-tier backend (api4, app, store).
|
|
829
822
|
if (serverRoot) {
|
|
830
|
-
const { multiTierFamilies: serverMulti, singleTierFamilies: serverSingle } = discoverServerDerivedFamilies((0, path_1.resolve)(serverRoot));
|
|
823
|
+
const { multiTierFamilies: serverMulti, singleTierFamilies: serverSingle } = discoverServerDerivedFamilies((0, path_1.resolve)(serverRoot), resolvedInfraFiles, resolvedTiers);
|
|
831
824
|
const existingIds = new Set(families.map((f) => f.id));
|
|
832
825
|
// Merge ALL server families (multi + single tier) into existing families,
|
|
833
826
|
// but only add NEW families if they span ≥2 tiers.
|
|
@@ -5,6 +5,8 @@ export interface EvidenceCheck {
|
|
|
5
5
|
hasPageObject: boolean;
|
|
6
6
|
hasUserAction: boolean;
|
|
7
7
|
hasExistingSpecCited: boolean;
|
|
8
|
+
/** Historical failure correlation boost (0-20) from failure_history */
|
|
9
|
+
historyBoost?: number;
|
|
8
10
|
}
|
|
9
11
|
export type ConfidenceClass = 'high' | 'medium' | 'low';
|
|
10
12
|
export declare const EVIDENCE_THRESHOLDS: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/validation/guardrails.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,MAAM,WAAW,aAAa;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/validation/guardrails.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AAExE,MAAM,WAAW,aAAa;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAuB9D;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CAQtE;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,4BAA4B,CACxC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,mBAAmB,GAC9B,OAAO,CAwBT;AAeD,wBAAgB,2BAA2B,CACvC,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,GACnC,MAAM,CAMR;AAED,wBAAgB,wBAAwB,CACpC,SAAS,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,GACvD,eAAe,CAUjB;AAGD,YAAY,EAAC,kBAAkB,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAC,gBAAgB,EAAE,YAAY,EAAC,MAAM,8BAA8B,CAAC"}
|
|
@@ -32,6 +32,11 @@ function computeConfidence(check) {
|
|
|
32
32
|
if (check.hasExistingSpecCited) {
|
|
33
33
|
score += 15;
|
|
34
34
|
}
|
|
35
|
+
// Historical failure correlation: if this file historically causes test failures,
|
|
36
|
+
// we're more confident it needs testing now
|
|
37
|
+
if (check.historyBoost) {
|
|
38
|
+
score += check.historyBoost;
|
|
39
|
+
}
|
|
35
40
|
return Math.min(100, score);
|
|
36
41
|
}
|
|
37
42
|
function classifyConfidence(confidence) {
|
|
@@ -9,6 +9,8 @@ export interface ExistingSpecCoverage {
|
|
|
9
9
|
coverageLevel: CoverageLevel;
|
|
10
10
|
missingScenarios?: string[];
|
|
11
11
|
}
|
|
12
|
+
export type { AssertionPattern } from '../knowledge/route_families.js';
|
|
13
|
+
import type { AssertionPattern } from '../knowledge/route_families.js';
|
|
12
14
|
export interface FlowDecision {
|
|
13
15
|
flowId: string;
|
|
14
16
|
flowName: string;
|
|
@@ -27,6 +29,7 @@ export interface FlowDecision {
|
|
|
27
29
|
blockingReason?: string;
|
|
28
30
|
priority: FlowPriority;
|
|
29
31
|
userActions: string[];
|
|
32
|
+
assertionPatterns?: AssertionPattern[];
|
|
30
33
|
}
|
|
31
34
|
export interface FlowDecisionSummary {
|
|
32
35
|
changedFiles: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output_schema.d.ts","sourceRoot":"","sources":["../../src/validation/output_schema.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,eAAe,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAC/F,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,eAAe,CAAC;AACjF,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"output_schema.d.ts","sourceRoot":"","sources":["../../src/validation/output_schema.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,eAAe,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAC/F,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,eAAe,CAAC;AACjF,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,YAAY,EAAC,gBAAgB,EAAC,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,gCAAgC,CAAC;AAErE,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,iBAAiB,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACL;AAMD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAC,CA0C1F;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,mBAAmB,CAmC3E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAoBnC"}
|