@ai-sdlc/orchestrator 0.1.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/dist/adapters.d.ts +60 -0
- package/dist/adapters.js +160 -0
- package/dist/admission.d.ts +49 -0
- package/dist/admission.js +90 -0
- package/dist/analysis/analyzer.d.ts +10 -0
- package/dist/analysis/analyzer.js +67 -0
- package/dist/analysis/complexity-scorer.d.ts +24 -0
- package/dist/analysis/complexity-scorer.js +39 -0
- package/dist/analysis/context-builder.d.ts +14 -0
- package/dist/analysis/context-builder.js +79 -0
- package/dist/analysis/convention-detector.d.ts +9 -0
- package/dist/analysis/convention-detector.js +177 -0
- package/dist/analysis/dependency-parser.d.ts +17 -0
- package/dist/analysis/dependency-parser.js +167 -0
- package/dist/analysis/file-walker.d.ts +18 -0
- package/dist/analysis/file-walker.js +158 -0
- package/dist/analysis/hotspot-analyzer.d.ts +28 -0
- package/dist/analysis/hotspot-analyzer.js +85 -0
- package/dist/analysis/index.d.ts +13 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/pattern-detector.d.ts +10 -0
- package/dist/analysis/pattern-detector.js +86 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.js +5 -0
- package/dist/audit-archival.d.ts +48 -0
- package/dist/audit-archival.js +72 -0
- package/dist/audit-export.d.ts +45 -0
- package/dist/audit-export.js +100 -0
- package/dist/audit-extended.d.ts +28 -0
- package/dist/audit-extended.js +38 -0
- package/dist/audit-scheduler.d.ts +32 -0
- package/dist/audit-scheduler.js +52 -0
- package/dist/audit-sqlite-sink.d.ts +11 -0
- package/dist/audit-sqlite-sink.js +51 -0
- package/dist/autonomy-tracker.d.ts +76 -0
- package/dist/autonomy-tracker.js +277 -0
- package/dist/check-runs.d.ts +37 -0
- package/dist/check-runs.js +72 -0
- package/dist/cli/commands/agents.d.ts +6 -0
- package/dist/cli/commands/agents.js +33 -0
- package/dist/cli/commands/complexity.d.ts +6 -0
- package/dist/cli/commands/complexity.js +35 -0
- package/dist/cli/commands/cost.d.ts +6 -0
- package/dist/cli/commands/cost.js +53 -0
- package/dist/cli/commands/dashboard.d.ts +9 -0
- package/dist/cli/commands/dashboard.js +50 -0
- package/dist/cli/commands/health.d.ts +6 -0
- package/dist/cli/commands/health.js +33 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/routing.d.ts +6 -0
- package/dist/cli/commands/routing.js +46 -0
- package/dist/cli/commands/run.d.ts +6 -0
- package/dist/cli/commands/run.js +36 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.js +34 -0
- package/dist/cli/dashboard-renderer.d.ts +24 -0
- package/dist/cli/dashboard-renderer.js +129 -0
- package/dist/cli/formatters/index.d.ts +6 -0
- package/dist/cli/formatters/index.js +18 -0
- package/dist/cli/formatters/json.d.ts +5 -0
- package/dist/cli/formatters/json.js +7 -0
- package/dist/cli/formatters/minimal.d.ts +5 -0
- package/dist/cli/formatters/minimal.js +36 -0
- package/dist/cli/formatters/table.d.ts +5 -0
- package/dist/cli/formatters/table.js +184 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/compliance-extended.d.ts +41 -0
- package/dist/compliance-extended.js +58 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +66 -0
- package/dist/context-enrichment.d.ts +33 -0
- package/dist/context-enrichment.js +125 -0
- package/dist/cost-governance.d.ts +22 -0
- package/dist/cost-governance.js +106 -0
- package/dist/cost-tracker.d.ts +75 -0
- package/dist/cost-tracker.js +177 -0
- package/dist/defaults.d.ts +140 -0
- package/dist/defaults.js +218 -0
- package/dist/deploy/flyio-target.d.ts +19 -0
- package/dist/deploy/flyio-target.js +114 -0
- package/dist/deploy/index.d.ts +15 -0
- package/dist/deploy/index.js +11 -0
- package/dist/deploy/kubernetes-target.d.ts +25 -0
- package/dist/deploy/kubernetes-target.js +124 -0
- package/dist/deploy/metrics-collector.d.ts +31 -0
- package/dist/deploy/metrics-collector.js +75 -0
- package/dist/deploy/rollout-controller.d.ts +42 -0
- package/dist/deploy/rollout-controller.js +192 -0
- package/dist/deploy/rollout-types.d.ts +86 -0
- package/dist/deploy/rollout-types.js +5 -0
- package/dist/deploy/types.d.ts +47 -0
- package/dist/deploy/types.js +5 -0
- package/dist/deploy/vercel-target.d.ts +16 -0
- package/dist/deploy/vercel-target.js +115 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.js +46 -0
- package/dist/episodic-enhanced.d.ts +88 -0
- package/dist/episodic-enhanced.js +212 -0
- package/dist/execute.d.ts +81 -0
- package/dist/execute.js +703 -0
- package/dist/fix-ci.d.ts +61 -0
- package/dist/fix-ci.js +380 -0
- package/dist/handoff-executor.d.ts +61 -0
- package/dist/handoff-executor.js +119 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +87 -0
- package/dist/instrumented.d.ts +27 -0
- package/dist/instrumented.js +34 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.js +37 -0
- package/dist/multi-repo/impact-analyzer.d.ts +22 -0
- package/dist/multi-repo/impact-analyzer.js +95 -0
- package/dist/multi-repo/index.d.ts +5 -0
- package/dist/multi-repo/index.js +4 -0
- package/dist/multi-repo/monorepo-detector.d.ts +16 -0
- package/dist/multi-repo/monorepo-detector.js +246 -0
- package/dist/multi-repo/service-map-builder.d.ts +24 -0
- package/dist/multi-repo/service-map-builder.js +235 -0
- package/dist/multi-repo/types.d.ts +54 -0
- package/dist/multi-repo/types.js +5 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/notification-router.d.ts +62 -0
- package/dist/notifications/notification-router.js +206 -0
- package/dist/notifications/slack-messenger.d.ts +26 -0
- package/dist/notifications/slack-messenger.js +69 -0
- package/dist/notifications/teams-messenger.d.ts +21 -0
- package/dist/notifications/teams-messenger.js +85 -0
- package/dist/notifications.d.ts +13 -0
- package/dist/notifications.js +15 -0
- package/dist/orchestration.d.ts +20 -0
- package/dist/orchestration.js +53 -0
- package/dist/orchestrator.d.ts +160 -0
- package/dist/orchestrator.js +372 -0
- package/dist/otel-exporter.d.ts +37 -0
- package/dist/otel-exporter.js +128 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +8 -0
- package/dist/policy-evaluators.d.ts +40 -0
- package/dist/policy-evaluators.js +57 -0
- package/dist/process-escalation.d.ts +38 -0
- package/dist/process-escalation.js +129 -0
- package/dist/progressive-gates.d.ts +54 -0
- package/dist/progressive-gates.js +135 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +61 -0
- package/dist/reconcilers.d.ts +28 -0
- package/dist/reconcilers.js +37 -0
- package/dist/runners/claude-code.d.ts +17 -0
- package/dist/runners/claude-code.js +200 -0
- package/dist/runners/codex.d.ts +19 -0
- package/dist/runners/codex.js +153 -0
- package/dist/runners/copilot-stub.d.ts +9 -0
- package/dist/runners/copilot-stub.js +15 -0
- package/dist/runners/copilot.d.ts +15 -0
- package/dist/runners/copilot.js +119 -0
- package/dist/runners/cursor-stub.d.ts +9 -0
- package/dist/runners/cursor-stub.js +15 -0
- package/dist/runners/cursor.d.ts +20 -0
- package/dist/runners/cursor.js +140 -0
- package/dist/runners/devin-stub.d.ts +9 -0
- package/dist/runners/devin-stub.js +15 -0
- package/dist/runners/generic-llm.d.ts +51 -0
- package/dist/runners/generic-llm.js +125 -0
- package/dist/runners/index.d.ts +8 -0
- package/dist/runners/index.js +7 -0
- package/dist/runners/runner-registry.d.ts +50 -0
- package/dist/runners/runner-registry.js +156 -0
- package/dist/runners/types.d.ts +59 -0
- package/dist/runners/types.js +7 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +70 -0
- package/dist/shared.d.ts +134 -0
- package/dist/shared.js +284 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +3 -0
- package/dist/state/schema.d.ts +16 -0
- package/dist/state/schema.js +264 -0
- package/dist/state/store.d.ts +109 -0
- package/dist/state/store.js +644 -0
- package/dist/state/types.d.ts +190 -0
- package/dist/state/types.js +5 -0
- package/dist/structured-logger.d.ts +20 -0
- package/dist/structured-logger.js +64 -0
- package/dist/task-decomposer.d.ts +61 -0
- package/dist/task-decomposer.js +265 -0
- package/dist/telemetry-extended.d.ts +24 -0
- package/dist/telemetry-extended.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +7 -0
- package/dist/validate-agent-output.d.ts +30 -0
- package/dist/validate-agent-output.js +81 -0
- package/dist/validate-issue.d.ts +25 -0
- package/dist/validate-issue.js +102 -0
- package/dist/watch.d.ts +43 -0
- package/dist/watch.js +99 -0
- package/dist/webhook-manager.d.ts +38 -0
- package/dist/webhook-manager.js +66 -0
- package/package.json +59 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic architectural pattern detection.
|
|
3
|
+
* Detects hexagonal, layered, event-driven, MVC, microservices patterns.
|
|
4
|
+
*/
|
|
5
|
+
import type { FileInfo, ModuleInfo, ArchitecturalPattern } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detect architectural patterns from file and module structure.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectPatterns(files: FileInfo[], modules: ModuleInfo[]): ArchitecturalPattern[];
|
|
10
|
+
//# sourceMappingURL=pattern-detector.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic architectural pattern detection.
|
|
3
|
+
* Detects hexagonal, layered, event-driven, MVC, microservices patterns.
|
|
4
|
+
*/
|
|
5
|
+
const PATTERN_SIGNALS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'hexagonal',
|
|
8
|
+
dirPatterns: [/(?:^|\/)(?:adapters?|ports?|domain|infrastructure|application)\/?/i],
|
|
9
|
+
filePatterns: [/(?:adapter|port|gateway|repository)\.\w+$/i],
|
|
10
|
+
description: (ev) => `Ports and adapters detected in ${ev.slice(0, 3).join(', ')}`,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'layered',
|
|
14
|
+
dirPatterns: [/(?:^|\/)(?:controllers?|services?|repositories?|models?|middleware)\/?/i],
|
|
15
|
+
filePatterns: [/(?:controller|service|repository|model|middleware)\.\w+$/i],
|
|
16
|
+
description: (ev) => `Layered architecture in ${ev.slice(0, 3).join(', ')}`,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'event-driven',
|
|
20
|
+
dirPatterns: [/(?:^|\/)(?:events?|handlers?|listeners?|subscribers?|emitters?)\/?/i],
|
|
21
|
+
filePatterns: [/(?:event|handler|listener|subscriber|emitter|bus)\.\w+$/i],
|
|
22
|
+
description: (ev) => `Event-driven patterns in ${ev.slice(0, 3).join(', ')}`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'mvc',
|
|
26
|
+
dirPatterns: [/(?:^|\/)(?:views?|controllers?|models?)\/?/i],
|
|
27
|
+
filePatterns: [/(?:view|controller|model)\.\w+$/i],
|
|
28
|
+
description: (ev) => `MVC structure in ${ev.slice(0, 3).join(', ')}`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'plugin-based',
|
|
32
|
+
dirPatterns: [/(?:^|\/)(?:plugins?|extensions?|addons?|modules?)\/?/i],
|
|
33
|
+
filePatterns: [/(?:plugin|extension|addon)\.\w+$/i],
|
|
34
|
+
description: (ev) => `Plugin architecture in ${ev.slice(0, 3).join(', ')}`,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Detect architectural patterns from file and module structure.
|
|
39
|
+
*/
|
|
40
|
+
export function detectPatterns(files, modules) {
|
|
41
|
+
const patterns = [];
|
|
42
|
+
const totalFiles = files.length;
|
|
43
|
+
if (totalFiles === 0)
|
|
44
|
+
return patterns;
|
|
45
|
+
for (const signal of PATTERN_SIGNALS) {
|
|
46
|
+
const evidence = [];
|
|
47
|
+
// Check directory/module names
|
|
48
|
+
for (const mod of modules) {
|
|
49
|
+
for (const re of signal.dirPatterns) {
|
|
50
|
+
if (re.test(mod.path)) {
|
|
51
|
+
evidence.push(mod.path);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Check file paths
|
|
56
|
+
for (const file of files) {
|
|
57
|
+
for (const re of signal.dirPatterns) {
|
|
58
|
+
if (re.test(file.relativePath) && !evidence.includes(file.relativePath)) {
|
|
59
|
+
evidence.push(file.relativePath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const re of signal.filePatterns) {
|
|
63
|
+
if (re.test(file.relativePath) && !evidence.includes(file.relativePath)) {
|
|
64
|
+
evidence.push(file.relativePath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (evidence.length === 0)
|
|
69
|
+
continue;
|
|
70
|
+
// Confidence based on evidence density
|
|
71
|
+
const confidence = Math.min(evidence.length / Math.max(totalFiles * 0.1, 3), 1);
|
|
72
|
+
const rounded = Math.round(confidence * 100) / 100;
|
|
73
|
+
if (rounded >= 0.1) {
|
|
74
|
+
patterns.push({
|
|
75
|
+
name: signal.name,
|
|
76
|
+
confidence: rounded,
|
|
77
|
+
description: signal.description(evidence),
|
|
78
|
+
evidence: evidence.slice(0, 10),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Sort by confidence descending
|
|
83
|
+
patterns.sort((a, b) => b.confidence - a.confidence);
|
|
84
|
+
return patterns;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=pattern-detector.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for codebase analysis — the data model for Phase 1 context & routing.
|
|
3
|
+
*/
|
|
4
|
+
/** Information about a module boundary (directory with index.ts or package.json). */
|
|
5
|
+
export interface ModuleInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
path: string;
|
|
8
|
+
fileCount: number;
|
|
9
|
+
dependencies: string[];
|
|
10
|
+
dependents: string[];
|
|
11
|
+
}
|
|
12
|
+
/** A file that changes frequently and/or has high complexity. */
|
|
13
|
+
export interface Hotspot {
|
|
14
|
+
filePath: string;
|
|
15
|
+
churnRate: number;
|
|
16
|
+
complexity: number;
|
|
17
|
+
commitCount: number;
|
|
18
|
+
lastModified?: string;
|
|
19
|
+
note?: string;
|
|
20
|
+
}
|
|
21
|
+
/** An architectural pattern detected in the codebase. */
|
|
22
|
+
export interface ArchitecturalPattern {
|
|
23
|
+
name: string;
|
|
24
|
+
confidence: number;
|
|
25
|
+
description: string;
|
|
26
|
+
evidence: string[];
|
|
27
|
+
}
|
|
28
|
+
/** A detected coding convention. */
|
|
29
|
+
export interface DetectedConvention {
|
|
30
|
+
category: 'naming' | 'testing' | 'imports' | 'structure' | 'formatting';
|
|
31
|
+
pattern: string;
|
|
32
|
+
confidence: number;
|
|
33
|
+
examples: string[];
|
|
34
|
+
}
|
|
35
|
+
/** Dependency edge in the module graph. */
|
|
36
|
+
export interface DependencyEdge {
|
|
37
|
+
from: string;
|
|
38
|
+
to: string;
|
|
39
|
+
importCount: number;
|
|
40
|
+
}
|
|
41
|
+
/** Module dependency graph. */
|
|
42
|
+
export interface ModuleGraph {
|
|
43
|
+
modules: ModuleInfo[];
|
|
44
|
+
edges: DependencyEdge[];
|
|
45
|
+
externalDependencies: string[];
|
|
46
|
+
cycles: string[][];
|
|
47
|
+
}
|
|
48
|
+
/** Full codebase analysis profile — persisted in state store. */
|
|
49
|
+
export interface CodebaseProfile {
|
|
50
|
+
repoPath: string;
|
|
51
|
+
score: number;
|
|
52
|
+
filesCount: number;
|
|
53
|
+
modulesCount: number;
|
|
54
|
+
dependencyCount: number;
|
|
55
|
+
modules: ModuleInfo[];
|
|
56
|
+
moduleGraph: ModuleGraph;
|
|
57
|
+
architecturalPatterns: ArchitecturalPattern[];
|
|
58
|
+
hotspots: Hotspot[];
|
|
59
|
+
conventions: DetectedConvention[];
|
|
60
|
+
analyzedAt: string;
|
|
61
|
+
}
|
|
62
|
+
/** Context injected into agent prompts. */
|
|
63
|
+
export interface CodebaseContext {
|
|
64
|
+
score: number;
|
|
65
|
+
filesCount: number;
|
|
66
|
+
modulesCount: number;
|
|
67
|
+
dependencyCount: number;
|
|
68
|
+
architectureSummary: string;
|
|
69
|
+
conventionsSummary: string;
|
|
70
|
+
hotspotsSummary: string;
|
|
71
|
+
}
|
|
72
|
+
/** Options for the analyzer facade. */
|
|
73
|
+
export interface AnalyzerOptions {
|
|
74
|
+
repoPath: string;
|
|
75
|
+
include?: string[];
|
|
76
|
+
exclude?: string[];
|
|
77
|
+
gitHistoryDays?: number;
|
|
78
|
+
hotspotThreshold?: number;
|
|
79
|
+
}
|
|
80
|
+
/** File info returned by the file walker. */
|
|
81
|
+
export interface FileInfo {
|
|
82
|
+
path: string;
|
|
83
|
+
relativePath: string;
|
|
84
|
+
lineCount: number;
|
|
85
|
+
extension: string;
|
|
86
|
+
}
|
|
87
|
+
/** Import statement found by the dependency parser. */
|
|
88
|
+
export interface ImportStatement {
|
|
89
|
+
source: string;
|
|
90
|
+
specifier: string;
|
|
91
|
+
isExternal: boolean;
|
|
92
|
+
line: number;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit trail archival — compaction of old entries into gzipped archives.
|
|
3
|
+
* Preserves chain root hash for continuity verification.
|
|
4
|
+
*/
|
|
5
|
+
import type { AuditEntry } from '@ai-sdlc/reference';
|
|
6
|
+
export interface ArchiveManifest {
|
|
7
|
+
/** Identifier for this archive. */
|
|
8
|
+
archiveId: string;
|
|
9
|
+
/** Number of entries in the archive. */
|
|
10
|
+
entryCount: number;
|
|
11
|
+
/** First entry timestamp. */
|
|
12
|
+
firstTimestamp: string;
|
|
13
|
+
/** Last entry timestamp. */
|
|
14
|
+
lastTimestamp: string;
|
|
15
|
+
/** Hash of the first entry (chain root for this segment). */
|
|
16
|
+
firstHash: string;
|
|
17
|
+
/** Hash of the last entry (chain tip for this segment). */
|
|
18
|
+
lastHash: string;
|
|
19
|
+
/** When the archive was created. */
|
|
20
|
+
archivedAt: string;
|
|
21
|
+
/** Path to the gzipped archive file. */
|
|
22
|
+
archivePath: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ArchivalOptions {
|
|
25
|
+
/** Entries older than this are eligible for archival. */
|
|
26
|
+
olderThanMs: number;
|
|
27
|
+
/** Base directory for archive files. */
|
|
28
|
+
archiveDir: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Archive old audit entries to a gzipped JSONL file.
|
|
32
|
+
* Returns entries that were NOT archived (i.e., still active).
|
|
33
|
+
*/
|
|
34
|
+
export declare function archiveEntries(entries: readonly AuditEntry[], options: ArchivalOptions): Promise<{
|
|
35
|
+
remaining: AuditEntry[];
|
|
36
|
+
manifest: ArchiveManifest | null;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Load entries from a gzipped JSONL archive.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadArchivedEntries(archivePath: string): Promise<AuditEntry[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Verify that an archive's chain links correctly to a continuation hash.
|
|
44
|
+
* The last entry's hash in the archive should match the previousHash
|
|
45
|
+
* of the first remaining entry.
|
|
46
|
+
*/
|
|
47
|
+
export declare function verifyArchiveContinuity(manifest: ArchiveManifest, firstRemainingEntry: AuditEntry | undefined): boolean;
|
|
48
|
+
//# sourceMappingURL=audit-archival.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit trail archival — compaction of old entries into gzipped archives.
|
|
3
|
+
* Preserves chain root hash for continuity verification.
|
|
4
|
+
*/
|
|
5
|
+
import { createGzip, createGunzip } from 'node:zlib';
|
|
6
|
+
import { createReadStream, createWriteStream, existsSync } from 'node:fs';
|
|
7
|
+
import { writeFile, unlink } from 'node:fs/promises';
|
|
8
|
+
import { pipeline } from 'node:stream/promises';
|
|
9
|
+
/**
|
|
10
|
+
* Archive old audit entries to a gzipped JSONL file.
|
|
11
|
+
* Returns entries that were NOT archived (i.e., still active).
|
|
12
|
+
*/
|
|
13
|
+
export async function archiveEntries(entries, options) {
|
|
14
|
+
const cutoff = new Date(Date.now() - options.olderThanMs).toISOString();
|
|
15
|
+
const toArchive = entries.filter((e) => e.timestamp < cutoff);
|
|
16
|
+
const remaining = entries.filter((e) => e.timestamp >= cutoff);
|
|
17
|
+
if (toArchive.length === 0) {
|
|
18
|
+
return { remaining: [...remaining], manifest: null };
|
|
19
|
+
}
|
|
20
|
+
const archiveId = `archive-${Date.now()}`;
|
|
21
|
+
const archivePath = `${options.archiveDir}/${archiveId}.jsonl.gz`;
|
|
22
|
+
// Write JSONL then gzip
|
|
23
|
+
const jsonl = toArchive.map((e) => JSON.stringify(e)).join('\n');
|
|
24
|
+
const tmpPath = `${options.archiveDir}/${archiveId}.jsonl`;
|
|
25
|
+
await writeFile(tmpPath, jsonl, 'utf-8');
|
|
26
|
+
// Gzip the file
|
|
27
|
+
await pipeline(createReadStream(tmpPath), createGzip(), createWriteStream(archivePath));
|
|
28
|
+
await unlink(tmpPath);
|
|
29
|
+
const manifest = {
|
|
30
|
+
archiveId,
|
|
31
|
+
entryCount: toArchive.length,
|
|
32
|
+
firstTimestamp: toArchive[0].timestamp,
|
|
33
|
+
lastTimestamp: toArchive[toArchive.length - 1].timestamp,
|
|
34
|
+
firstHash: toArchive[0].hash ?? '',
|
|
35
|
+
lastHash: toArchive[toArchive.length - 1].hash ?? '',
|
|
36
|
+
archivedAt: new Date().toISOString(),
|
|
37
|
+
archivePath,
|
|
38
|
+
};
|
|
39
|
+
return { remaining: [...remaining], manifest };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Load entries from a gzipped JSONL archive.
|
|
43
|
+
*/
|
|
44
|
+
export async function loadArchivedEntries(archivePath) {
|
|
45
|
+
if (!existsSync(archivePath))
|
|
46
|
+
return [];
|
|
47
|
+
const chunks = [];
|
|
48
|
+
await pipeline(createReadStream(archivePath), createGunzip(), async function* collect(source) {
|
|
49
|
+
for await (const chunk of source) {
|
|
50
|
+
chunks.push(Buffer.from(chunk));
|
|
51
|
+
}
|
|
52
|
+
yield Buffer.alloc(0);
|
|
53
|
+
});
|
|
54
|
+
const content = Buffer.concat(chunks).toString('utf-8');
|
|
55
|
+
if (!content.trim())
|
|
56
|
+
return [];
|
|
57
|
+
return content
|
|
58
|
+
.split('\n')
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.map((line) => JSON.parse(line));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Verify that an archive's chain links correctly to a continuation hash.
|
|
64
|
+
* The last entry's hash in the archive should match the previousHash
|
|
65
|
+
* of the first remaining entry.
|
|
66
|
+
*/
|
|
67
|
+
export function verifyArchiveContinuity(manifest, firstRemainingEntry) {
|
|
68
|
+
if (!firstRemainingEntry)
|
|
69
|
+
return true; // nothing to chain to
|
|
70
|
+
return firstRemainingEntry.previousHash === manifest.lastHash;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=audit-archival.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit trail export — JSON, CSV, JSONL formats with filter support.
|
|
3
|
+
*/
|
|
4
|
+
import type { AuditEntry, AuditFilter, AuditLog } from '@ai-sdlc/reference';
|
|
5
|
+
export type ExportFormat = 'json' | 'csv' | 'jsonl';
|
|
6
|
+
export interface ExportOptions {
|
|
7
|
+
/** Output format. */
|
|
8
|
+
format: ExportFormat;
|
|
9
|
+
/** Optional filter to apply before export. */
|
|
10
|
+
filter?: AuditFilter;
|
|
11
|
+
/** Include hash/integrity fields in output (defaults to true). */
|
|
12
|
+
includeIntegrity?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ComplianceReportOptions {
|
|
15
|
+
/** Report title. */
|
|
16
|
+
title: string;
|
|
17
|
+
/** Audit log to report on. */
|
|
18
|
+
auditLog: AuditLog;
|
|
19
|
+
/** Filter entries for the report period. */
|
|
20
|
+
filter?: AuditFilter;
|
|
21
|
+
/** Include integrity verification result. */
|
|
22
|
+
verifyIntegrity?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ComplianceReport {
|
|
25
|
+
title: string;
|
|
26
|
+
generatedAt: string;
|
|
27
|
+
entryCount: number;
|
|
28
|
+
actorSummary: Record<string, number>;
|
|
29
|
+
actionSummary: Record<string, number>;
|
|
30
|
+
decisionSummary: Record<string, number>;
|
|
31
|
+
integrityResult?: {
|
|
32
|
+
valid: boolean;
|
|
33
|
+
brokenAt?: number;
|
|
34
|
+
};
|
|
35
|
+
entries: AuditEntry[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Export audit entries to the specified format.
|
|
39
|
+
*/
|
|
40
|
+
export declare function exportAuditEntries(entries: readonly AuditEntry[], options: ExportOptions): string;
|
|
41
|
+
/**
|
|
42
|
+
* Generate a compliance-ready audit report.
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateComplianceReport(options: ComplianceReportOptions): ComplianceReport;
|
|
45
|
+
//# sourceMappingURL=audit-export.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit trail export — JSON, CSV, JSONL formats with filter support.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Export audit entries to the specified format.
|
|
6
|
+
*/
|
|
7
|
+
export function exportAuditEntries(entries, options) {
|
|
8
|
+
const includeIntegrity = options.includeIntegrity ?? true;
|
|
9
|
+
const filtered = options.filter
|
|
10
|
+
? entries.filter((e) => matchesFilter(e, options.filter))
|
|
11
|
+
: entries;
|
|
12
|
+
switch (options.format) {
|
|
13
|
+
case 'json':
|
|
14
|
+
return JSON.stringify(filtered.map((e) => formatEntry(e, includeIntegrity)), null, 2);
|
|
15
|
+
case 'jsonl':
|
|
16
|
+
return filtered.map((e) => JSON.stringify(formatEntry(e, includeIntegrity))).join('\n');
|
|
17
|
+
case 'csv': {
|
|
18
|
+
const headers = includeIntegrity
|
|
19
|
+
? ['id', 'timestamp', 'actor', 'action', 'resource', 'decision', 'hash', 'previousHash']
|
|
20
|
+
: ['id', 'timestamp', 'actor', 'action', 'resource', 'decision'];
|
|
21
|
+
const rows = filtered.map((e) => headers.map((h) => csvEscape(String(e[h] ?? ''))).join(','));
|
|
22
|
+
return [headers.join(','), ...rows].join('\n');
|
|
23
|
+
}
|
|
24
|
+
default:
|
|
25
|
+
throw new Error(`Unsupported format: ${options.format}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generate a compliance-ready audit report.
|
|
30
|
+
*/
|
|
31
|
+
export function generateComplianceReport(options) {
|
|
32
|
+
const allEntries = options.auditLog.entries();
|
|
33
|
+
const filtered = options.filter
|
|
34
|
+
? allEntries.filter((e) => matchesFilter(e, options.filter))
|
|
35
|
+
: allEntries;
|
|
36
|
+
const actorSummary = {};
|
|
37
|
+
const actionSummary = {};
|
|
38
|
+
const decisionSummary = {};
|
|
39
|
+
for (const entry of filtered) {
|
|
40
|
+
actorSummary[entry.actor] = (actorSummary[entry.actor] ?? 0) + 1;
|
|
41
|
+
actionSummary[entry.action] = (actionSummary[entry.action] ?? 0) + 1;
|
|
42
|
+
decisionSummary[entry.decision] = (decisionSummary[entry.decision] ?? 0) + 1;
|
|
43
|
+
}
|
|
44
|
+
const report = {
|
|
45
|
+
title: options.title,
|
|
46
|
+
generatedAt: new Date().toISOString(),
|
|
47
|
+
entryCount: filtered.length,
|
|
48
|
+
actorSummary,
|
|
49
|
+
actionSummary,
|
|
50
|
+
decisionSummary,
|
|
51
|
+
entries: [...filtered],
|
|
52
|
+
};
|
|
53
|
+
if (options.verifyIntegrity) {
|
|
54
|
+
const result = options.auditLog.verifyIntegrity();
|
|
55
|
+
report.integrityResult = { valid: result.valid, brokenAt: result.brokenAt };
|
|
56
|
+
}
|
|
57
|
+
return report;
|
|
58
|
+
}
|
|
59
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
60
|
+
function matchesFilter(entry, filter) {
|
|
61
|
+
if (filter.actor && entry.actor !== filter.actor)
|
|
62
|
+
return false;
|
|
63
|
+
if (filter.action && entry.action !== filter.action)
|
|
64
|
+
return false;
|
|
65
|
+
if (filter.resource && entry.resource !== filter.resource)
|
|
66
|
+
return false;
|
|
67
|
+
if (filter.decision && entry.decision !== filter.decision)
|
|
68
|
+
return false;
|
|
69
|
+
if (filter.from && entry.timestamp < filter.from)
|
|
70
|
+
return false;
|
|
71
|
+
if (filter.to && entry.timestamp > filter.to)
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function formatEntry(entry, includeIntegrity) {
|
|
76
|
+
const base = {
|
|
77
|
+
id: entry.id,
|
|
78
|
+
timestamp: entry.timestamp,
|
|
79
|
+
actor: entry.actor,
|
|
80
|
+
action: entry.action,
|
|
81
|
+
resource: entry.resource,
|
|
82
|
+
decision: entry.decision,
|
|
83
|
+
};
|
|
84
|
+
if (entry.details)
|
|
85
|
+
base.details = entry.details;
|
|
86
|
+
if (entry.policy)
|
|
87
|
+
base.policy = entry.policy;
|
|
88
|
+
if (includeIntegrity) {
|
|
89
|
+
base.hash = entry.hash;
|
|
90
|
+
base.previousHash = entry.previousHash;
|
|
91
|
+
}
|
|
92
|
+
return base;
|
|
93
|
+
}
|
|
94
|
+
function csvEscape(value) {
|
|
95
|
+
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
|
96
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=audit-export.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended audit integration — covers file-based persistence, integrity
|
|
3
|
+
* verification, rotation, and hash computation from the reference audit module.
|
|
4
|
+
*/
|
|
5
|
+
import { computeEntryHash, createFileSink, loadEntriesFromFile, verifyFileIntegrity, rotateAuditFile, createAuditLog, type AuditEntry, type AuditFilter, type AuditSink, type AuditLog, type IntegrityResult } from '@ai-sdlc/reference';
|
|
6
|
+
/**
|
|
7
|
+
* Create a file-backed audit log with integrity verification.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createFileAuditLog(filePath: string): AuditLog;
|
|
10
|
+
/**
|
|
11
|
+
* Verify the integrity of an audit log file (hash chain verification).
|
|
12
|
+
*/
|
|
13
|
+
export declare function verifyAuditIntegrity(filePath: string): Promise<IntegrityResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Load all audit entries from a file for querying/analysis.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadAuditEntries(filePath: string): Promise<AuditEntry[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Rotate an audit log file (archive and create new).
|
|
20
|
+
*/
|
|
21
|
+
export declare function rotateAuditLog(filePath: string): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Compute a SHA-256 hash for an audit entry (for manual chain building).
|
|
24
|
+
*/
|
|
25
|
+
export declare function computeAuditHash(entry: AuditEntry, previousHash?: string): string;
|
|
26
|
+
export { computeEntryHash, createFileSink, loadEntriesFromFile, verifyFileIntegrity, rotateAuditFile, createAuditLog, };
|
|
27
|
+
export type { AuditEntry, AuditFilter, AuditSink, AuditLog, IntegrityResult };
|
|
28
|
+
//# sourceMappingURL=audit-extended.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended audit integration — covers file-based persistence, integrity
|
|
3
|
+
* verification, rotation, and hash computation from the reference audit module.
|
|
4
|
+
*/
|
|
5
|
+
import { computeEntryHash, createFileSink, loadEntriesFromFile, verifyFileIntegrity, rotateAuditFile, createAuditLog, } from '@ai-sdlc/reference';
|
|
6
|
+
/**
|
|
7
|
+
* Create a file-backed audit log with integrity verification.
|
|
8
|
+
*/
|
|
9
|
+
export function createFileAuditLog(filePath) {
|
|
10
|
+
const sink = createFileSink(filePath);
|
|
11
|
+
return createAuditLog(sink);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Verify the integrity of an audit log file (hash chain verification).
|
|
15
|
+
*/
|
|
16
|
+
export async function verifyAuditIntegrity(filePath) {
|
|
17
|
+
return verifyFileIntegrity(filePath);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load all audit entries from a file for querying/analysis.
|
|
21
|
+
*/
|
|
22
|
+
export async function loadAuditEntries(filePath) {
|
|
23
|
+
return loadEntriesFromFile(filePath);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Rotate an audit log file (archive and create new).
|
|
27
|
+
*/
|
|
28
|
+
export async function rotateAuditLog(filePath) {
|
|
29
|
+
return rotateAuditFile(filePath);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compute a SHA-256 hash for an audit entry (for manual chain building).
|
|
33
|
+
*/
|
|
34
|
+
export function computeAuditHash(entry, previousHash) {
|
|
35
|
+
return computeEntryHash(entry, previousHash);
|
|
36
|
+
}
|
|
37
|
+
export { computeEntryHash, createFileSink, loadEntriesFromFile, verifyFileIntegrity, rotateAuditFile, createAuditLog, };
|
|
38
|
+
//# sourceMappingURL=audit-extended.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic audit integrity verification and key rotation scheduler.
|
|
3
|
+
*/
|
|
4
|
+
import type { AuditLog, IntegrityResult } from '@ai-sdlc/reference';
|
|
5
|
+
export interface AuditSchedulerConfig {
|
|
6
|
+
/** Audit log to verify. */
|
|
7
|
+
auditLog: AuditLog;
|
|
8
|
+
/** Interval in ms between integrity checks (defaults to 60000). */
|
|
9
|
+
verifyIntervalMs?: number;
|
|
10
|
+
/** Callback when integrity check fails. */
|
|
11
|
+
onIntegrityFailure?: (result: IntegrityResult) => void;
|
|
12
|
+
/** Callback when integrity check succeeds. */
|
|
13
|
+
onIntegritySuccess?: (result: IntegrityResult) => void;
|
|
14
|
+
/** Optional key rotation callback (called on rotation interval). */
|
|
15
|
+
onRotation?: () => void;
|
|
16
|
+
/** Interval in ms between key rotations (0 = disabled). */
|
|
17
|
+
rotationIntervalMs?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface AuditScheduler {
|
|
20
|
+
/** Start the scheduler. */
|
|
21
|
+
start(): void;
|
|
22
|
+
/** Stop the scheduler. */
|
|
23
|
+
stop(): void;
|
|
24
|
+
/** Run a manual integrity check. */
|
|
25
|
+
verify(): IntegrityResult;
|
|
26
|
+
/** Whether the scheduler is currently running. */
|
|
27
|
+
readonly running: boolean;
|
|
28
|
+
/** Number of checks performed. */
|
|
29
|
+
readonly checkCount: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function createAuditScheduler(config: AuditSchedulerConfig): AuditScheduler;
|
|
32
|
+
//# sourceMappingURL=audit-scheduler.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic audit integrity verification and key rotation scheduler.
|
|
3
|
+
*/
|
|
4
|
+
export function createAuditScheduler(config) {
|
|
5
|
+
const verifyIntervalMs = config.verifyIntervalMs ?? 60_000;
|
|
6
|
+
const rotationIntervalMs = config.rotationIntervalMs ?? 0;
|
|
7
|
+
let verifyTimer = null;
|
|
8
|
+
let rotationTimer = null;
|
|
9
|
+
let _running = false;
|
|
10
|
+
let _checkCount = 0;
|
|
11
|
+
function verify() {
|
|
12
|
+
_checkCount++;
|
|
13
|
+
const result = config.auditLog.verifyIntegrity();
|
|
14
|
+
if (result.valid) {
|
|
15
|
+
config.onIntegritySuccess?.(result);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
config.onIntegrityFailure?.(result);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
start() {
|
|
24
|
+
if (_running)
|
|
25
|
+
return;
|
|
26
|
+
_running = true;
|
|
27
|
+
verifyTimer = setInterval(verify, verifyIntervalMs);
|
|
28
|
+
if (rotationIntervalMs > 0 && config.onRotation) {
|
|
29
|
+
rotationTimer = setInterval(config.onRotation, rotationIntervalMs);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
stop() {
|
|
33
|
+
_running = false;
|
|
34
|
+
if (verifyTimer) {
|
|
35
|
+
clearInterval(verifyTimer);
|
|
36
|
+
verifyTimer = null;
|
|
37
|
+
}
|
|
38
|
+
if (rotationTimer) {
|
|
39
|
+
clearInterval(rotationTimer);
|
|
40
|
+
rotationTimer = null;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
verify,
|
|
44
|
+
get running() {
|
|
45
|
+
return _running;
|
|
46
|
+
},
|
|
47
|
+
get checkCount() {
|
|
48
|
+
return _checkCount;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=audit-scheduler.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed audit sink using the state store's audit_entries table.
|
|
3
|
+
* Provides indexed queries by actor, action, resource, and time range.
|
|
4
|
+
*/
|
|
5
|
+
import type { AuditSink } from '@ai-sdlc/reference';
|
|
6
|
+
import type { StateStore } from './state/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Create an audit sink that persists entries to the SQLite state store.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createSqliteAuditSink(store: StateStore): AuditSink;
|
|
11
|
+
//# sourceMappingURL=audit-sqlite-sink.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed audit sink using the state store's audit_entries table.
|
|
3
|
+
* Provides indexed queries by actor, action, resource, and time range.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create an audit sink that persists entries to the SQLite state store.
|
|
7
|
+
*/
|
|
8
|
+
export function createSqliteAuditSink(store) {
|
|
9
|
+
return {
|
|
10
|
+
write(entry) {
|
|
11
|
+
store.saveAuditEntry({
|
|
12
|
+
entryId: entry.id,
|
|
13
|
+
actor: entry.actor,
|
|
14
|
+
action: entry.action,
|
|
15
|
+
resourceType: entry.resource.split('/')[0],
|
|
16
|
+
resourceId: entry.resource,
|
|
17
|
+
detail: entry.details ? JSON.stringify(entry.details) : undefined,
|
|
18
|
+
hash: entry.hash,
|
|
19
|
+
previousHash: entry.previousHash,
|
|
20
|
+
createdAt: entry.timestamp,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async query(filter) {
|
|
24
|
+
const records = store.queryAuditEntries({
|
|
25
|
+
actor: filter.actor,
|
|
26
|
+
action: filter.action,
|
|
27
|
+
resourceType: filter.resource?.split('/')[0],
|
|
28
|
+
since: filter.from,
|
|
29
|
+
until: filter.to,
|
|
30
|
+
});
|
|
31
|
+
return records
|
|
32
|
+
.filter((r) => {
|
|
33
|
+
if (filter.resource && r.resourceId !== filter.resource)
|
|
34
|
+
return false;
|
|
35
|
+
return true;
|
|
36
|
+
})
|
|
37
|
+
.map((r) => ({
|
|
38
|
+
id: r.entryId,
|
|
39
|
+
timestamp: r.createdAt ?? '',
|
|
40
|
+
actor: r.actor,
|
|
41
|
+
action: r.action,
|
|
42
|
+
resource: r.resourceId ?? r.resourceType ?? '',
|
|
43
|
+
decision: 'allowed',
|
|
44
|
+
details: r.detail ? JSON.parse(r.detail) : undefined,
|
|
45
|
+
hash: r.hash,
|
|
46
|
+
previousHash: r.previousHash,
|
|
47
|
+
}));
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=audit-sqlite-sink.js.map
|