pumuki 6.3.13 → 6.3.15
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 -7
- package/VERSION +1 -1
- package/bin/pumuki-mcp-enterprise.js +5 -0
- package/bin/pumuki-pre-write.js +11 -0
- package/docs/API_REFERENCE.md +2 -1
- package/docs/CORE_INTEGRATIONS_UNTESTED_INVENTORY.md +57 -0
- package/docs/INSTALLATION.md +101 -54
- package/docs/MCP_SERVERS.md +167 -74
- package/docs/PUMUKI_FULL_VALIDATION_CHECKLIST.md +46 -45
- package/docs/PUMUKI_OPENSPEC_SDD_ROADMAP.md +55 -0
- package/docs/README.md +9 -0
- package/docs/REFRACTOR_PROGRESS.md +288 -6
- package/docs/USAGE.md +115 -8
- package/docs/validation/README.md +5 -0
- package/docs/validation/mock-consumer-next-cycle-enterprise-checklist.md +68 -0
- package/docs/validation/mock-consumer-next-round-scope.md +222 -0
- package/docs/validation/mock-consumer-post-release-handoff-pack.md +486 -0
- package/docs/validation/phase12-go-no-go-report.md +73 -0
- package/docs/validation/post-phase12-next-lot-decision.md +75 -0
- package/integrations/config/skillsRuleSet.ts +53 -6
- package/integrations/evidence/buildEvidence.ts +42 -3
- package/integrations/evidence/generateEvidence.test.ts +59 -0
- package/integrations/evidence/readEvidence.test.ts +61 -0
- package/integrations/evidence/schema.test.ts +81 -0
- package/integrations/evidence/schema.ts +11 -0
- package/integrations/evidence/writeEvidence.test.ts +18 -0
- package/integrations/evidence/writeEvidence.ts +11 -0
- package/integrations/git/resolveGitRefs.ts +2 -2
- package/integrations/git/runPlatformGate.ts +64 -0
- package/integrations/git/runPlatformGateEvidence.ts +13 -0
- package/integrations/git/stageRunners.ts +10 -1
- package/integrations/lifecycle/artifacts.ts +57 -4
- package/integrations/lifecycle/cli.ts +248 -12
- package/integrations/lifecycle/constants.ts +1 -0
- package/integrations/lifecycle/gitService.ts +1 -0
- package/integrations/lifecycle/install.ts +24 -1
- package/integrations/lifecycle/openSpecBootstrap.ts +190 -0
- package/integrations/lifecycle/state.ts +57 -0
- package/integrations/lifecycle/uninstall.ts +3 -1
- package/integrations/lifecycle/update.ts +11 -0
- package/integrations/mcp/enterpriseServer.cli.ts +12 -0
- package/integrations/mcp/enterpriseServer.ts +762 -0
- package/integrations/mcp/evidenceFacets.ts +5 -2
- package/integrations/mcp/evidenceFacetsBase.ts +3 -94
- package/integrations/mcp/evidenceFacetsFindings.ts +39 -0
- package/integrations/mcp/evidenceFacetsLedger.ts +27 -0
- package/integrations/mcp/evidenceFacetsPlatforms.ts +21 -0
- package/integrations/mcp/evidenceFacetsRulesets.ts +53 -0
- package/integrations/mcp/evidenceFacetsSeverity.ts +62 -0
- package/integrations/mcp/evidenceFacetsSnapshot.ts +4 -104
- package/integrations/mcp/evidencePayloadBuilders.ts +2 -0
- package/integrations/mcp/evidencePayloadContext.ts +5 -0
- package/integrations/mcp/evidencePayloadStatus.ts +100 -0
- package/integrations/mcp/evidencePayloadSummary.ts +0 -81
- package/integrations/mcp/evidencePayloads.ts +2 -8
- package/integrations/mcp/index.ts +1 -0
- package/integrations/sdd/index.ts +11 -0
- package/integrations/sdd/openSpecCli.ts +180 -0
- package/integrations/sdd/policy.ts +190 -0
- package/integrations/sdd/sessionStore.ts +152 -0
- package/integrations/sdd/types.ts +69 -0
- package/package.json +10 -5
- package/scripts/framework-menu-runner-path-lib.ts +10 -3
- package/scripts/framework-menu.ts +86 -5
- package/scripts/package-install-smoke-gate-lib.ts +6 -1
- package/scripts/package-install-smoke-lifecycle-lib.ts +3 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './evidenceFacetsRulesets';
|
|
2
|
+
export * from './evidenceFacetsPlatforms';
|
|
3
|
+
export * from './evidenceFacetsSeverity';
|
|
4
|
+
export * from './evidenceFacetsFindings';
|
|
5
|
+
export * from './evidenceFacetsLedger';
|
|
2
6
|
export * from './evidenceFacetsSuppressed';
|
|
3
|
-
export * from './evidenceFacetsSnapshot';
|
|
@@ -1,94 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
const byPlatform = left.platform.localeCompare(right.platform);
|
|
5
|
-
if (byPlatform !== 0) {
|
|
6
|
-
return byPlatform;
|
|
7
|
-
}
|
|
8
|
-
const byBundle = left.bundle.localeCompare(right.bundle);
|
|
9
|
-
if (byBundle !== 0) {
|
|
10
|
-
return byBundle;
|
|
11
|
-
}
|
|
12
|
-
return left.hash.localeCompare(right.hash);
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const sortPlatforms = (platforms: AiEvidenceV2_1['platforms']) => {
|
|
17
|
-
return Object.entries(platforms)
|
|
18
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
19
|
-
.map(([platform, state]) => ({
|
|
20
|
-
platform,
|
|
21
|
-
detected: state.detected,
|
|
22
|
-
confidence: state.confidence,
|
|
23
|
-
}));
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const severityOrder = ['CRITICAL', 'ERROR', 'WARN', 'INFO'] as const;
|
|
27
|
-
export const severityRank: Record<string, number> = {
|
|
28
|
-
CRITICAL: 0,
|
|
29
|
-
ERROR: 1,
|
|
30
|
-
WARN: 2,
|
|
31
|
-
INFO: 3,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const toSeverityCounts = (
|
|
35
|
-
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
36
|
-
): Record<string, number> => {
|
|
37
|
-
const counts = new Map<string, number>();
|
|
38
|
-
for (const finding of findings) {
|
|
39
|
-
const key = finding.severity.toUpperCase();
|
|
40
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const orderedEntries = [...counts.entries()].sort(([left], [right]) => {
|
|
44
|
-
const leftIndex = severityOrder.indexOf(left as (typeof severityOrder)[number]);
|
|
45
|
-
const rightIndex = severityOrder.indexOf(right as (typeof severityOrder)[number]);
|
|
46
|
-
const normalizedLeft = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
|
|
47
|
-
const normalizedRight = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
|
|
48
|
-
if (normalizedLeft !== normalizedRight) {
|
|
49
|
-
return normalizedLeft - normalizedRight;
|
|
50
|
-
}
|
|
51
|
-
return left.localeCompare(right);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return Object.fromEntries(orderedEntries);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const toRulesetsByPlatform = (rulesets: AiEvidenceV2_1['rulesets']): Record<string, number> => {
|
|
58
|
-
const counts = new Map<string, number>();
|
|
59
|
-
for (const ruleset of rulesets) {
|
|
60
|
-
counts.set(ruleset.platform, (counts.get(ruleset.platform) ?? 0) + 1);
|
|
61
|
-
}
|
|
62
|
-
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const toRulesetsFingerprint = (rulesets: AiEvidenceV2_1['rulesets']): string => {
|
|
66
|
-
return sortRulesets(rulesets)
|
|
67
|
-
.map((ruleset) => ruleset.hash)
|
|
68
|
-
.join('|');
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const toRulesetsBundlesCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
72
|
-
const bundles = new Set<string>();
|
|
73
|
-
for (const ruleset of rulesets) {
|
|
74
|
-
bundles.add(ruleset.bundle);
|
|
75
|
-
}
|
|
76
|
-
return bundles.size;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const toRulesetsPlatformsCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
80
|
-
const platforms = new Set<string>();
|
|
81
|
-
for (const ruleset of rulesets) {
|
|
82
|
-
platforms.add(ruleset.platform);
|
|
83
|
-
}
|
|
84
|
-
return platforms.size;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export const toRulesetsHashesCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
88
|
-
const hashes = new Set<string>();
|
|
89
|
-
for (const ruleset of rulesets) {
|
|
90
|
-
hashes.add(ruleset.hash);
|
|
91
|
-
}
|
|
92
|
-
return hashes.size;
|
|
93
|
-
};
|
|
94
|
-
|
|
1
|
+
export * from './evidenceFacetsRulesets';
|
|
2
|
+
export * from './evidenceFacetsPlatforms';
|
|
3
|
+
export * from './evidenceFacetsSeverity';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
|
+
import { inferPlatformFromFilePath } from './evidenceFacetsSuppressed';
|
|
3
|
+
|
|
4
|
+
export const toFindingsFilesCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
5
|
+
const files = new Set<string>();
|
|
6
|
+
for (const finding of findings) {
|
|
7
|
+
files.add(finding.file);
|
|
8
|
+
}
|
|
9
|
+
return files.size;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const toFindingsRulesCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
13
|
+
const rules = new Set<string>();
|
|
14
|
+
for (const finding of findings) {
|
|
15
|
+
rules.add(finding.ruleId);
|
|
16
|
+
}
|
|
17
|
+
return rules.size;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const toFindingsWithLinesCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
21
|
+
let count = 0;
|
|
22
|
+
for (const finding of findings) {
|
|
23
|
+
if (finding.lines && Array.isArray(finding.lines) && finding.lines.length > 0) {
|
|
24
|
+
count += 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return count;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const toFindingsByPlatform = (
|
|
31
|
+
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
32
|
+
): Record<string, number> => {
|
|
33
|
+
const counts = new Map<string, number>();
|
|
34
|
+
for (const finding of findings) {
|
|
35
|
+
const platform = inferPlatformFromFilePath(finding.file);
|
|
36
|
+
counts.set(platform, (counts.get(platform) ?? 0) + 1);
|
|
37
|
+
}
|
|
38
|
+
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
39
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
|
+
import { inferPlatformFromFilePath } from './evidenceFacetsSuppressed';
|
|
3
|
+
|
|
4
|
+
export const toLedgerByPlatform = (ledger: AiEvidenceV2_1['ledger']): Record<string, number> => {
|
|
5
|
+
const counts = new Map<string, number>();
|
|
6
|
+
for (const entry of ledger) {
|
|
7
|
+
const platform = inferPlatformFromFilePath(entry.file);
|
|
8
|
+
counts.set(platform, (counts.get(platform) ?? 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const toLedgerFilesCount = (ledger: AiEvidenceV2_1['ledger']): number => {
|
|
14
|
+
const files = new Set<string>();
|
|
15
|
+
for (const entry of ledger) {
|
|
16
|
+
files.add(entry.file);
|
|
17
|
+
}
|
|
18
|
+
return files.size;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const toLedgerRulesCount = (ledger: AiEvidenceV2_1['ledger']): number => {
|
|
22
|
+
const rules = new Set<string>();
|
|
23
|
+
for (const entry of ledger) {
|
|
24
|
+
rules.add(entry.ruleId);
|
|
25
|
+
}
|
|
26
|
+
return rules.size;
|
|
27
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
|
+
|
|
3
|
+
export const sortPlatforms = (platforms: AiEvidenceV2_1['platforms']) => {
|
|
4
|
+
return Object.entries(platforms)
|
|
5
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
6
|
+
.map(([platform, state]) => ({
|
|
7
|
+
platform,
|
|
8
|
+
detected: state.detected,
|
|
9
|
+
confidence: state.confidence,
|
|
10
|
+
}));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const toPlatformConfidenceCounts = (
|
|
14
|
+
platforms: AiEvidenceV2_1['platforms']
|
|
15
|
+
): Record<string, number> => {
|
|
16
|
+
const counts = new Map<string, number>();
|
|
17
|
+
for (const entry of Object.values(platforms)) {
|
|
18
|
+
counts.set(entry.confidence, (counts.get(entry.confidence) ?? 0) + 1);
|
|
19
|
+
}
|
|
20
|
+
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
21
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
|
+
|
|
3
|
+
export const sortRulesets = (rulesets: AiEvidenceV2_1['rulesets']): AiEvidenceV2_1['rulesets'] => {
|
|
4
|
+
return [...rulesets].sort((left, right) => {
|
|
5
|
+
const byPlatform = left.platform.localeCompare(right.platform);
|
|
6
|
+
if (byPlatform !== 0) {
|
|
7
|
+
return byPlatform;
|
|
8
|
+
}
|
|
9
|
+
const byBundle = left.bundle.localeCompare(right.bundle);
|
|
10
|
+
if (byBundle !== 0) {
|
|
11
|
+
return byBundle;
|
|
12
|
+
}
|
|
13
|
+
return left.hash.localeCompare(right.hash);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const toRulesetsByPlatform = (rulesets: AiEvidenceV2_1['rulesets']): Record<string, number> => {
|
|
18
|
+
const counts = new Map<string, number>();
|
|
19
|
+
for (const ruleset of rulesets) {
|
|
20
|
+
counts.set(ruleset.platform, (counts.get(ruleset.platform) ?? 0) + 1);
|
|
21
|
+
}
|
|
22
|
+
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const toRulesetsFingerprint = (rulesets: AiEvidenceV2_1['rulesets']): string => {
|
|
26
|
+
return sortRulesets(rulesets)
|
|
27
|
+
.map((ruleset) => ruleset.hash)
|
|
28
|
+
.join('|');
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const toRulesetsBundlesCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
32
|
+
const bundles = new Set<string>();
|
|
33
|
+
for (const ruleset of rulesets) {
|
|
34
|
+
bundles.add(ruleset.bundle);
|
|
35
|
+
}
|
|
36
|
+
return bundles.size;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const toRulesetsPlatformsCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
40
|
+
const platforms = new Set<string>();
|
|
41
|
+
for (const ruleset of rulesets) {
|
|
42
|
+
platforms.add(ruleset.platform);
|
|
43
|
+
}
|
|
44
|
+
return platforms.size;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const toRulesetsHashesCount = (rulesets: AiEvidenceV2_1['rulesets']): number => {
|
|
48
|
+
const hashes = new Set<string>();
|
|
49
|
+
for (const ruleset of rulesets) {
|
|
50
|
+
hashes.add(ruleset.hash);
|
|
51
|
+
}
|
|
52
|
+
return hashes.size;
|
|
53
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
|
+
|
|
3
|
+
export const severityOrder = ['CRITICAL', 'ERROR', 'WARN', 'INFO'] as const;
|
|
4
|
+
export const severityRank: Record<string, number> = {
|
|
5
|
+
CRITICAL: 0,
|
|
6
|
+
ERROR: 1,
|
|
7
|
+
WARN: 2,
|
|
8
|
+
INFO: 3,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const toSeverityCounts = (
|
|
12
|
+
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
13
|
+
): Record<string, number> => {
|
|
14
|
+
const counts = new Map<string, number>();
|
|
15
|
+
for (const finding of findings) {
|
|
16
|
+
const key = finding.severity.toUpperCase();
|
|
17
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const orderedEntries = [...counts.entries()].sort(([left], [right]) => {
|
|
21
|
+
const leftIndex = severityOrder.indexOf(left as (typeof severityOrder)[number]);
|
|
22
|
+
const rightIndex = severityOrder.indexOf(right as (typeof severityOrder)[number]);
|
|
23
|
+
const normalizedLeft = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
|
|
24
|
+
const normalizedRight = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
|
|
25
|
+
if (normalizedLeft !== normalizedRight) {
|
|
26
|
+
return normalizedLeft - normalizedRight;
|
|
27
|
+
}
|
|
28
|
+
return left.localeCompare(right);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return Object.fromEntries(orderedEntries);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const toHighestSeverity = (
|
|
35
|
+
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
36
|
+
): string | null => {
|
|
37
|
+
let highest: string | null = null;
|
|
38
|
+
for (const finding of findings) {
|
|
39
|
+
const severity = finding.severity.toUpperCase();
|
|
40
|
+
if (highest === null) {
|
|
41
|
+
highest = severity;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const currentRank = severityRank[severity] ?? Number.MAX_SAFE_INTEGER;
|
|
45
|
+
const highestRank = severityRank[highest] ?? Number.MAX_SAFE_INTEGER;
|
|
46
|
+
if (currentRank < highestRank) {
|
|
47
|
+
highest = severity;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return highest;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const toBlockingFindingsCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
54
|
+
let count = 0;
|
|
55
|
+
for (const finding of findings) {
|
|
56
|
+
const rank = severityRank[finding.severity.toUpperCase()] ?? Number.MAX_SAFE_INTEGER;
|
|
57
|
+
if (rank <= severityRank.ERROR) {
|
|
58
|
+
count += 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return count;
|
|
62
|
+
};
|
|
@@ -1,104 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
const files = new Set<string>();
|
|
6
|
-
for (const finding of findings) {
|
|
7
|
-
files.add(finding.file);
|
|
8
|
-
}
|
|
9
|
-
return files.size;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const toFindingsRulesCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
13
|
-
const rules = new Set<string>();
|
|
14
|
-
for (const finding of findings) {
|
|
15
|
-
rules.add(finding.ruleId);
|
|
16
|
-
}
|
|
17
|
-
return rules.size;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const toFindingsWithLinesCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
21
|
-
let count = 0;
|
|
22
|
-
for (const finding of findings) {
|
|
23
|
-
if (finding.lines && Array.isArray(finding.lines) && finding.lines.length > 0) {
|
|
24
|
-
count += 1;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return count;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const toPlatformConfidenceCounts = (
|
|
31
|
-
platforms: AiEvidenceV2_1['platforms']
|
|
32
|
-
): Record<string, number> => {
|
|
33
|
-
const counts = new Map<string, number>();
|
|
34
|
-
for (const entry of Object.values(platforms)) {
|
|
35
|
-
counts.set(entry.confidence, (counts.get(entry.confidence) ?? 0) + 1);
|
|
36
|
-
}
|
|
37
|
-
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const toFindingsByPlatform = (
|
|
41
|
-
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
42
|
-
): Record<string, number> => {
|
|
43
|
-
const counts = new Map<string, number>();
|
|
44
|
-
for (const finding of findings) {
|
|
45
|
-
const platform = inferPlatformFromFilePath(finding.file);
|
|
46
|
-
counts.set(platform, (counts.get(platform) ?? 0) + 1);
|
|
47
|
-
}
|
|
48
|
-
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const toLedgerByPlatform = (ledger: AiEvidenceV2_1['ledger']): Record<string, number> => {
|
|
52
|
-
const counts = new Map<string, number>();
|
|
53
|
-
for (const entry of ledger) {
|
|
54
|
-
const platform = inferPlatformFromFilePath(entry.file);
|
|
55
|
-
counts.set(platform, (counts.get(platform) ?? 0) + 1);
|
|
56
|
-
}
|
|
57
|
-
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const toLedgerFilesCount = (ledger: AiEvidenceV2_1['ledger']): number => {
|
|
61
|
-
const files = new Set<string>();
|
|
62
|
-
for (const entry of ledger) {
|
|
63
|
-
files.add(entry.file);
|
|
64
|
-
}
|
|
65
|
-
return files.size;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const toLedgerRulesCount = (ledger: AiEvidenceV2_1['ledger']): number => {
|
|
69
|
-
const rules = new Set<string>();
|
|
70
|
-
for (const entry of ledger) {
|
|
71
|
-
rules.add(entry.ruleId);
|
|
72
|
-
}
|
|
73
|
-
return rules.size;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const toHighestSeverity = (
|
|
77
|
-
findings: AiEvidenceV2_1['snapshot']['findings']
|
|
78
|
-
): string | null => {
|
|
79
|
-
let highest: string | null = null;
|
|
80
|
-
for (const finding of findings) {
|
|
81
|
-
const severity = finding.severity.toUpperCase();
|
|
82
|
-
if (highest === null) {
|
|
83
|
-
highest = severity;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const currentRank = severityRank[severity] ?? Number.MAX_SAFE_INTEGER;
|
|
87
|
-
const highestRank = severityRank[highest] ?? Number.MAX_SAFE_INTEGER;
|
|
88
|
-
if (currentRank < highestRank) {
|
|
89
|
-
highest = severity;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return highest;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export const toBlockingFindingsCount = (findings: AiEvidenceV2_1['snapshot']['findings']): number => {
|
|
96
|
-
let count = 0;
|
|
97
|
-
for (const finding of findings) {
|
|
98
|
-
const rank = severityRank[finding.severity.toUpperCase()] ?? Number.MAX_SAFE_INTEGER;
|
|
99
|
-
if (rank <= severityRank.ERROR) {
|
|
100
|
-
count += 1;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return count;
|
|
104
|
-
};
|
|
1
|
+
export * from './evidenceFacetsPlatforms';
|
|
2
|
+
export * from './evidenceFacetsFindings';
|
|
3
|
+
export * from './evidenceFacetsLedger';
|
|
4
|
+
export * from './evidenceFacetsSeverity';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { readEvidenceResult } from '../evidence/readEvidence';
|
|
3
|
+
import {
|
|
4
|
+
sortPlatforms,
|
|
5
|
+
toBlockingFindingsCount,
|
|
6
|
+
toFindingsByPlatform,
|
|
7
|
+
toFindingsFilesCount,
|
|
8
|
+
toFindingsRulesCount,
|
|
9
|
+
toFindingsWithLinesCount,
|
|
10
|
+
toHighestSeverity,
|
|
11
|
+
toLedgerByPlatform,
|
|
12
|
+
toLedgerFilesCount,
|
|
13
|
+
toLedgerRulesCount,
|
|
14
|
+
toPlatformConfidenceCounts,
|
|
15
|
+
toRulesetsBundlesCount,
|
|
16
|
+
toRulesetsByPlatform,
|
|
17
|
+
toRulesetsFingerprint,
|
|
18
|
+
toRulesetsHashesCount,
|
|
19
|
+
toRulesetsPlatformsCount,
|
|
20
|
+
toSeverityCounts,
|
|
21
|
+
} from './evidenceFacets';
|
|
22
|
+
import { CONTEXT_API_CAPABILITIES } from './evidencePayloadConfig';
|
|
23
|
+
import { toSuppressedSummaryFields } from './evidencePayloadSummarySuppressed';
|
|
24
|
+
|
|
25
|
+
export const toStatusPayload = (repoRoot: string): unknown => {
|
|
26
|
+
const readResult = readEvidenceResult(repoRoot);
|
|
27
|
+
const evidencePath = resolve(repoRoot, '.ai_evidence.json');
|
|
28
|
+
|
|
29
|
+
if (readResult.kind === 'missing') {
|
|
30
|
+
return {
|
|
31
|
+
status: 'degraded',
|
|
32
|
+
context_api: CONTEXT_API_CAPABILITIES,
|
|
33
|
+
evidence: {
|
|
34
|
+
path: evidencePath,
|
|
35
|
+
present: false,
|
|
36
|
+
valid: false,
|
|
37
|
+
version: null,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (readResult.kind === 'invalid') {
|
|
43
|
+
return {
|
|
44
|
+
status: 'degraded',
|
|
45
|
+
context_api: CONTEXT_API_CAPABILITIES,
|
|
46
|
+
evidence: {
|
|
47
|
+
path: evidencePath,
|
|
48
|
+
present: true,
|
|
49
|
+
valid: false,
|
|
50
|
+
version: readResult.version ?? null,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const { evidence } = readResult;
|
|
56
|
+
const sortedPlatforms = sortPlatforms(evidence.platforms);
|
|
57
|
+
const detectedPlatformsCount = sortedPlatforms.filter((entry) => entry.detected).length;
|
|
58
|
+
const suppressedFindingsCount = evidence.consolidation?.suppressed?.length ?? 0;
|
|
59
|
+
const findingsWithLinesCount = toFindingsWithLinesCount(evidence.snapshot.findings);
|
|
60
|
+
return {
|
|
61
|
+
status: 'ok',
|
|
62
|
+
context_api: CONTEXT_API_CAPABILITIES,
|
|
63
|
+
evidence: {
|
|
64
|
+
path: evidencePath,
|
|
65
|
+
present: true,
|
|
66
|
+
valid: true,
|
|
67
|
+
version: evidence.version,
|
|
68
|
+
timestamp: evidence.timestamp,
|
|
69
|
+
stage: evidence.snapshot.stage,
|
|
70
|
+
outcome: evidence.snapshot.outcome,
|
|
71
|
+
has_findings: evidence.snapshot.findings.length > 0,
|
|
72
|
+
findings_count: evidence.snapshot.findings.length,
|
|
73
|
+
findings_files_count: toFindingsFilesCount(evidence.snapshot.findings),
|
|
74
|
+
findings_rules_count: toFindingsRulesCount(evidence.snapshot.findings),
|
|
75
|
+
findings_with_lines_count: findingsWithLinesCount,
|
|
76
|
+
findings_without_lines_count: evidence.snapshot.findings.length - findingsWithLinesCount,
|
|
77
|
+
severity_counts: toSeverityCounts(evidence.snapshot.findings),
|
|
78
|
+
findings_by_platform: toFindingsByPlatform(evidence.snapshot.findings),
|
|
79
|
+
highest_severity: toHighestSeverity(evidence.snapshot.findings),
|
|
80
|
+
blocking_findings_count: toBlockingFindingsCount(evidence.snapshot.findings),
|
|
81
|
+
ledger_count: evidence.ledger.length,
|
|
82
|
+
ledger_files_count: toLedgerFilesCount(evidence.ledger),
|
|
83
|
+
ledger_rules_count: toLedgerRulesCount(evidence.ledger),
|
|
84
|
+
ledger_by_platform: toLedgerByPlatform(evidence.ledger),
|
|
85
|
+
rulesets_count: evidence.rulesets.length,
|
|
86
|
+
rulesets_platforms_count: toRulesetsPlatformsCount(evidence.rulesets),
|
|
87
|
+
rulesets_bundles_count: toRulesetsBundlesCount(evidence.rulesets),
|
|
88
|
+
rulesets_hashes_count: toRulesetsHashesCount(evidence.rulesets),
|
|
89
|
+
rulesets_by_platform: toRulesetsByPlatform(evidence.rulesets),
|
|
90
|
+
rulesets_fingerprint: toRulesetsFingerprint(evidence.rulesets),
|
|
91
|
+
platform_confidence_counts: toPlatformConfidenceCounts(evidence.platforms),
|
|
92
|
+
suppressed_findings_count: suppressedFindingsCount,
|
|
93
|
+
...toSuppressedSummaryFields(evidence),
|
|
94
|
+
tracked_platforms_count: sortedPlatforms.length,
|
|
95
|
+
detected_platforms_count: detectedPlatformsCount,
|
|
96
|
+
non_detected_platforms_count: sortedPlatforms.length - detectedPlatformsCount,
|
|
97
|
+
platforms: Object.keys(evidence.platforms).sort(),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { resolve } from 'node:path';
|
|
2
1
|
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
3
|
-
import { readEvidenceResult } from '../evidence/readEvidence';
|
|
4
2
|
import {
|
|
5
3
|
sortPlatforms,
|
|
6
4
|
toBlockingFindingsCount,
|
|
@@ -21,7 +19,6 @@ import {
|
|
|
21
19
|
toSeverityCounts,
|
|
22
20
|
} from './evidenceFacets';
|
|
23
21
|
import { toSuppressedSummaryFields } from './evidencePayloadSummarySuppressed';
|
|
24
|
-
import { CONTEXT_API_CAPABILITIES } from './evidencePayloadConfig';
|
|
25
22
|
|
|
26
23
|
export const toSummaryPayload = (evidence: AiEvidenceV2_1) => {
|
|
27
24
|
const sortedPlatforms = sortPlatforms(evidence.platforms);
|
|
@@ -64,81 +61,3 @@ export const toSummaryPayload = (evidence: AiEvidenceV2_1) => {
|
|
|
64
61
|
platforms: detectedPlatforms,
|
|
65
62
|
};
|
|
66
63
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
export const toStatusPayload = (repoRoot: string): unknown => {
|
|
70
|
-
const readResult = readEvidenceResult(repoRoot);
|
|
71
|
-
const evidencePath = resolve(repoRoot, '.ai_evidence.json');
|
|
72
|
-
|
|
73
|
-
if (readResult.kind === 'missing') {
|
|
74
|
-
return {
|
|
75
|
-
status: 'degraded',
|
|
76
|
-
context_api: CONTEXT_API_CAPABILITIES,
|
|
77
|
-
evidence: {
|
|
78
|
-
path: evidencePath,
|
|
79
|
-
present: false,
|
|
80
|
-
valid: false,
|
|
81
|
-
version: null,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (readResult.kind === 'invalid') {
|
|
87
|
-
return {
|
|
88
|
-
status: 'degraded',
|
|
89
|
-
context_api: CONTEXT_API_CAPABILITIES,
|
|
90
|
-
evidence: {
|
|
91
|
-
path: evidencePath,
|
|
92
|
-
present: true,
|
|
93
|
-
valid: false,
|
|
94
|
-
version: readResult.version ?? null,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const { evidence } = readResult;
|
|
100
|
-
const sortedPlatforms = sortPlatforms(evidence.platforms);
|
|
101
|
-
const detectedPlatformsCount = sortedPlatforms.filter((entry) => entry.detected).length;
|
|
102
|
-
const suppressedFindingsCount = evidence.consolidation?.suppressed?.length ?? 0;
|
|
103
|
-
const findingsWithLinesCount = toFindingsWithLinesCount(evidence.snapshot.findings);
|
|
104
|
-
return {
|
|
105
|
-
status: 'ok',
|
|
106
|
-
context_api: CONTEXT_API_CAPABILITIES,
|
|
107
|
-
evidence: {
|
|
108
|
-
path: evidencePath,
|
|
109
|
-
present: true,
|
|
110
|
-
valid: true,
|
|
111
|
-
version: evidence.version,
|
|
112
|
-
timestamp: evidence.timestamp,
|
|
113
|
-
stage: evidence.snapshot.stage,
|
|
114
|
-
outcome: evidence.snapshot.outcome,
|
|
115
|
-
has_findings: evidence.snapshot.findings.length > 0,
|
|
116
|
-
findings_count: evidence.snapshot.findings.length,
|
|
117
|
-
findings_files_count: toFindingsFilesCount(evidence.snapshot.findings),
|
|
118
|
-
findings_rules_count: toFindingsRulesCount(evidence.snapshot.findings),
|
|
119
|
-
findings_with_lines_count: findingsWithLinesCount,
|
|
120
|
-
findings_without_lines_count: evidence.snapshot.findings.length - findingsWithLinesCount,
|
|
121
|
-
severity_counts: toSeverityCounts(evidence.snapshot.findings),
|
|
122
|
-
findings_by_platform: toFindingsByPlatform(evidence.snapshot.findings),
|
|
123
|
-
highest_severity: toHighestSeverity(evidence.snapshot.findings),
|
|
124
|
-
blocking_findings_count: toBlockingFindingsCount(evidence.snapshot.findings),
|
|
125
|
-
ledger_count: evidence.ledger.length,
|
|
126
|
-
ledger_files_count: toLedgerFilesCount(evidence.ledger),
|
|
127
|
-
ledger_rules_count: toLedgerRulesCount(evidence.ledger),
|
|
128
|
-
ledger_by_platform: toLedgerByPlatform(evidence.ledger),
|
|
129
|
-
rulesets_count: evidence.rulesets.length,
|
|
130
|
-
rulesets_platforms_count: toRulesetsPlatformsCount(evidence.rulesets),
|
|
131
|
-
rulesets_bundles_count: toRulesetsBundlesCount(evidence.rulesets),
|
|
132
|
-
rulesets_hashes_count: toRulesetsHashesCount(evidence.rulesets),
|
|
133
|
-
rulesets_by_platform: toRulesetsByPlatform(evidence.rulesets),
|
|
134
|
-
rulesets_fingerprint: toRulesetsFingerprint(evidence.rulesets),
|
|
135
|
-
platform_confidence_counts: toPlatformConfidenceCounts(evidence.platforms),
|
|
136
|
-
suppressed_findings_count: suppressedFindingsCount,
|
|
137
|
-
...toSuppressedSummaryFields(evidence),
|
|
138
|
-
tracked_platforms_count: sortedPlatforms.length,
|
|
139
|
-
detected_platforms_count: detectedPlatformsCount,
|
|
140
|
-
non_detected_platforms_count: sortedPlatforms.length - detectedPlatformsCount,
|
|
141
|
-
platforms: Object.keys(evidence.platforms).sort(),
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
};
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type { EvidenceReadResult } from '../evidence/readEvidence';
|
|
4
|
-
export { readEvidenceResult, readEvidence };
|
|
5
|
-
|
|
6
|
-
export * from './evidencePayloadConfig';
|
|
7
|
-
export * from './evidencePayloadCollections';
|
|
8
|
-
export * from './evidencePayloadSummary';
|
|
1
|
+
export * from './evidencePayloadContext';
|
|
2
|
+
export * from './evidencePayloadBuilders';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
OpenSpecValidationSummary,
|
|
3
|
+
SddDecision,
|
|
4
|
+
SddDecisionCode,
|
|
5
|
+
SddEvaluateResult,
|
|
6
|
+
SddSessionState,
|
|
7
|
+
SddStage,
|
|
8
|
+
SddStatusPayload,
|
|
9
|
+
} from './types';
|
|
10
|
+
export { evaluateSddPolicy, readSddStatus } from './policy';
|
|
11
|
+
export { closeSddSession, openSddSession, readSddSession, refreshSddSession } from './sessionStore';
|