@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,76 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify } from "jose";
|
|
2
|
+
const EXEMPT_ROUTES = ["/health", "/api/health", "/api/auth/login", "/api/auth/register", "/api/auth/refresh", "/api/auth/status", "/api/auth/providers", "/api/envoy/report"];
|
|
3
|
+
const EXEMPT_PREFIXES = ["/api/auth/oidc/", "/api/auth/callback/oidc/", "/api/auth/saml/", "/api/auth/callback/saml/", "/api/auth/ldap/", "/api/intake/webhook/"];
|
|
4
|
+
// Envoy callback endpoints — validated by envoy token, not user JWT
|
|
5
|
+
const EXEMPT_PATTERNS = [/^\/api\/deployments\/[^/]+\/progress$/];
|
|
6
|
+
/**
|
|
7
|
+
* Registers JWT-based authentication middleware on a Fastify instance.
|
|
8
|
+
*
|
|
9
|
+
* All /api/ and /mcp routes require a valid JWT Bearer token,
|
|
10
|
+
* except routes listed in EXEMPT_ROUTES. Static file serving and
|
|
11
|
+
* health endpoints are always accessible.
|
|
12
|
+
*/
|
|
13
|
+
export function registerAuthMiddleware(app, userStore, userRoleStore, sessionStore, jwtSecret) {
|
|
14
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
15
|
+
// Skip exempt routes
|
|
16
|
+
if (EXEMPT_ROUTES.some((r) => request.url.startsWith(r)))
|
|
17
|
+
return;
|
|
18
|
+
// Skip OIDC auth routes (dynamic paths)
|
|
19
|
+
if (EXEMPT_PREFIXES.some((p) => request.url.startsWith(p)))
|
|
20
|
+
return;
|
|
21
|
+
// Skip envoy callback endpoints (validated by envoy token in the route handler)
|
|
22
|
+
if (EXEMPT_PATTERNS.some((p) => p.test(request.url)))
|
|
23
|
+
return;
|
|
24
|
+
// Also skip static file serving (non-API routes), but NOT /mcp — it requires auth
|
|
25
|
+
if (!request.url.startsWith("/api/") && !request.url.startsWith("/mcp"))
|
|
26
|
+
return;
|
|
27
|
+
// Accept token from Authorization header or ?token= query param
|
|
28
|
+
// (EventSource API cannot send headers, so SSE endpoints use query param)
|
|
29
|
+
const authHeader = request.headers.authorization;
|
|
30
|
+
const queryToken = request.query?.token;
|
|
31
|
+
const rawToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryToken;
|
|
32
|
+
if (!rawToken) {
|
|
33
|
+
reply.status(401).send({ error: "Authentication required" });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const token = rawToken;
|
|
37
|
+
try {
|
|
38
|
+
const { payload } = await jwtVerify(token, jwtSecret);
|
|
39
|
+
const userId = payload.sub;
|
|
40
|
+
const user = userStore.getById(userId);
|
|
41
|
+
if (!user) {
|
|
42
|
+
reply.status(401).send({ error: "User not found" });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const session = sessionStore.getByToken(token);
|
|
46
|
+
if (!session) {
|
|
47
|
+
reply.status(401).send({ error: "Session expired" });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const permissions = userRoleStore.getUserPermissions(userId);
|
|
51
|
+
request.user = { id: userId, email: user.email, name: user.name, permissions };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
reply.status(401).send({ error: "Invalid token" });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return { enabled: true };
|
|
58
|
+
}
|
|
59
|
+
export async function generateTokens(userId, jwtSecret) {
|
|
60
|
+
const sessionTtl = process.env.SYNTH_SESSION_TTL ?? "8h";
|
|
61
|
+
const sessionTtlMs = sessionTtl.endsWith("h")
|
|
62
|
+
? parseInt(sessionTtl) * 60 * 60 * 1000
|
|
63
|
+
: sessionTtl.endsWith("m")
|
|
64
|
+
? parseInt(sessionTtl) * 60 * 1000
|
|
65
|
+
: 8 * 60 * 60 * 1000;
|
|
66
|
+
const token = await new SignJWT({ sub: userId })
|
|
67
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
68
|
+
.setExpirationTime(sessionTtl)
|
|
69
|
+
.sign(jwtSecret);
|
|
70
|
+
const refreshToken = await new SignJWT({ sub: userId, type: "refresh" })
|
|
71
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
72
|
+
.setExpirationTime("7d")
|
|
73
|
+
.sign(jwtSecret);
|
|
74
|
+
return { token, refreshToken, expiresAt: new Date(Date.now() + sessionTtlMs) };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import type { Permission, EnterpriseFeature } from "@synth-deploy/core";
|
|
3
|
+
/**
|
|
4
|
+
* Returns a Fastify preHandler that checks whether the authenticated user
|
|
5
|
+
* has all of the specified permissions.
|
|
6
|
+
*/
|
|
7
|
+
export declare function requirePermission(...permissions: Permission[]): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a Fastify preHandler that gates an enterprise-only feature.
|
|
10
|
+
* Throws EditionError (caught by global error handler → 402) on Community edition.
|
|
11
|
+
*/
|
|
12
|
+
export declare function requireEdition(feature: EnterpriseFeature): (_request: FastifyRequest, _reply: FastifyReply) => Promise<void>;
|
|
13
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/middleware/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGxE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,WAAW,EAAE,UAAU,EAAE,IAC9C,SAAS,cAAc,EAAE,OAAO,YAAY,mBAe3D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,IACzC,UAAU,cAAc,EAAE,QAAQ,YAAY,mBAG7D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { requireEnterprise } from "@synth-deploy/core";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a Fastify preHandler that checks whether the authenticated user
|
|
4
|
+
* has all of the specified permissions.
|
|
5
|
+
*/
|
|
6
|
+
export function requirePermission(...permissions) {
|
|
7
|
+
return async (request, reply) => {
|
|
8
|
+
const user = request.user;
|
|
9
|
+
if (!user) {
|
|
10
|
+
reply.status(401).send({ error: "Authentication required" });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const missing = permissions.filter((p) => !user.permissions.includes(p));
|
|
14
|
+
if (missing.length > 0) {
|
|
15
|
+
reply.status(403).send({
|
|
16
|
+
error: "Forbidden",
|
|
17
|
+
required: permissions,
|
|
18
|
+
message: `This action requires: ${missing.join(", ")}`,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns a Fastify preHandler that gates an enterprise-only feature.
|
|
25
|
+
* Throws EditionError (caught by global error handler → 402) on Community edition.
|
|
26
|
+
*/
|
|
27
|
+
export function requireEdition(feature) {
|
|
28
|
+
return async (_request, _reply) => {
|
|
29
|
+
requireEnterprise(feature);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/middleware/permissions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAuB,MAAM,oBAAoB,CAAC;AAE5E;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG,WAAyB;IAC5D,OAAO,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACzE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACrB,KAAK,EAAE,WAAW;gBAClB,QAAQ,EAAE,WAAW;gBACrB,OAAO,EAAE,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aACvD,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAA0B;IACvD,OAAO,KAAK,EAAE,QAAwB,EAAE,MAAoB,EAAE,EAAE;QAC9D,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export interface CorrectionRecord {
|
|
2
|
+
timestamp: Date;
|
|
3
|
+
field: string;
|
|
4
|
+
from: string;
|
|
5
|
+
to: string;
|
|
6
|
+
artifactId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalysisPattern {
|
|
9
|
+
id: string;
|
|
10
|
+
source: string;
|
|
11
|
+
artifactType: string;
|
|
12
|
+
namePattern: string;
|
|
13
|
+
corrections: CorrectionRecord[];
|
|
14
|
+
derivedAnalysis: DerivedAnalysis;
|
|
15
|
+
confidence: number;
|
|
16
|
+
appliedCount: number;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
updatedAt: Date;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The subset of ArtifactAnalysis fields that can be derived from pattern
|
|
22
|
+
* corrections. Each field is optional — only corrected fields are stored.
|
|
23
|
+
*/
|
|
24
|
+
export interface DerivedAnalysis {
|
|
25
|
+
summary?: string;
|
|
26
|
+
dependencies?: string[];
|
|
27
|
+
configurationExpectations?: Record<string, string>;
|
|
28
|
+
deploymentIntent?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PatternMatch {
|
|
31
|
+
pattern: AnalysisPattern;
|
|
32
|
+
/** "auto" when confidence >= 0.7 and >= 2 corrections; "suggest" otherwise */
|
|
33
|
+
mode: "auto" | "suggest";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Recomputes confidence from a correction history.
|
|
37
|
+
*
|
|
38
|
+
* - First correction: 0.5
|
|
39
|
+
* - Each subsequent correction that agrees with the current value: +0.15
|
|
40
|
+
* - A contradictory correction (same field, different `to` value as the
|
|
41
|
+
* last correction for that field) resets to 0.5
|
|
42
|
+
* - Capped at 0.95
|
|
43
|
+
*/
|
|
44
|
+
export declare function computeConfidence(corrections: CorrectionRecord[]): number;
|
|
45
|
+
/**
|
|
46
|
+
* SQLite-backed storage for artifact analysis patterns.
|
|
47
|
+
*
|
|
48
|
+
* Patterns capture corrections users make to LLM-generated artifact analyses.
|
|
49
|
+
* When enough consistent corrections accumulate for a given source + type +
|
|
50
|
+
* name combination, the system can auto-apply the learned corrections instead
|
|
51
|
+
* of re-running LLM analysis.
|
|
52
|
+
*/
|
|
53
|
+
export declare class PatternStore {
|
|
54
|
+
private db;
|
|
55
|
+
private stmts;
|
|
56
|
+
constructor(dbPath: string);
|
|
57
|
+
/**
|
|
58
|
+
* Record a correction against a pattern, creating the pattern if it doesn't
|
|
59
|
+
* exist. Returns the updated pattern.
|
|
60
|
+
*/
|
|
61
|
+
recordCorrection(key: {
|
|
62
|
+
source: string;
|
|
63
|
+
artifactType: string;
|
|
64
|
+
namePattern: string;
|
|
65
|
+
}, correction: Omit<CorrectionRecord, "timestamp">): AnalysisPattern;
|
|
66
|
+
/**
|
|
67
|
+
* Find patterns matching a given artifact by source + type, then filter by
|
|
68
|
+
* name glob match. Returns patterns sorted by confidence (descending).
|
|
69
|
+
*/
|
|
70
|
+
findMatches(source: string, artifactType: string, artifactName: string): PatternMatch[];
|
|
71
|
+
/**
|
|
72
|
+
* Record that a pattern was applied to an artifact.
|
|
73
|
+
*/
|
|
74
|
+
recordApplication(patternId: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get a pattern by ID.
|
|
77
|
+
*/
|
|
78
|
+
getById(id: string): AnalysisPattern | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* List all patterns, most recently updated first.
|
|
81
|
+
*/
|
|
82
|
+
listAll(): AnalysisPattern[];
|
|
83
|
+
/**
|
|
84
|
+
* Delete a pattern.
|
|
85
|
+
*/
|
|
86
|
+
delete(id: string): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Close the database connection.
|
|
89
|
+
*/
|
|
90
|
+
close(): void;
|
|
91
|
+
private _findExact;
|
|
92
|
+
/**
|
|
93
|
+
* Build a DerivedAnalysis by taking the latest correction `to` value for
|
|
94
|
+
* each known field. Supports: summary, deploymentIntent, dependencies
|
|
95
|
+
* (comma-separated), and configurationExpectations (key=value).
|
|
96
|
+
*/
|
|
97
|
+
private _buildDerivedAnalysis;
|
|
98
|
+
/**
|
|
99
|
+
* Simple glob matching: supports `*` (any characters) and `?` (single char).
|
|
100
|
+
* Used to match artifact names against stored name patterns.
|
|
101
|
+
*/
|
|
102
|
+
private _globMatch;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=pattern-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-store.d.ts","sourceRoot":"","sources":["../src/pattern-store.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAWD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAqBzE;AA6CD;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,KAAK,CAQX;gBAEU,MAAM,EAAE,MAAM;IAwD1B;;;OAGG;IACH,gBAAgB,CACd,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAClE,UAAU,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAC9C,eAAe;IA+DlB;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,EAAE;IAmBvF;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO1C;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAKhD;;OAEG;IACH,OAAO,IAAI,eAAe,EAAE;IAK5B;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAK3B;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,UAAU;IAclB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;CAQnB"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Confidence calculation
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
const INITIAL_CORRECTION_CONFIDENCE = 0.5;
|
|
7
|
+
const CONSISTENT_CORRECTION_BOOST = 0.15;
|
|
8
|
+
const CONTRADICTION_RESET_CONFIDENCE = 0.5;
|
|
9
|
+
const MAX_CONFIDENCE = 0.95;
|
|
10
|
+
/**
|
|
11
|
+
* Recomputes confidence from a correction history.
|
|
12
|
+
*
|
|
13
|
+
* - First correction: 0.5
|
|
14
|
+
* - Each subsequent correction that agrees with the current value: +0.15
|
|
15
|
+
* - A contradictory correction (same field, different `to` value as the
|
|
16
|
+
* last correction for that field) resets to 0.5
|
|
17
|
+
* - Capped at 0.95
|
|
18
|
+
*/
|
|
19
|
+
export function computeConfidence(corrections) {
|
|
20
|
+
if (corrections.length === 0)
|
|
21
|
+
return 0;
|
|
22
|
+
let confidence = INITIAL_CORRECTION_CONFIDENCE;
|
|
23
|
+
// Track the latest `to` value per field to detect contradictions
|
|
24
|
+
const latestByField = new Map();
|
|
25
|
+
for (const c of corrections) {
|
|
26
|
+
const prev = latestByField.get(c.field);
|
|
27
|
+
if (prev !== undefined && prev !== c.to) {
|
|
28
|
+
// Contradictory correction — reset
|
|
29
|
+
confidence = CONTRADICTION_RESET_CONFIDENCE;
|
|
30
|
+
}
|
|
31
|
+
else if (prev !== undefined) {
|
|
32
|
+
// Consistent — boost
|
|
33
|
+
confidence = Math.min(confidence + CONSISTENT_CORRECTION_BOOST, MAX_CONFIDENCE);
|
|
34
|
+
}
|
|
35
|
+
// First correction for this field — confidence stays at initial
|
|
36
|
+
latestByField.set(c.field, c.to);
|
|
37
|
+
}
|
|
38
|
+
return Math.round(confidence * 100) / 100; // avoid floating-point noise
|
|
39
|
+
}
|
|
40
|
+
function rowToPattern(row) {
|
|
41
|
+
const corrections = JSON.parse(row.corrections).map((c) => ({
|
|
42
|
+
...c,
|
|
43
|
+
timestamp: new Date(c.timestamp),
|
|
44
|
+
}));
|
|
45
|
+
return {
|
|
46
|
+
id: row.id,
|
|
47
|
+
source: row.source,
|
|
48
|
+
artifactType: row.artifact_type,
|
|
49
|
+
namePattern: row.name_pattern,
|
|
50
|
+
corrections,
|
|
51
|
+
derivedAnalysis: JSON.parse(row.derived_analysis),
|
|
52
|
+
confidence: row.confidence,
|
|
53
|
+
appliedCount: row.applied_count,
|
|
54
|
+
createdAt: new Date(row.created_at),
|
|
55
|
+
updatedAt: new Date(row.updated_at),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// PatternStore
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
/**
|
|
62
|
+
* SQLite-backed storage for artifact analysis patterns.
|
|
63
|
+
*
|
|
64
|
+
* Patterns capture corrections users make to LLM-generated artifact analyses.
|
|
65
|
+
* When enough consistent corrections accumulate for a given source + type +
|
|
66
|
+
* name combination, the system can auto-apply the learned corrections instead
|
|
67
|
+
* of re-running LLM analysis.
|
|
68
|
+
*/
|
|
69
|
+
export class PatternStore {
|
|
70
|
+
db;
|
|
71
|
+
stmts;
|
|
72
|
+
constructor(dbPath) {
|
|
73
|
+
this.db = new Database(dbPath);
|
|
74
|
+
this.db.pragma("journal_mode = WAL");
|
|
75
|
+
this.db.pragma("foreign_keys = ON");
|
|
76
|
+
this.db.exec(`
|
|
77
|
+
CREATE TABLE IF NOT EXISTS analysis_patterns (
|
|
78
|
+
id TEXT PRIMARY KEY,
|
|
79
|
+
source TEXT NOT NULL,
|
|
80
|
+
artifact_type TEXT NOT NULL,
|
|
81
|
+
name_pattern TEXT NOT NULL,
|
|
82
|
+
corrections TEXT NOT NULL DEFAULT '[]',
|
|
83
|
+
derived_analysis TEXT NOT NULL DEFAULT '{}',
|
|
84
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
85
|
+
applied_count INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
created_at TEXT NOT NULL,
|
|
87
|
+
updated_at TEXT NOT NULL
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_source_type
|
|
91
|
+
ON analysis_patterns(source, artifact_type);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_confidence
|
|
93
|
+
ON analysis_patterns(confidence);
|
|
94
|
+
`);
|
|
95
|
+
this.stmts = {
|
|
96
|
+
insert: this.db.prepare(`
|
|
97
|
+
INSERT INTO analysis_patterns
|
|
98
|
+
(id, source, artifact_type, name_pattern, corrections, derived_analysis, confidence, applied_count, created_at, updated_at)
|
|
99
|
+
VALUES
|
|
100
|
+
(@id, @source, @artifact_type, @name_pattern, @corrections, @derived_analysis, @confidence, @applied_count, @created_at, @updated_at)
|
|
101
|
+
`),
|
|
102
|
+
update: this.db.prepare(`
|
|
103
|
+
UPDATE analysis_patterns
|
|
104
|
+
SET corrections = @corrections,
|
|
105
|
+
derived_analysis = @derived_analysis,
|
|
106
|
+
confidence = @confidence,
|
|
107
|
+
updated_at = @updated_at
|
|
108
|
+
WHERE id = @id
|
|
109
|
+
`),
|
|
110
|
+
getById: this.db.prepare(`SELECT * FROM analysis_patterns WHERE id = ?`),
|
|
111
|
+
findMatches: this.db.prepare(`
|
|
112
|
+
SELECT * FROM analysis_patterns
|
|
113
|
+
WHERE source = @source AND artifact_type = @artifact_type
|
|
114
|
+
ORDER BY confidence DESC
|
|
115
|
+
`),
|
|
116
|
+
incrementApplied: this.db.prepare(`
|
|
117
|
+
UPDATE analysis_patterns
|
|
118
|
+
SET applied_count = applied_count + 1, updated_at = @updated_at
|
|
119
|
+
WHERE id = @id
|
|
120
|
+
`),
|
|
121
|
+
listAll: this.db.prepare(`SELECT * FROM analysis_patterns ORDER BY updated_at DESC`),
|
|
122
|
+
deleteById: this.db.prepare(`DELETE FROM analysis_patterns WHERE id = ?`),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Record a correction against a pattern, creating the pattern if it doesn't
|
|
127
|
+
* exist. Returns the updated pattern.
|
|
128
|
+
*/
|
|
129
|
+
recordCorrection(key, correction) {
|
|
130
|
+
const now = new Date();
|
|
131
|
+
const existing = this._findExact(key.source, key.artifactType, key.namePattern);
|
|
132
|
+
if (existing) {
|
|
133
|
+
const record = { ...correction, timestamp: now };
|
|
134
|
+
const corrections = [...existing.corrections, record];
|
|
135
|
+
const confidence = computeConfidence(corrections);
|
|
136
|
+
// Rebuild derived analysis from latest corrections
|
|
137
|
+
const derivedAnalysis = this._buildDerivedAnalysis(corrections);
|
|
138
|
+
this.stmts.update.run({
|
|
139
|
+
id: existing.id,
|
|
140
|
+
corrections: JSON.stringify(corrections),
|
|
141
|
+
derived_analysis: JSON.stringify(derivedAnalysis),
|
|
142
|
+
confidence,
|
|
143
|
+
updated_at: now.toISOString(),
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
...existing,
|
|
147
|
+
corrections,
|
|
148
|
+
derivedAnalysis: derivedAnalysis,
|
|
149
|
+
confidence,
|
|
150
|
+
updatedAt: now,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Create new pattern
|
|
154
|
+
const record = { ...correction, timestamp: now };
|
|
155
|
+
const corrections = [record];
|
|
156
|
+
const confidence = computeConfidence(corrections);
|
|
157
|
+
const derivedAnalysis = this._buildDerivedAnalysis(corrections);
|
|
158
|
+
const id = crypto.randomUUID();
|
|
159
|
+
this.stmts.insert.run({
|
|
160
|
+
id,
|
|
161
|
+
source: key.source,
|
|
162
|
+
artifact_type: key.artifactType,
|
|
163
|
+
name_pattern: key.namePattern,
|
|
164
|
+
corrections: JSON.stringify(corrections),
|
|
165
|
+
derived_analysis: JSON.stringify(derivedAnalysis),
|
|
166
|
+
confidence,
|
|
167
|
+
applied_count: 0,
|
|
168
|
+
created_at: now.toISOString(),
|
|
169
|
+
updated_at: now.toISOString(),
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
id,
|
|
173
|
+
source: key.source,
|
|
174
|
+
artifactType: key.artifactType,
|
|
175
|
+
namePattern: key.namePattern,
|
|
176
|
+
corrections,
|
|
177
|
+
derivedAnalysis: derivedAnalysis,
|
|
178
|
+
confidence,
|
|
179
|
+
appliedCount: 0,
|
|
180
|
+
createdAt: now,
|
|
181
|
+
updatedAt: now,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Find patterns matching a given artifact by source + type, then filter by
|
|
186
|
+
* name glob match. Returns patterns sorted by confidence (descending).
|
|
187
|
+
*/
|
|
188
|
+
findMatches(source, artifactType, artifactName) {
|
|
189
|
+
const rows = this.stmts.findMatches.all({ source, artifact_type: artifactType });
|
|
190
|
+
const matches = [];
|
|
191
|
+
for (const row of rows) {
|
|
192
|
+
const pattern = rowToPattern(row);
|
|
193
|
+
if (this._globMatch(pattern.namePattern, artifactName)) {
|
|
194
|
+
const autoApply = pattern.corrections.length >= 2 && pattern.confidence >= 0.7;
|
|
195
|
+
matches.push({
|
|
196
|
+
pattern,
|
|
197
|
+
mode: autoApply ? "auto" : "suggest",
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return matches;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Record that a pattern was applied to an artifact.
|
|
205
|
+
*/
|
|
206
|
+
recordApplication(patternId) {
|
|
207
|
+
this.stmts.incrementApplied.run({
|
|
208
|
+
id: patternId,
|
|
209
|
+
updated_at: new Date().toISOString(),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get a pattern by ID.
|
|
214
|
+
*/
|
|
215
|
+
getById(id) {
|
|
216
|
+
const row = this.stmts.getById.get(id);
|
|
217
|
+
return row ? rowToPattern(row) : undefined;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* List all patterns, most recently updated first.
|
|
221
|
+
*/
|
|
222
|
+
listAll() {
|
|
223
|
+
const rows = this.stmts.listAll.all();
|
|
224
|
+
return rows.map(rowToPattern);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Delete a pattern.
|
|
228
|
+
*/
|
|
229
|
+
delete(id) {
|
|
230
|
+
const result = this.stmts.deleteById.run(id);
|
|
231
|
+
return result.changes > 0;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Close the database connection.
|
|
235
|
+
*/
|
|
236
|
+
close() {
|
|
237
|
+
this.db.close();
|
|
238
|
+
}
|
|
239
|
+
// -------------------------------------------------------------------------
|
|
240
|
+
// Private helpers
|
|
241
|
+
// -------------------------------------------------------------------------
|
|
242
|
+
_findExact(source, artifactType, namePattern) {
|
|
243
|
+
const rows = this.stmts.findMatches.all({ source, artifact_type: artifactType });
|
|
244
|
+
for (const row of rows) {
|
|
245
|
+
if (row.name_pattern === namePattern) {
|
|
246
|
+
return rowToPattern(row);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Build a DerivedAnalysis by taking the latest correction `to` value for
|
|
253
|
+
* each known field. Supports: summary, deploymentIntent, dependencies
|
|
254
|
+
* (comma-separated), and configurationExpectations (key=value).
|
|
255
|
+
*/
|
|
256
|
+
_buildDerivedAnalysis(corrections) {
|
|
257
|
+
const derived = {};
|
|
258
|
+
const latestByField = new Map();
|
|
259
|
+
for (const c of corrections) {
|
|
260
|
+
latestByField.set(c.field, c.to);
|
|
261
|
+
}
|
|
262
|
+
for (const [field, value] of latestByField) {
|
|
263
|
+
switch (field) {
|
|
264
|
+
case "summary":
|
|
265
|
+
derived.summary = value;
|
|
266
|
+
break;
|
|
267
|
+
case "deploymentIntent":
|
|
268
|
+
derived.deploymentIntent = value;
|
|
269
|
+
break;
|
|
270
|
+
case "dependencies":
|
|
271
|
+
derived.dependencies = value.split(",").map((d) => d.trim()).filter(Boolean);
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
// Treat as a configuration expectation
|
|
275
|
+
if (field.startsWith("config.")) {
|
|
276
|
+
if (!derived.configurationExpectations) {
|
|
277
|
+
derived.configurationExpectations = {};
|
|
278
|
+
}
|
|
279
|
+
derived.configurationExpectations[field.slice("config.".length)] = value;
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return derived;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Simple glob matching: supports `*` (any characters) and `?` (single char).
|
|
288
|
+
* Used to match artifact names against stored name patterns.
|
|
289
|
+
*/
|
|
290
|
+
_globMatch(pattern, name) {
|
|
291
|
+
// Escape regex special chars except * and ?
|
|
292
|
+
const regexStr = pattern
|
|
293
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
294
|
+
.replace(/\*/g, ".*")
|
|
295
|
+
.replace(/\?/g, ".");
|
|
296
|
+
return new RegExp(`^${regexStr}$`).test(name);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=pattern-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-store.js","sourceRoot":"","sources":["../src/pattern-store.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@synth-deploy/server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "BUSL-1.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"synth-server": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./dist/index.js",
|
|
15
|
+
"./agent/*": "./dist/agent/*",
|
|
16
|
+
"./api/*": "./dist/api/*",
|
|
17
|
+
"./mcp/*": "./dist/mcp/*"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"dev": "tsx --env-file=../../.env --watch src/index.ts",
|
|
22
|
+
"start": "node dist/index.js",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@fastify/cors": "^11.2.0",
|
|
27
|
+
"@fastify/formbody": "^8.0.2",
|
|
28
|
+
"@fastify/multipart": "^9.4.0",
|
|
29
|
+
"@fastify/rate-limit": "^10.3.0",
|
|
30
|
+
"@fastify/static": "^9.0.0",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
32
|
+
"@node-saml/node-saml": "^5.1.0",
|
|
33
|
+
"@synth-deploy/core": "*",
|
|
34
|
+
"@types/bcryptjs": "^2.4.6",
|
|
35
|
+
"adm-zip": "^0.5.16",
|
|
36
|
+
"bcryptjs": "^3.0.3",
|
|
37
|
+
"better-sqlite3": "^12.6.2",
|
|
38
|
+
"fastify": "^5.0.0",
|
|
39
|
+
"ldapjs": "^3.0.7",
|
|
40
|
+
"openid-client": "^6.8.2",
|
|
41
|
+
"tar-stream": "^2.2.0",
|
|
42
|
+
"zod": "^3.25.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/adm-zip": "^0.5.7",
|
|
46
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
47
|
+
"@types/ldapjs": "^3.0.6",
|
|
48
|
+
"@types/node": "^25.3.0",
|
|
49
|
+
"@types/tar-stream": "^3.1.4",
|
|
50
|
+
"tsx": "^4.0.0",
|
|
51
|
+
"typescript": "^5.7.0",
|
|
52
|
+
"vitest": "^4.0.18"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PersistentDecisionDebrief } from "@synth-deploy/core";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_RETENTION_DAYS = Number(
|
|
4
|
+
process.env.SYNTH_DEBRIEF_RETENTION_DAYS ?? 90,
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
const DEFAULT_RETENTION_INTERVAL_MS = Number(
|
|
8
|
+
process.env.SYNTH_RETENTION_INTERVAL_MS ?? 24 * 60 * 60 * 1000, // daily
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Runs a single retention pass: purges debrief entries older than
|
|
13
|
+
* the retention threshold.
|
|
14
|
+
*/
|
|
15
|
+
export function runRetentionPass(
|
|
16
|
+
debrief: PersistentDecisionDebrief,
|
|
17
|
+
retentionDays: number = DEFAULT_RETENTION_DAYS,
|
|
18
|
+
): number {
|
|
19
|
+
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);
|
|
20
|
+
const purged = debrief.purgeOlderThan(cutoff);
|
|
21
|
+
if (purged > 0) {
|
|
22
|
+
console.log(`[debrief-retention] Purged ${purged} entries older than ${retentionDays} days`);
|
|
23
|
+
}
|
|
24
|
+
return purged;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Starts a periodic retention scanner.
|
|
29
|
+
* Returns a cleanup function to stop the interval.
|
|
30
|
+
*/
|
|
31
|
+
export function startRetentionScanner(
|
|
32
|
+
debrief: PersistentDecisionDebrief,
|
|
33
|
+
intervalMs: number = DEFAULT_RETENTION_INTERVAL_MS,
|
|
34
|
+
retentionDays: number = DEFAULT_RETENTION_DAYS,
|
|
35
|
+
): () => void {
|
|
36
|
+
// Run once on startup
|
|
37
|
+
runRetentionPass(debrief, retentionDays);
|
|
38
|
+
|
|
39
|
+
const timer = setInterval(() => {
|
|
40
|
+
runRetentionPass(debrief, retentionDays);
|
|
41
|
+
}, intervalMs);
|
|
42
|
+
|
|
43
|
+
return () => clearInterval(timer);
|
|
44
|
+
}
|