@synth-deploy/server 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/agent/debrief-retention.d.ts +12 -0
- package/dist/agent/debrief-retention.d.ts.map +1 -0
- package/dist/agent/debrief-retention.js +27 -0
- package/dist/agent/debrief-retention.js.map +1 -0
- package/dist/agent/envoy-client.d.ts +216 -0
- package/dist/agent/envoy-client.d.ts.map +1 -0
- package/dist/agent/envoy-client.js +266 -0
- package/dist/agent/envoy-client.js.map +1 -0
- package/dist/agent/envoy-registry.d.ts +102 -0
- package/dist/agent/envoy-registry.d.ts.map +1 -0
- package/dist/agent/envoy-registry.js +319 -0
- package/dist/agent/envoy-registry.js.map +1 -0
- package/dist/agent/health-checker.d.ts +39 -0
- package/dist/agent/health-checker.d.ts.map +1 -0
- package/dist/agent/health-checker.js +49 -0
- package/dist/agent/health-checker.js.map +1 -0
- package/dist/agent/mcp-client-manager.d.ts +36 -0
- package/dist/agent/mcp-client-manager.d.ts.map +1 -0
- package/dist/agent/mcp-client-manager.js +106 -0
- package/dist/agent/mcp-client-manager.js.map +1 -0
- package/dist/agent/stale-deployment-detector.d.ts +15 -0
- package/dist/agent/stale-deployment-detector.d.ts.map +1 -0
- package/dist/agent/stale-deployment-detector.js +50 -0
- package/dist/agent/stale-deployment-detector.js.map +1 -0
- package/dist/agent/step-runner.d.ts +31 -0
- package/dist/agent/step-runner.d.ts.map +1 -0
- package/dist/agent/step-runner.js +80 -0
- package/dist/agent/step-runner.js.map +1 -0
- package/dist/agent/synth-agent.d.ts +168 -0
- package/dist/agent/synth-agent.d.ts.map +1 -0
- package/dist/agent/synth-agent.js +1195 -0
- package/dist/agent/synth-agent.js.map +1 -0
- package/dist/api/agent.d.ts +36 -0
- package/dist/api/agent.d.ts.map +1 -0
- package/dist/api/agent.js +867 -0
- package/dist/api/agent.js.map +1 -0
- package/dist/api/api-keys.d.ts +4 -0
- package/dist/api/api-keys.d.ts.map +1 -0
- package/dist/api/api-keys.js +118 -0
- package/dist/api/api-keys.js.map +1 -0
- package/dist/api/artifacts.d.ts +5 -0
- package/dist/api/artifacts.d.ts.map +1 -0
- package/dist/api/artifacts.js +142 -0
- package/dist/api/artifacts.js.map +1 -0
- package/dist/api/auth.d.ts +4 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +280 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/deployments.d.ts +11 -0
- package/dist/api/deployments.d.ts.map +1 -0
- package/dist/api/deployments.js +1098 -0
- package/dist/api/deployments.js.map +1 -0
- package/dist/api/environments.d.ts +5 -0
- package/dist/api/environments.d.ts.map +1 -0
- package/dist/api/environments.js +69 -0
- package/dist/api/environments.js.map +1 -0
- package/dist/api/envoy-reports.d.ts +17 -0
- package/dist/api/envoy-reports.d.ts.map +1 -0
- package/dist/api/envoy-reports.js +138 -0
- package/dist/api/envoy-reports.js.map +1 -0
- package/dist/api/envoys.d.ts +5 -0
- package/dist/api/envoys.d.ts.map +1 -0
- package/dist/api/envoys.js +192 -0
- package/dist/api/envoys.js.map +1 -0
- package/dist/api/fleet.d.ts +11 -0
- package/dist/api/fleet.d.ts.map +1 -0
- package/dist/api/fleet.js +394 -0
- package/dist/api/fleet.js.map +1 -0
- package/dist/api/graph.d.ts +8 -0
- package/dist/api/graph.d.ts.map +1 -0
- package/dist/api/graph.js +355 -0
- package/dist/api/graph.js.map +1 -0
- package/dist/api/health.d.ts +20 -0
- package/dist/api/health.d.ts.map +1 -0
- package/dist/api/health.js +248 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/idp-schemas.d.ts +41 -0
- package/dist/api/idp-schemas.d.ts.map +1 -0
- package/dist/api/idp-schemas.js +17 -0
- package/dist/api/idp-schemas.js.map +1 -0
- package/dist/api/idp.d.ts +6 -0
- package/dist/api/idp.d.ts.map +1 -0
- package/dist/api/idp.js +620 -0
- package/dist/api/idp.js.map +1 -0
- package/dist/api/intake.d.ts +10 -0
- package/dist/api/intake.d.ts.map +1 -0
- package/dist/api/intake.js +418 -0
- package/dist/api/intake.js.map +1 -0
- package/dist/api/partitions.d.ts +5 -0
- package/dist/api/partitions.d.ts.map +1 -0
- package/dist/api/partitions.js +113 -0
- package/dist/api/partitions.js.map +1 -0
- package/dist/api/progress-event-store.d.ts +62 -0
- package/dist/api/progress-event-store.d.ts.map +1 -0
- package/dist/api/progress-event-store.js +118 -0
- package/dist/api/progress-event-store.js.map +1 -0
- package/dist/api/schemas.d.ts +1000 -0
- package/dist/api/schemas.d.ts.map +1 -0
- package/dist/api/schemas.js +328 -0
- package/dist/api/schemas.js.map +1 -0
- package/dist/api/security-boundaries.d.ts +4 -0
- package/dist/api/security-boundaries.d.ts.map +1 -0
- package/dist/api/security-boundaries.js +32 -0
- package/dist/api/security-boundaries.js.map +1 -0
- package/dist/api/settings.d.ts +4 -0
- package/dist/api/settings.d.ts.map +1 -0
- package/dist/api/settings.js +99 -0
- package/dist/api/settings.js.map +1 -0
- package/dist/api/system.d.ts +75 -0
- package/dist/api/system.d.ts.map +1 -0
- package/dist/api/system.js +558 -0
- package/dist/api/system.js.map +1 -0
- package/dist/api/telemetry.d.ts +4 -0
- package/dist/api/telemetry.d.ts.map +1 -0
- package/dist/api/telemetry.js +24 -0
- package/dist/api/telemetry.js.map +1 -0
- package/dist/api/users.d.ts +4 -0
- package/dist/api/users.d.ts.map +1 -0
- package/dist/api/users.js +173 -0
- package/dist/api/users.js.map +1 -0
- package/dist/archive-unpacker.d.ts +24 -0
- package/dist/archive-unpacker.d.ts.map +1 -0
- package/dist/archive-unpacker.js +239 -0
- package/dist/archive-unpacker.js.map +1 -0
- package/dist/artifact-analyzer.d.ts +59 -0
- package/dist/artifact-analyzer.d.ts.map +1 -0
- package/dist/artifact-analyzer.js +334 -0
- package/dist/artifact-analyzer.js.map +1 -0
- package/dist/auth/idp/index.d.ts +9 -0
- package/dist/auth/idp/index.d.ts.map +1 -0
- package/dist/auth/idp/index.js +5 -0
- package/dist/auth/idp/index.js.map +1 -0
- package/dist/auth/idp/ldap.d.ts +56 -0
- package/dist/auth/idp/ldap.d.ts.map +1 -0
- package/dist/auth/idp/ldap.js +276 -0
- package/dist/auth/idp/ldap.js.map +1 -0
- package/dist/auth/idp/oidc.d.ts +27 -0
- package/dist/auth/idp/oidc.d.ts.map +1 -0
- package/dist/auth/idp/oidc.js +97 -0
- package/dist/auth/idp/oidc.js.map +1 -0
- package/dist/auth/idp/role-mapping.d.ts +9 -0
- package/dist/auth/idp/role-mapping.d.ts.map +1 -0
- package/dist/auth/idp/role-mapping.js +16 -0
- package/dist/auth/idp/role-mapping.js.map +1 -0
- package/dist/auth/idp/saml.d.ts +40 -0
- package/dist/auth/idp/saml.d.ts.map +1 -0
- package/dist/auth/idp/saml.js +117 -0
- package/dist/auth/idp/saml.js.map +1 -0
- package/dist/auth/idp/types.d.ts +23 -0
- package/dist/auth/idp/types.d.ts.map +1 -0
- package/dist/auth/idp/types.js +2 -0
- package/dist/auth/idp/types.js.map +1 -0
- package/dist/fleet/fleet-executor.d.ts +35 -0
- package/dist/fleet/fleet-executor.d.ts.map +1 -0
- package/dist/fleet/fleet-executor.js +228 -0
- package/dist/fleet/fleet-executor.js.map +1 -0
- package/dist/fleet/fleet-store.d.ts +13 -0
- package/dist/fleet/fleet-store.d.ts.map +1 -0
- package/dist/fleet/fleet-store.js +13 -0
- package/dist/fleet/fleet-store.js.map +1 -0
- package/dist/fleet/index.d.ts +5 -0
- package/dist/fleet/index.d.ts.map +1 -0
- package/dist/fleet/index.js +4 -0
- package/dist/fleet/index.js.map +1 -0
- package/dist/fleet/representative-selector.d.ts +15 -0
- package/dist/fleet/representative-selector.d.ts.map +1 -0
- package/dist/fleet/representative-selector.js +71 -0
- package/dist/fleet/representative-selector.js.map +1 -0
- package/dist/graph/graph-executor.d.ts +36 -0
- package/dist/graph/graph-executor.d.ts.map +1 -0
- package/dist/graph/graph-executor.js +348 -0
- package/dist/graph/graph-executor.js.map +1 -0
- package/dist/graph/graph-inference.d.ts +22 -0
- package/dist/graph/graph-inference.d.ts.map +1 -0
- package/dist/graph/graph-inference.js +149 -0
- package/dist/graph/graph-inference.js.map +1 -0
- package/dist/graph/graph-store.d.ts +12 -0
- package/dist/graph/graph-store.d.ts.map +1 -0
- package/dist/graph/graph-store.js +61 -0
- package/dist/graph/graph-store.js.map +1 -0
- package/dist/graph/index.d.ts +5 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +4 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +837 -0
- package/dist/index.js.map +1 -0
- package/dist/intake/index.d.ts +6 -0
- package/dist/intake/index.d.ts.map +1 -0
- package/dist/intake/index.js +5 -0
- package/dist/intake/index.js.map +1 -0
- package/dist/intake/intake-processor.d.ts +17 -0
- package/dist/intake/intake-processor.d.ts.map +1 -0
- package/dist/intake/intake-processor.js +99 -0
- package/dist/intake/intake-processor.js.map +1 -0
- package/dist/intake/intake-store.d.ts +7 -0
- package/dist/intake/intake-store.d.ts.map +1 -0
- package/dist/intake/intake-store.js +7 -0
- package/dist/intake/intake-store.js.map +1 -0
- package/dist/intake/registry-poller.d.ts +41 -0
- package/dist/intake/registry-poller.d.ts.map +1 -0
- package/dist/intake/registry-poller.js +202 -0
- package/dist/intake/registry-poller.js.map +1 -0
- package/dist/intake/webhook-handlers.d.ts +37 -0
- package/dist/intake/webhook-handlers.d.ts.map +1 -0
- package/dist/intake/webhook-handlers.js +268 -0
- package/dist/intake/webhook-handlers.js.map +1 -0
- package/dist/logger.d.ts +5 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp/resources.d.ts +9 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +72 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +15 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +20 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +88 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/middleware/auth.d.ts +29 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +76 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/permissions.d.ts +13 -0
- package/dist/middleware/permissions.d.ts.map +1 -0
- package/dist/middleware/permissions.js +32 -0
- package/dist/middleware/permissions.js.map +1 -0
- package/dist/pattern-store.d.ts +104 -0
- package/dist/pattern-store.d.ts.map +1 -0
- package/dist/pattern-store.js +299 -0
- package/dist/pattern-store.js.map +1 -0
- package/package.json +54 -0
- package/src/agent/debrief-retention.ts +44 -0
- package/src/agent/envoy-client.ts +474 -0
- package/src/agent/envoy-registry.ts +384 -0
- package/src/agent/health-checker.ts +70 -0
- package/src/agent/mcp-client-manager.ts +131 -0
- package/src/agent/stale-deployment-detector.ts +79 -0
- package/src/agent/step-runner.ts +124 -0
- package/src/agent/synth-agent.ts +1567 -0
- package/src/api/agent.ts +1075 -0
- package/src/api/api-keys.ts +129 -0
- package/src/api/artifacts.ts +194 -0
- package/src/api/auth.ts +320 -0
- package/src/api/deployments.ts +1347 -0
- package/src/api/environments.ts +97 -0
- package/src/api/envoy-reports.ts +159 -0
- package/src/api/envoys.ts +237 -0
- package/src/api/fleet.ts +510 -0
- package/src/api/graph.ts +516 -0
- package/src/api/health.ts +311 -0
- package/src/api/idp-schemas.ts +19 -0
- package/src/api/idp.ts +735 -0
- package/src/api/intake.ts +537 -0
- package/src/api/partitions.ts +147 -0
- package/src/api/progress-event-store.ts +153 -0
- package/src/api/schemas.ts +376 -0
- package/src/api/security-boundaries.ts +54 -0
- package/src/api/settings.ts +118 -0
- package/src/api/system.ts +704 -0
- package/src/api/telemetry.ts +32 -0
- package/src/api/users.ts +210 -0
- package/src/archive-unpacker.ts +271 -0
- package/src/artifact-analyzer.ts +438 -0
- package/src/auth/idp/index.ts +8 -0
- package/src/auth/idp/ldap.ts +340 -0
- package/src/auth/idp/oidc.ts +117 -0
- package/src/auth/idp/role-mapping.ts +22 -0
- package/src/auth/idp/saml.ts +148 -0
- package/src/auth/idp/types.ts +22 -0
- package/src/fleet/fleet-executor.ts +309 -0
- package/src/fleet/fleet-store.ts +13 -0
- package/src/fleet/index.ts +4 -0
- package/src/fleet/representative-selector.ts +83 -0
- package/src/graph/graph-executor.ts +446 -0
- package/src/graph/graph-inference.ts +184 -0
- package/src/graph/graph-store.ts +75 -0
- package/src/graph/index.ts +4 -0
- package/src/index.ts +916 -0
- package/src/intake/index.ts +5 -0
- package/src/intake/intake-processor.ts +111 -0
- package/src/intake/intake-store.ts +7 -0
- package/src/intake/registry-poller.ts +230 -0
- package/src/intake/webhook-handlers.ts +328 -0
- package/src/logger.ts +19 -0
- package/src/mcp/resources.ts +98 -0
- package/src/mcp/server.ts +34 -0
- package/src/mcp/tools.ts +117 -0
- package/src/middleware/auth.ts +103 -0
- package/src/middleware/permissions.ts +35 -0
- package/src/pattern-store.ts +409 -0
- package/tests/agent-mode.test.ts +536 -0
- package/tests/api-handlers.test.ts +1245 -0
- package/tests/archive-unpacker.test.ts +179 -0
- package/tests/artifact-analyzer.test.ts +240 -0
- package/tests/auth-middleware.test.ts +189 -0
- package/tests/decision-diary.test.ts +957 -0
- package/tests/diary-reader.test.ts +782 -0
- package/tests/envoy-client.test.ts +342 -0
- package/tests/envoy-reports.test.ts +156 -0
- package/tests/mcp-tools.test.ts +213 -0
- package/tests/orchestration.test.ts +536 -0
- package/tests/partition-deletion.test.ts +143 -0
- package/tests/partition-isolation.test.ts +830 -0
- package/tests/pattern-store.test.ts +371 -0
- package/tests/rbac-enforcement.test.ts +409 -0
- package/tests/ssrf-validation.test.ts +56 -0
- package/tests/stale-deployment.test.ts +85 -0
- package/tests/step-runner.test.ts +308 -0
- package/tests/ui-journey.test.ts +330 -0
- package/tsconfig.json +11 -0
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Types
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface CorrectionRecord {
|
|
9
|
+
timestamp: Date;
|
|
10
|
+
field: string;
|
|
11
|
+
from: string;
|
|
12
|
+
to: string;
|
|
13
|
+
artifactId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AnalysisPattern {
|
|
17
|
+
id: string;
|
|
18
|
+
source: string;
|
|
19
|
+
artifactType: string;
|
|
20
|
+
namePattern: string;
|
|
21
|
+
corrections: CorrectionRecord[];
|
|
22
|
+
derivedAnalysis: DerivedAnalysis;
|
|
23
|
+
confidence: number;
|
|
24
|
+
appliedCount: number;
|
|
25
|
+
createdAt: Date;
|
|
26
|
+
updatedAt: Date;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The subset of ArtifactAnalysis fields that can be derived from pattern
|
|
31
|
+
* corrections. Each field is optional — only corrected fields are stored.
|
|
32
|
+
*/
|
|
33
|
+
export interface DerivedAnalysis {
|
|
34
|
+
summary?: string;
|
|
35
|
+
dependencies?: string[];
|
|
36
|
+
configurationExpectations?: Record<string, string>;
|
|
37
|
+
deploymentIntent?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PatternMatch {
|
|
41
|
+
pattern: AnalysisPattern;
|
|
42
|
+
/** "auto" when confidence >= 0.7 and >= 2 corrections; "suggest" otherwise */
|
|
43
|
+
mode: "auto" | "suggest";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Confidence calculation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
const INITIAL_CORRECTION_CONFIDENCE = 0.5;
|
|
51
|
+
const CONSISTENT_CORRECTION_BOOST = 0.15;
|
|
52
|
+
const CONTRADICTION_RESET_CONFIDENCE = 0.5;
|
|
53
|
+
const MAX_CONFIDENCE = 0.95;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Recomputes confidence from a correction history.
|
|
57
|
+
*
|
|
58
|
+
* - First correction: 0.5
|
|
59
|
+
* - Each subsequent correction that agrees with the current value: +0.15
|
|
60
|
+
* - A contradictory correction (same field, different `to` value as the
|
|
61
|
+
* last correction for that field) resets to 0.5
|
|
62
|
+
* - Capped at 0.95
|
|
63
|
+
*/
|
|
64
|
+
export function computeConfidence(corrections: CorrectionRecord[]): number {
|
|
65
|
+
if (corrections.length === 0) return 0;
|
|
66
|
+
|
|
67
|
+
let confidence = INITIAL_CORRECTION_CONFIDENCE;
|
|
68
|
+
// Track the latest `to` value per field to detect contradictions
|
|
69
|
+
const latestByField = new Map<string, string>();
|
|
70
|
+
|
|
71
|
+
for (const c of corrections) {
|
|
72
|
+
const prev = latestByField.get(c.field);
|
|
73
|
+
if (prev !== undefined && prev !== c.to) {
|
|
74
|
+
// Contradictory correction — reset
|
|
75
|
+
confidence = CONTRADICTION_RESET_CONFIDENCE;
|
|
76
|
+
} else if (prev !== undefined) {
|
|
77
|
+
// Consistent — boost
|
|
78
|
+
confidence = Math.min(confidence + CONSISTENT_CORRECTION_BOOST, MAX_CONFIDENCE);
|
|
79
|
+
}
|
|
80
|
+
// First correction for this field — confidence stays at initial
|
|
81
|
+
latestByField.set(c.field, c.to);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Math.round(confidence * 100) / 100; // avoid floating-point noise
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// SQLite row shape
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
interface PatternRow {
|
|
92
|
+
id: string;
|
|
93
|
+
source: string;
|
|
94
|
+
artifact_type: string;
|
|
95
|
+
name_pattern: string;
|
|
96
|
+
corrections: string; // JSON
|
|
97
|
+
derived_analysis: string; // JSON
|
|
98
|
+
confidence: number;
|
|
99
|
+
applied_count: number;
|
|
100
|
+
created_at: string;
|
|
101
|
+
updated_at: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function rowToPattern(row: PatternRow): AnalysisPattern {
|
|
105
|
+
const corrections: CorrectionRecord[] = JSON.parse(row.corrections).map(
|
|
106
|
+
(c: { timestamp: string; field: string; from: string; to: string; artifactId: string }) => ({
|
|
107
|
+
...c,
|
|
108
|
+
timestamp: new Date(c.timestamp),
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
id: row.id,
|
|
114
|
+
source: row.source,
|
|
115
|
+
artifactType: row.artifact_type,
|
|
116
|
+
namePattern: row.name_pattern,
|
|
117
|
+
corrections,
|
|
118
|
+
derivedAnalysis: JSON.parse(row.derived_analysis),
|
|
119
|
+
confidence: row.confidence,
|
|
120
|
+
appliedCount: row.applied_count,
|
|
121
|
+
createdAt: new Date(row.created_at),
|
|
122
|
+
updatedAt: new Date(row.updated_at),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// PatternStore
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* SQLite-backed storage for artifact analysis patterns.
|
|
132
|
+
*
|
|
133
|
+
* Patterns capture corrections users make to LLM-generated artifact analyses.
|
|
134
|
+
* When enough consistent corrections accumulate for a given source + type +
|
|
135
|
+
* name combination, the system can auto-apply the learned corrections instead
|
|
136
|
+
* of re-running LLM analysis.
|
|
137
|
+
*/
|
|
138
|
+
export class PatternStore {
|
|
139
|
+
private db: Database.Database;
|
|
140
|
+
private stmts: {
|
|
141
|
+
insert: Database.Statement;
|
|
142
|
+
update: Database.Statement;
|
|
143
|
+
getById: Database.Statement;
|
|
144
|
+
findMatches: Database.Statement;
|
|
145
|
+
incrementApplied: Database.Statement;
|
|
146
|
+
listAll: Database.Statement;
|
|
147
|
+
deleteById: Database.Statement;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
constructor(dbPath: string) {
|
|
151
|
+
this.db = new Database(dbPath);
|
|
152
|
+
this.db.pragma("journal_mode = WAL");
|
|
153
|
+
this.db.pragma("foreign_keys = ON");
|
|
154
|
+
|
|
155
|
+
this.db.exec(`
|
|
156
|
+
CREATE TABLE IF NOT EXISTS analysis_patterns (
|
|
157
|
+
id TEXT PRIMARY KEY,
|
|
158
|
+
source TEXT NOT NULL,
|
|
159
|
+
artifact_type TEXT NOT NULL,
|
|
160
|
+
name_pattern TEXT NOT NULL,
|
|
161
|
+
corrections TEXT NOT NULL DEFAULT '[]',
|
|
162
|
+
derived_analysis TEXT NOT NULL DEFAULT '{}',
|
|
163
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
164
|
+
applied_count INTEGER NOT NULL DEFAULT 0,
|
|
165
|
+
created_at TEXT NOT NULL,
|
|
166
|
+
updated_at TEXT NOT NULL
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_source_type
|
|
170
|
+
ON analysis_patterns(source, artifact_type);
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_confidence
|
|
172
|
+
ON analysis_patterns(confidence);
|
|
173
|
+
`);
|
|
174
|
+
|
|
175
|
+
this.stmts = {
|
|
176
|
+
insert: this.db.prepare(`
|
|
177
|
+
INSERT INTO analysis_patterns
|
|
178
|
+
(id, source, artifact_type, name_pattern, corrections, derived_analysis, confidence, applied_count, created_at, updated_at)
|
|
179
|
+
VALUES
|
|
180
|
+
(@id, @source, @artifact_type, @name_pattern, @corrections, @derived_analysis, @confidence, @applied_count, @created_at, @updated_at)
|
|
181
|
+
`),
|
|
182
|
+
update: this.db.prepare(`
|
|
183
|
+
UPDATE analysis_patterns
|
|
184
|
+
SET corrections = @corrections,
|
|
185
|
+
derived_analysis = @derived_analysis,
|
|
186
|
+
confidence = @confidence,
|
|
187
|
+
updated_at = @updated_at
|
|
188
|
+
WHERE id = @id
|
|
189
|
+
`),
|
|
190
|
+
getById: this.db.prepare(`SELECT * FROM analysis_patterns WHERE id = ?`),
|
|
191
|
+
findMatches: this.db.prepare(`
|
|
192
|
+
SELECT * FROM analysis_patterns
|
|
193
|
+
WHERE source = @source AND artifact_type = @artifact_type
|
|
194
|
+
ORDER BY confidence DESC
|
|
195
|
+
`),
|
|
196
|
+
incrementApplied: this.db.prepare(`
|
|
197
|
+
UPDATE analysis_patterns
|
|
198
|
+
SET applied_count = applied_count + 1, updated_at = @updated_at
|
|
199
|
+
WHERE id = @id
|
|
200
|
+
`),
|
|
201
|
+
listAll: this.db.prepare(`SELECT * FROM analysis_patterns ORDER BY updated_at DESC`),
|
|
202
|
+
deleteById: this.db.prepare(`DELETE FROM analysis_patterns WHERE id = ?`),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Record a correction against a pattern, creating the pattern if it doesn't
|
|
208
|
+
* exist. Returns the updated pattern.
|
|
209
|
+
*/
|
|
210
|
+
recordCorrection(
|
|
211
|
+
key: { source: string; artifactType: string; namePattern: string },
|
|
212
|
+
correction: Omit<CorrectionRecord, "timestamp">,
|
|
213
|
+
): AnalysisPattern {
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const existing = this._findExact(key.source, key.artifactType, key.namePattern);
|
|
216
|
+
|
|
217
|
+
if (existing) {
|
|
218
|
+
const record: CorrectionRecord = { ...correction, timestamp: now };
|
|
219
|
+
const corrections = [...existing.corrections, record];
|
|
220
|
+
const confidence = computeConfidence(corrections);
|
|
221
|
+
|
|
222
|
+
// Rebuild derived analysis from latest corrections
|
|
223
|
+
const derivedAnalysis = this._buildDerivedAnalysis(corrections);
|
|
224
|
+
|
|
225
|
+
this.stmts.update.run({
|
|
226
|
+
id: existing.id,
|
|
227
|
+
corrections: JSON.stringify(corrections),
|
|
228
|
+
derived_analysis: JSON.stringify(derivedAnalysis),
|
|
229
|
+
confidence,
|
|
230
|
+
updated_at: now.toISOString(),
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
...existing,
|
|
235
|
+
corrections,
|
|
236
|
+
derivedAnalysis: derivedAnalysis,
|
|
237
|
+
confidence,
|
|
238
|
+
updatedAt: now,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Create new pattern
|
|
243
|
+
const record: CorrectionRecord = { ...correction, timestamp: now };
|
|
244
|
+
const corrections = [record];
|
|
245
|
+
const confidence = computeConfidence(corrections);
|
|
246
|
+
const derivedAnalysis = this._buildDerivedAnalysis(corrections);
|
|
247
|
+
const id = crypto.randomUUID();
|
|
248
|
+
|
|
249
|
+
this.stmts.insert.run({
|
|
250
|
+
id,
|
|
251
|
+
source: key.source,
|
|
252
|
+
artifact_type: key.artifactType,
|
|
253
|
+
name_pattern: key.namePattern,
|
|
254
|
+
corrections: JSON.stringify(corrections),
|
|
255
|
+
derived_analysis: JSON.stringify(derivedAnalysis),
|
|
256
|
+
confidence,
|
|
257
|
+
applied_count: 0,
|
|
258
|
+
created_at: now.toISOString(),
|
|
259
|
+
updated_at: now.toISOString(),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
id,
|
|
264
|
+
source: key.source,
|
|
265
|
+
artifactType: key.artifactType,
|
|
266
|
+
namePattern: key.namePattern,
|
|
267
|
+
corrections,
|
|
268
|
+
derivedAnalysis: derivedAnalysis,
|
|
269
|
+
confidence,
|
|
270
|
+
appliedCount: 0,
|
|
271
|
+
createdAt: now,
|
|
272
|
+
updatedAt: now,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Find patterns matching a given artifact by source + type, then filter by
|
|
278
|
+
* name glob match. Returns patterns sorted by confidence (descending).
|
|
279
|
+
*/
|
|
280
|
+
findMatches(source: string, artifactType: string, artifactName: string): PatternMatch[] {
|
|
281
|
+
const rows = this.stmts.findMatches.all({ source, artifact_type: artifactType }) as PatternRow[];
|
|
282
|
+
const matches: PatternMatch[] = [];
|
|
283
|
+
|
|
284
|
+
for (const row of rows) {
|
|
285
|
+
const pattern = rowToPattern(row);
|
|
286
|
+
if (this._globMatch(pattern.namePattern, artifactName)) {
|
|
287
|
+
const autoApply =
|
|
288
|
+
pattern.corrections.length >= 2 && pattern.confidence >= 0.7;
|
|
289
|
+
matches.push({
|
|
290
|
+
pattern,
|
|
291
|
+
mode: autoApply ? "auto" : "suggest",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return matches;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Record that a pattern was applied to an artifact.
|
|
301
|
+
*/
|
|
302
|
+
recordApplication(patternId: string): void {
|
|
303
|
+
this.stmts.incrementApplied.run({
|
|
304
|
+
id: patternId,
|
|
305
|
+
updated_at: new Date().toISOString(),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get a pattern by ID.
|
|
311
|
+
*/
|
|
312
|
+
getById(id: string): AnalysisPattern | undefined {
|
|
313
|
+
const row = this.stmts.getById.get(id) as PatternRow | undefined;
|
|
314
|
+
return row ? rowToPattern(row) : undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* List all patterns, most recently updated first.
|
|
319
|
+
*/
|
|
320
|
+
listAll(): AnalysisPattern[] {
|
|
321
|
+
const rows = this.stmts.listAll.all() as PatternRow[];
|
|
322
|
+
return rows.map(rowToPattern);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Delete a pattern.
|
|
327
|
+
*/
|
|
328
|
+
delete(id: string): boolean {
|
|
329
|
+
const result = this.stmts.deleteById.run(id);
|
|
330
|
+
return result.changes > 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Close the database connection.
|
|
335
|
+
*/
|
|
336
|
+
close(): void {
|
|
337
|
+
this.db.close();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// -------------------------------------------------------------------------
|
|
341
|
+
// Private helpers
|
|
342
|
+
// -------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
private _findExact(
|
|
345
|
+
source: string,
|
|
346
|
+
artifactType: string,
|
|
347
|
+
namePattern: string,
|
|
348
|
+
): AnalysisPattern | undefined {
|
|
349
|
+
const rows = this.stmts.findMatches.all({ source, artifact_type: artifactType }) as PatternRow[];
|
|
350
|
+
for (const row of rows) {
|
|
351
|
+
if (row.name_pattern === namePattern) {
|
|
352
|
+
return rowToPattern(row);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Build a DerivedAnalysis by taking the latest correction `to` value for
|
|
360
|
+
* each known field. Supports: summary, deploymentIntent, dependencies
|
|
361
|
+
* (comma-separated), and configurationExpectations (key=value).
|
|
362
|
+
*/
|
|
363
|
+
private _buildDerivedAnalysis(corrections: CorrectionRecord[]): DerivedAnalysis {
|
|
364
|
+
const derived: DerivedAnalysis = {};
|
|
365
|
+
const latestByField = new Map<string, string>();
|
|
366
|
+
|
|
367
|
+
for (const c of corrections) {
|
|
368
|
+
latestByField.set(c.field, c.to);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
for (const [field, value] of latestByField) {
|
|
372
|
+
switch (field) {
|
|
373
|
+
case "summary":
|
|
374
|
+
derived.summary = value;
|
|
375
|
+
break;
|
|
376
|
+
case "deploymentIntent":
|
|
377
|
+
derived.deploymentIntent = value;
|
|
378
|
+
break;
|
|
379
|
+
case "dependencies":
|
|
380
|
+
derived.dependencies = value.split(",").map((d) => d.trim()).filter(Boolean);
|
|
381
|
+
break;
|
|
382
|
+
default:
|
|
383
|
+
// Treat as a configuration expectation
|
|
384
|
+
if (field.startsWith("config.")) {
|
|
385
|
+
if (!derived.configurationExpectations) {
|
|
386
|
+
derived.configurationExpectations = {};
|
|
387
|
+
}
|
|
388
|
+
derived.configurationExpectations[field.slice("config.".length)] = value;
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return derived;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Simple glob matching: supports `*` (any characters) and `?` (single char).
|
|
399
|
+
* Used to match artifact names against stored name patterns.
|
|
400
|
+
*/
|
|
401
|
+
private _globMatch(pattern: string, name: string): boolean {
|
|
402
|
+
// Escape regex special chars except * and ?
|
|
403
|
+
const regexStr = pattern
|
|
404
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
405
|
+
.replace(/\*/g, ".*")
|
|
406
|
+
.replace(/\?/g, ".");
|
|
407
|
+
return new RegExp(`^${regexStr}$`).test(name);
|
|
408
|
+
}
|
|
409
|
+
}
|