@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.
Files changed (317) hide show
  1. package/dist/agent/debrief-retention.d.ts +12 -0
  2. package/dist/agent/debrief-retention.d.ts.map +1 -0
  3. package/dist/agent/debrief-retention.js +27 -0
  4. package/dist/agent/debrief-retention.js.map +1 -0
  5. package/dist/agent/envoy-client.d.ts +216 -0
  6. package/dist/agent/envoy-client.d.ts.map +1 -0
  7. package/dist/agent/envoy-client.js +266 -0
  8. package/dist/agent/envoy-client.js.map +1 -0
  9. package/dist/agent/envoy-registry.d.ts +102 -0
  10. package/dist/agent/envoy-registry.d.ts.map +1 -0
  11. package/dist/agent/envoy-registry.js +319 -0
  12. package/dist/agent/envoy-registry.js.map +1 -0
  13. package/dist/agent/health-checker.d.ts +39 -0
  14. package/dist/agent/health-checker.d.ts.map +1 -0
  15. package/dist/agent/health-checker.js +49 -0
  16. package/dist/agent/health-checker.js.map +1 -0
  17. package/dist/agent/mcp-client-manager.d.ts +36 -0
  18. package/dist/agent/mcp-client-manager.d.ts.map +1 -0
  19. package/dist/agent/mcp-client-manager.js +106 -0
  20. package/dist/agent/mcp-client-manager.js.map +1 -0
  21. package/dist/agent/stale-deployment-detector.d.ts +15 -0
  22. package/dist/agent/stale-deployment-detector.d.ts.map +1 -0
  23. package/dist/agent/stale-deployment-detector.js +50 -0
  24. package/dist/agent/stale-deployment-detector.js.map +1 -0
  25. package/dist/agent/step-runner.d.ts +31 -0
  26. package/dist/agent/step-runner.d.ts.map +1 -0
  27. package/dist/agent/step-runner.js +80 -0
  28. package/dist/agent/step-runner.js.map +1 -0
  29. package/dist/agent/synth-agent.d.ts +168 -0
  30. package/dist/agent/synth-agent.d.ts.map +1 -0
  31. package/dist/agent/synth-agent.js +1195 -0
  32. package/dist/agent/synth-agent.js.map +1 -0
  33. package/dist/api/agent.d.ts +36 -0
  34. package/dist/api/agent.d.ts.map +1 -0
  35. package/dist/api/agent.js +867 -0
  36. package/dist/api/agent.js.map +1 -0
  37. package/dist/api/api-keys.d.ts +4 -0
  38. package/dist/api/api-keys.d.ts.map +1 -0
  39. package/dist/api/api-keys.js +118 -0
  40. package/dist/api/api-keys.js.map +1 -0
  41. package/dist/api/artifacts.d.ts +5 -0
  42. package/dist/api/artifacts.d.ts.map +1 -0
  43. package/dist/api/artifacts.js +142 -0
  44. package/dist/api/artifacts.js.map +1 -0
  45. package/dist/api/auth.d.ts +4 -0
  46. package/dist/api/auth.d.ts.map +1 -0
  47. package/dist/api/auth.js +280 -0
  48. package/dist/api/auth.js.map +1 -0
  49. package/dist/api/deployments.d.ts +11 -0
  50. package/dist/api/deployments.d.ts.map +1 -0
  51. package/dist/api/deployments.js +1098 -0
  52. package/dist/api/deployments.js.map +1 -0
  53. package/dist/api/environments.d.ts +5 -0
  54. package/dist/api/environments.d.ts.map +1 -0
  55. package/dist/api/environments.js +69 -0
  56. package/dist/api/environments.js.map +1 -0
  57. package/dist/api/envoy-reports.d.ts +17 -0
  58. package/dist/api/envoy-reports.d.ts.map +1 -0
  59. package/dist/api/envoy-reports.js +138 -0
  60. package/dist/api/envoy-reports.js.map +1 -0
  61. package/dist/api/envoys.d.ts +5 -0
  62. package/dist/api/envoys.d.ts.map +1 -0
  63. package/dist/api/envoys.js +192 -0
  64. package/dist/api/envoys.js.map +1 -0
  65. package/dist/api/fleet.d.ts +11 -0
  66. package/dist/api/fleet.d.ts.map +1 -0
  67. package/dist/api/fleet.js +394 -0
  68. package/dist/api/fleet.js.map +1 -0
  69. package/dist/api/graph.d.ts +8 -0
  70. package/dist/api/graph.d.ts.map +1 -0
  71. package/dist/api/graph.js +355 -0
  72. package/dist/api/graph.js.map +1 -0
  73. package/dist/api/health.d.ts +20 -0
  74. package/dist/api/health.d.ts.map +1 -0
  75. package/dist/api/health.js +248 -0
  76. package/dist/api/health.js.map +1 -0
  77. package/dist/api/idp-schemas.d.ts +41 -0
  78. package/dist/api/idp-schemas.d.ts.map +1 -0
  79. package/dist/api/idp-schemas.js +17 -0
  80. package/dist/api/idp-schemas.js.map +1 -0
  81. package/dist/api/idp.d.ts +6 -0
  82. package/dist/api/idp.d.ts.map +1 -0
  83. package/dist/api/idp.js +620 -0
  84. package/dist/api/idp.js.map +1 -0
  85. package/dist/api/intake.d.ts +10 -0
  86. package/dist/api/intake.d.ts.map +1 -0
  87. package/dist/api/intake.js +418 -0
  88. package/dist/api/intake.js.map +1 -0
  89. package/dist/api/partitions.d.ts +5 -0
  90. package/dist/api/partitions.d.ts.map +1 -0
  91. package/dist/api/partitions.js +113 -0
  92. package/dist/api/partitions.js.map +1 -0
  93. package/dist/api/progress-event-store.d.ts +62 -0
  94. package/dist/api/progress-event-store.d.ts.map +1 -0
  95. package/dist/api/progress-event-store.js +118 -0
  96. package/dist/api/progress-event-store.js.map +1 -0
  97. package/dist/api/schemas.d.ts +1000 -0
  98. package/dist/api/schemas.d.ts.map +1 -0
  99. package/dist/api/schemas.js +328 -0
  100. package/dist/api/schemas.js.map +1 -0
  101. package/dist/api/security-boundaries.d.ts +4 -0
  102. package/dist/api/security-boundaries.d.ts.map +1 -0
  103. package/dist/api/security-boundaries.js +32 -0
  104. package/dist/api/security-boundaries.js.map +1 -0
  105. package/dist/api/settings.d.ts +4 -0
  106. package/dist/api/settings.d.ts.map +1 -0
  107. package/dist/api/settings.js +99 -0
  108. package/dist/api/settings.js.map +1 -0
  109. package/dist/api/system.d.ts +75 -0
  110. package/dist/api/system.d.ts.map +1 -0
  111. package/dist/api/system.js +558 -0
  112. package/dist/api/system.js.map +1 -0
  113. package/dist/api/telemetry.d.ts +4 -0
  114. package/dist/api/telemetry.d.ts.map +1 -0
  115. package/dist/api/telemetry.js +24 -0
  116. package/dist/api/telemetry.js.map +1 -0
  117. package/dist/api/users.d.ts +4 -0
  118. package/dist/api/users.d.ts.map +1 -0
  119. package/dist/api/users.js +173 -0
  120. package/dist/api/users.js.map +1 -0
  121. package/dist/archive-unpacker.d.ts +24 -0
  122. package/dist/archive-unpacker.d.ts.map +1 -0
  123. package/dist/archive-unpacker.js +239 -0
  124. package/dist/archive-unpacker.js.map +1 -0
  125. package/dist/artifact-analyzer.d.ts +59 -0
  126. package/dist/artifact-analyzer.d.ts.map +1 -0
  127. package/dist/artifact-analyzer.js +334 -0
  128. package/dist/artifact-analyzer.js.map +1 -0
  129. package/dist/auth/idp/index.d.ts +9 -0
  130. package/dist/auth/idp/index.d.ts.map +1 -0
  131. package/dist/auth/idp/index.js +5 -0
  132. package/dist/auth/idp/index.js.map +1 -0
  133. package/dist/auth/idp/ldap.d.ts +56 -0
  134. package/dist/auth/idp/ldap.d.ts.map +1 -0
  135. package/dist/auth/idp/ldap.js +276 -0
  136. package/dist/auth/idp/ldap.js.map +1 -0
  137. package/dist/auth/idp/oidc.d.ts +27 -0
  138. package/dist/auth/idp/oidc.d.ts.map +1 -0
  139. package/dist/auth/idp/oidc.js +97 -0
  140. package/dist/auth/idp/oidc.js.map +1 -0
  141. package/dist/auth/idp/role-mapping.d.ts +9 -0
  142. package/dist/auth/idp/role-mapping.d.ts.map +1 -0
  143. package/dist/auth/idp/role-mapping.js +16 -0
  144. package/dist/auth/idp/role-mapping.js.map +1 -0
  145. package/dist/auth/idp/saml.d.ts +40 -0
  146. package/dist/auth/idp/saml.d.ts.map +1 -0
  147. package/dist/auth/idp/saml.js +117 -0
  148. package/dist/auth/idp/saml.js.map +1 -0
  149. package/dist/auth/idp/types.d.ts +23 -0
  150. package/dist/auth/idp/types.d.ts.map +1 -0
  151. package/dist/auth/idp/types.js +2 -0
  152. package/dist/auth/idp/types.js.map +1 -0
  153. package/dist/fleet/fleet-executor.d.ts +35 -0
  154. package/dist/fleet/fleet-executor.d.ts.map +1 -0
  155. package/dist/fleet/fleet-executor.js +228 -0
  156. package/dist/fleet/fleet-executor.js.map +1 -0
  157. package/dist/fleet/fleet-store.d.ts +13 -0
  158. package/dist/fleet/fleet-store.d.ts.map +1 -0
  159. package/dist/fleet/fleet-store.js +13 -0
  160. package/dist/fleet/fleet-store.js.map +1 -0
  161. package/dist/fleet/index.d.ts +5 -0
  162. package/dist/fleet/index.d.ts.map +1 -0
  163. package/dist/fleet/index.js +4 -0
  164. package/dist/fleet/index.js.map +1 -0
  165. package/dist/fleet/representative-selector.d.ts +15 -0
  166. package/dist/fleet/representative-selector.d.ts.map +1 -0
  167. package/dist/fleet/representative-selector.js +71 -0
  168. package/dist/fleet/representative-selector.js.map +1 -0
  169. package/dist/graph/graph-executor.d.ts +36 -0
  170. package/dist/graph/graph-executor.d.ts.map +1 -0
  171. package/dist/graph/graph-executor.js +348 -0
  172. package/dist/graph/graph-executor.js.map +1 -0
  173. package/dist/graph/graph-inference.d.ts +22 -0
  174. package/dist/graph/graph-inference.d.ts.map +1 -0
  175. package/dist/graph/graph-inference.js +149 -0
  176. package/dist/graph/graph-inference.js.map +1 -0
  177. package/dist/graph/graph-store.d.ts +12 -0
  178. package/dist/graph/graph-store.d.ts.map +1 -0
  179. package/dist/graph/graph-store.js +61 -0
  180. package/dist/graph/graph-store.js.map +1 -0
  181. package/dist/graph/index.d.ts +5 -0
  182. package/dist/graph/index.d.ts.map +1 -0
  183. package/dist/graph/index.js +4 -0
  184. package/dist/graph/index.js.map +1 -0
  185. package/dist/index.d.ts +2 -0
  186. package/dist/index.d.ts.map +1 -0
  187. package/dist/index.js +837 -0
  188. package/dist/index.js.map +1 -0
  189. package/dist/intake/index.d.ts +6 -0
  190. package/dist/intake/index.d.ts.map +1 -0
  191. package/dist/intake/index.js +5 -0
  192. package/dist/intake/index.js.map +1 -0
  193. package/dist/intake/intake-processor.d.ts +17 -0
  194. package/dist/intake/intake-processor.d.ts.map +1 -0
  195. package/dist/intake/intake-processor.js +99 -0
  196. package/dist/intake/intake-processor.js.map +1 -0
  197. package/dist/intake/intake-store.d.ts +7 -0
  198. package/dist/intake/intake-store.d.ts.map +1 -0
  199. package/dist/intake/intake-store.js +7 -0
  200. package/dist/intake/intake-store.js.map +1 -0
  201. package/dist/intake/registry-poller.d.ts +41 -0
  202. package/dist/intake/registry-poller.d.ts.map +1 -0
  203. package/dist/intake/registry-poller.js +202 -0
  204. package/dist/intake/registry-poller.js.map +1 -0
  205. package/dist/intake/webhook-handlers.d.ts +37 -0
  206. package/dist/intake/webhook-handlers.d.ts.map +1 -0
  207. package/dist/intake/webhook-handlers.js +268 -0
  208. package/dist/intake/webhook-handlers.js.map +1 -0
  209. package/dist/logger.d.ts +5 -0
  210. package/dist/logger.d.ts.map +1 -0
  211. package/dist/logger.js +15 -0
  212. package/dist/logger.js.map +1 -0
  213. package/dist/mcp/resources.d.ts +9 -0
  214. package/dist/mcp/resources.d.ts.map +1 -0
  215. package/dist/mcp/resources.js +72 -0
  216. package/dist/mcp/resources.js.map +1 -0
  217. package/dist/mcp/server.d.ts +15 -0
  218. package/dist/mcp/server.d.ts.map +1 -0
  219. package/dist/mcp/server.js +20 -0
  220. package/dist/mcp/server.js.map +1 -0
  221. package/dist/mcp/tools.d.ts +9 -0
  222. package/dist/mcp/tools.d.ts.map +1 -0
  223. package/dist/mcp/tools.js +88 -0
  224. package/dist/mcp/tools.js.map +1 -0
  225. package/dist/middleware/auth.d.ts +29 -0
  226. package/dist/middleware/auth.d.ts.map +1 -0
  227. package/dist/middleware/auth.js +76 -0
  228. package/dist/middleware/auth.js.map +1 -0
  229. package/dist/middleware/permissions.d.ts +13 -0
  230. package/dist/middleware/permissions.d.ts.map +1 -0
  231. package/dist/middleware/permissions.js +32 -0
  232. package/dist/middleware/permissions.js.map +1 -0
  233. package/dist/pattern-store.d.ts +104 -0
  234. package/dist/pattern-store.d.ts.map +1 -0
  235. package/dist/pattern-store.js +299 -0
  236. package/dist/pattern-store.js.map +1 -0
  237. package/package.json +54 -0
  238. package/src/agent/debrief-retention.ts +44 -0
  239. package/src/agent/envoy-client.ts +474 -0
  240. package/src/agent/envoy-registry.ts +384 -0
  241. package/src/agent/health-checker.ts +70 -0
  242. package/src/agent/mcp-client-manager.ts +131 -0
  243. package/src/agent/stale-deployment-detector.ts +79 -0
  244. package/src/agent/step-runner.ts +124 -0
  245. package/src/agent/synth-agent.ts +1567 -0
  246. package/src/api/agent.ts +1075 -0
  247. package/src/api/api-keys.ts +129 -0
  248. package/src/api/artifacts.ts +194 -0
  249. package/src/api/auth.ts +320 -0
  250. package/src/api/deployments.ts +1347 -0
  251. package/src/api/environments.ts +97 -0
  252. package/src/api/envoy-reports.ts +159 -0
  253. package/src/api/envoys.ts +237 -0
  254. package/src/api/fleet.ts +510 -0
  255. package/src/api/graph.ts +516 -0
  256. package/src/api/health.ts +311 -0
  257. package/src/api/idp-schemas.ts +19 -0
  258. package/src/api/idp.ts +735 -0
  259. package/src/api/intake.ts +537 -0
  260. package/src/api/partitions.ts +147 -0
  261. package/src/api/progress-event-store.ts +153 -0
  262. package/src/api/schemas.ts +376 -0
  263. package/src/api/security-boundaries.ts +54 -0
  264. package/src/api/settings.ts +118 -0
  265. package/src/api/system.ts +704 -0
  266. package/src/api/telemetry.ts +32 -0
  267. package/src/api/users.ts +210 -0
  268. package/src/archive-unpacker.ts +271 -0
  269. package/src/artifact-analyzer.ts +438 -0
  270. package/src/auth/idp/index.ts +8 -0
  271. package/src/auth/idp/ldap.ts +340 -0
  272. package/src/auth/idp/oidc.ts +117 -0
  273. package/src/auth/idp/role-mapping.ts +22 -0
  274. package/src/auth/idp/saml.ts +148 -0
  275. package/src/auth/idp/types.ts +22 -0
  276. package/src/fleet/fleet-executor.ts +309 -0
  277. package/src/fleet/fleet-store.ts +13 -0
  278. package/src/fleet/index.ts +4 -0
  279. package/src/fleet/representative-selector.ts +83 -0
  280. package/src/graph/graph-executor.ts +446 -0
  281. package/src/graph/graph-inference.ts +184 -0
  282. package/src/graph/graph-store.ts +75 -0
  283. package/src/graph/index.ts +4 -0
  284. package/src/index.ts +916 -0
  285. package/src/intake/index.ts +5 -0
  286. package/src/intake/intake-processor.ts +111 -0
  287. package/src/intake/intake-store.ts +7 -0
  288. package/src/intake/registry-poller.ts +230 -0
  289. package/src/intake/webhook-handlers.ts +328 -0
  290. package/src/logger.ts +19 -0
  291. package/src/mcp/resources.ts +98 -0
  292. package/src/mcp/server.ts +34 -0
  293. package/src/mcp/tools.ts +117 -0
  294. package/src/middleware/auth.ts +103 -0
  295. package/src/middleware/permissions.ts +35 -0
  296. package/src/pattern-store.ts +409 -0
  297. package/tests/agent-mode.test.ts +536 -0
  298. package/tests/api-handlers.test.ts +1245 -0
  299. package/tests/archive-unpacker.test.ts +179 -0
  300. package/tests/artifact-analyzer.test.ts +240 -0
  301. package/tests/auth-middleware.test.ts +189 -0
  302. package/tests/decision-diary.test.ts +957 -0
  303. package/tests/diary-reader.test.ts +782 -0
  304. package/tests/envoy-client.test.ts +342 -0
  305. package/tests/envoy-reports.test.ts +156 -0
  306. package/tests/mcp-tools.test.ts +213 -0
  307. package/tests/orchestration.test.ts +536 -0
  308. package/tests/partition-deletion.test.ts +143 -0
  309. package/tests/partition-isolation.test.ts +830 -0
  310. package/tests/pattern-store.test.ts +371 -0
  311. package/tests/rbac-enforcement.test.ts +409 -0
  312. package/tests/ssrf-validation.test.ts +56 -0
  313. package/tests/stale-deployment.test.ts +85 -0
  314. package/tests/step-runner.test.ts +308 -0
  315. package/tests/ui-journey.test.ts +330 -0
  316. package/tsconfig.json +11 -0
  317. package/vitest.config.ts +27 -0
@@ -0,0 +1,355 @@
1
+ import { requirePermission, requireEdition } from "../middleware/permissions.js";
2
+ import { GraphExecutor } from "../graph/graph-executor.js";
3
+ import { EnvoyClient } from "../agent/envoy-client.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Deployment Graph API routes
6
+ // ---------------------------------------------------------------------------
7
+ export function registerGraphRoutes(app, graphStore, inferenceEngine, envoyRegistry, artifactStore, debrief) {
8
+ // Create a deployment graph (triggers inference)
9
+ app.post("/api/deployment-graphs", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.create")] }, async (request, reply) => {
10
+ const body = request.body;
11
+ if (!body.artifactIds || body.artifactIds.length === 0) {
12
+ return reply
13
+ .status(400)
14
+ .send({ error: "artifactIds is required and must not be empty" });
15
+ }
16
+ if (!body.envoyAssignments || Object.keys(body.envoyAssignments).length === 0) {
17
+ return reply
18
+ .status(400)
19
+ .send({ error: "envoyAssignments is required" });
20
+ }
21
+ // Validate all artifacts exist
22
+ for (const artifactId of body.artifactIds) {
23
+ if (!artifactStore.get(artifactId)) {
24
+ return reply
25
+ .status(404)
26
+ .send({ error: `Artifact not found: ${artifactId}` });
27
+ }
28
+ }
29
+ // Validate all assigned envoys exist
30
+ for (const [artifactId, envoyId] of Object.entries(body.envoyAssignments)) {
31
+ if (!envoyRegistry.get(envoyId)) {
32
+ return reply
33
+ .status(404)
34
+ .send({
35
+ error: `Envoy not found: ${envoyId} (assigned to artifact ${artifactId})`,
36
+ });
37
+ }
38
+ }
39
+ // Infer the graph structure using LLM (or flat fallback)
40
+ const graph = await inferenceEngine.inferGraph({
41
+ artifactIds: body.artifactIds,
42
+ envoyAssignments: body.envoyAssignments,
43
+ partitionId: body.partitionId,
44
+ graphName: body.name,
45
+ });
46
+ if (body.approvalMode) {
47
+ graph.approvalMode = body.approvalMode;
48
+ }
49
+ graphStore.create(graph);
50
+ // Record debrief entry for graph creation with inference reasoning
51
+ debrief.record({
52
+ partitionId: graph.partitionId ?? null,
53
+ deploymentId: null,
54
+ agent: "server",
55
+ decisionType: "plan-generation",
56
+ decision: `Created deployment graph "${graph.name}" with ${graph.nodes.length} nodes and ${graph.edges.length} edges`,
57
+ reasoning: `Inferred dependency graph for artifacts: ${body.artifactIds.join(", ")}. Edges: ${JSON.stringify(graph.edges.map((e) => `${e.from} -[${e.type}]-> ${e.to}`))}`,
58
+ context: {
59
+ graphId: graph.id,
60
+ nodeCount: graph.nodes.length,
61
+ edgeCount: graph.edges.length,
62
+ approvalMode: graph.approvalMode,
63
+ edges: graph.edges,
64
+ },
65
+ });
66
+ return reply.status(201).send({ graph });
67
+ });
68
+ // List all deployment graphs
69
+ app.get("/api/deployment-graphs", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.view")] }, async () => {
70
+ return { graphs: graphStore.list() };
71
+ });
72
+ // Get a specific deployment graph with node statuses
73
+ app.get("/api/deployment-graphs/:id", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.view")] }, async (request, reply) => {
74
+ const { id } = request.params;
75
+ const graph = graphStore.getById(id);
76
+ if (!graph) {
77
+ return reply.status(404).send({ error: "Deployment graph not found" });
78
+ }
79
+ // Enrich with artifact names for display
80
+ const enrichedNodes = graph.nodes.map((node) => {
81
+ const artifact = artifactStore.get(node.artifactId);
82
+ const envoy = envoyRegistry.get(node.envoyId);
83
+ return {
84
+ ...node,
85
+ artifactName: artifact?.name ?? node.artifactId,
86
+ envoyName: envoy?.name ?? node.envoyId,
87
+ };
88
+ });
89
+ return { graph: { ...graph, nodes: enrichedNodes } };
90
+ });
91
+ // Update a deployment graph (user corrections to inferred structure)
92
+ app.put("/api/deployment-graphs/:id", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.create")] }, async (request, reply) => {
93
+ const { id } = request.params;
94
+ const graph = graphStore.getById(id);
95
+ if (!graph) {
96
+ return reply.status(404).send({ error: "Deployment graph not found" });
97
+ }
98
+ const body = request.body;
99
+ if (graph.status !== "draft" && graph.status !== "awaiting_approval") {
100
+ return reply
101
+ .status(409)
102
+ .send({ error: `Cannot modify graph in status: ${graph.status}` });
103
+ }
104
+ const updated = graphStore.update(id, body);
105
+ // Record debrief entry for user corrections
106
+ debrief.record({
107
+ partitionId: graph.partitionId ?? null,
108
+ deploymentId: null,
109
+ agent: "server",
110
+ decisionType: "plan-modification",
111
+ decision: `User corrected deployment graph "${graph.name}"`,
112
+ reasoning: `Updated fields: ${Object.keys(body).filter((k) => body[k] !== undefined).join(", ")}`,
113
+ context: {
114
+ graphId: id,
115
+ updatedFields: Object.keys(body).filter((k) => body[k] !== undefined),
116
+ },
117
+ });
118
+ return { graph: updated };
119
+ });
120
+ // Delete a deployment graph
121
+ app.delete("/api/deployment-graphs/:id", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.create")] }, async (request, reply) => {
122
+ const { id } = request.params;
123
+ const graph = graphStore.getById(id);
124
+ if (!graph) {
125
+ return reply.status(404).send({ error: "Deployment graph not found" });
126
+ }
127
+ if (graph.status === "executing") {
128
+ return reply
129
+ .status(409)
130
+ .send({ error: "Cannot delete a graph that is currently executing" });
131
+ }
132
+ graphStore.delete(id);
133
+ return reply.status(204).send();
134
+ });
135
+ // Execute a deployment graph
136
+ app.post("/api/deployment-graphs/:id/execute", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.approve")] }, async (request, reply) => {
137
+ const { id } = request.params;
138
+ const body = request.body;
139
+ const graph = graphStore.getById(id);
140
+ if (!graph) {
141
+ return reply.status(404).send({ error: "Deployment graph not found" });
142
+ }
143
+ if (graph.status !== "draft" &&
144
+ graph.status !== "awaiting_approval") {
145
+ return reply
146
+ .status(409)
147
+ .send({ error: `Cannot execute graph in status: ${graph.status}` });
148
+ }
149
+ if (!body.plans || Object.keys(body.plans).length === 0) {
150
+ return reply
151
+ .status(400)
152
+ .send({ error: "plans map (nodeId -> DeploymentPlan) is required" });
153
+ }
154
+ // Validate all nodes have plans
155
+ const missingPlans = graph.nodes
156
+ .filter((n) => !body.plans[n.id])
157
+ .map((n) => n.id);
158
+ if (missingPlans.length > 0) {
159
+ return reply.status(400).send({
160
+ error: `Missing plans for nodes: ${missingPlans.join(", ")}`,
161
+ });
162
+ }
163
+ graphStore.updateStatus(id, "executing");
164
+ const executor = new GraphExecutor(envoyRegistry, (url, timeoutMs) => new EnvoyClient(url, timeoutMs));
165
+ const plansMap = new Map(Object.entries(body.plans));
166
+ const events = [];
167
+ const dataFlowValues = {};
168
+ // Execute and collect results
169
+ // (In a production system this would be async with SSE/WebSocket progress,
170
+ // but for now we run synchronously and return the final state.)
171
+ try {
172
+ for await (const event of executor.execute(graph, plansMap, body.partitionVariables)) {
173
+ events.push(event);
174
+ // Update node status in store
175
+ if (event.nodeId) {
176
+ if (event.type === "node-started") {
177
+ graphStore.updateNode(id, event.nodeId, { status: "executing" });
178
+ }
179
+ else if (event.type === "node-completed") {
180
+ graphStore.updateNode(id, event.nodeId, { status: "completed" });
181
+ if (event.outputCapture) {
182
+ dataFlowValues[event.nodeId] = event.outputCapture;
183
+ }
184
+ }
185
+ else if (event.type === "node-failed") {
186
+ graphStore.updateNode(id, event.nodeId, { status: "failed" });
187
+ }
188
+ else if (event.type === "node-skipped") {
189
+ // Skipped nodes remain in "pending" status — they were never started
190
+ graphStore.updateNode(id, event.nodeId, { status: "pending" });
191
+ }
192
+ }
193
+ if (event.type === "graph-completed") {
194
+ graphStore.updateStatus(id, "completed");
195
+ }
196
+ else if (event.type === "graph-failed") {
197
+ graphStore.updateStatus(id, "failed");
198
+ }
199
+ }
200
+ }
201
+ catch (err) {
202
+ graphStore.updateStatus(id, "failed");
203
+ const message = err instanceof Error ? err.message : String(err);
204
+ return reply.status(500).send({ error: message, events });
205
+ }
206
+ const finalGraph = graphStore.getById(id);
207
+ // Record debrief summary for execution completion
208
+ const finalCompletedCount = finalGraph?.nodes.filter((n) => n.status === "completed").length ?? 0;
209
+ const finalFailedCount = finalGraph?.nodes.filter((n) => n.status === "failed").length ?? 0;
210
+ debrief.record({
211
+ partitionId: graph.partitionId ?? null,
212
+ deploymentId: null,
213
+ agent: "server",
214
+ decisionType: finalFailedCount > 0 ? "deployment-failure" : "deployment-completion",
215
+ decision: `Graph "${graph.name}" execution ${finalFailedCount > 0 ? "failed" : "completed"}: ${finalCompletedCount}/${graph.nodes.length} nodes succeeded`,
216
+ reasoning: `Executed deployment graph with ${graph.nodes.length} nodes. ${finalCompletedCount} completed, ${finalFailedCount} failed. Data flow values captured for ${Object.keys(dataFlowValues).length} nodes.`,
217
+ context: {
218
+ graphId: id,
219
+ completedCount: finalCompletedCount,
220
+ failedCount: finalFailedCount,
221
+ totalNodes: graph.nodes.length,
222
+ dataFlowValues,
223
+ partitionVariables: body.partitionVariables,
224
+ },
225
+ });
226
+ return { graph: finalGraph, events };
227
+ });
228
+ // Rollback a deployment graph
229
+ app.post("/api/deployment-graphs/:id/rollback", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.rollback")] }, async (request, reply) => {
230
+ const { id } = request.params;
231
+ const body = request.body;
232
+ const graph = graphStore.getById(id);
233
+ if (!graph) {
234
+ return reply.status(404).send({ error: "Deployment graph not found" });
235
+ }
236
+ if (graph.status !== "completed" && graph.status !== "failed") {
237
+ return reply.status(409).send({
238
+ error: `Cannot rollback graph in status: ${graph.status}`,
239
+ });
240
+ }
241
+ if (!body.rollbackPlans || Object.keys(body.rollbackPlans).length === 0) {
242
+ return reply
243
+ .status(400)
244
+ .send({ error: "rollbackPlans map is required" });
245
+ }
246
+ graphStore.updateStatus(id, "executing");
247
+ const executor = new GraphExecutor(envoyRegistry, (url, timeoutMs) => new EnvoyClient(url, timeoutMs));
248
+ const rollbackMap = new Map(Object.entries(body.rollbackPlans));
249
+ const events = [];
250
+ try {
251
+ for await (const event of executor.rollback(graph, rollbackMap)) {
252
+ events.push(event);
253
+ }
254
+ }
255
+ catch {
256
+ // Rollback errors are non-fatal — we still update status
257
+ }
258
+ graphStore.updateStatus(id, "rolled_back");
259
+ const finalGraph = graphStore.getById(id);
260
+ return { graph: finalGraph, events };
261
+ });
262
+ // Per-node approval (for per-node approval mode)
263
+ app.post("/api/deployment-graphs/:id/nodes/:nodeId/approve", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.approve")] }, async (request, reply) => {
264
+ const { id, nodeId } = request.params;
265
+ const graph = graphStore.getById(id);
266
+ if (!graph) {
267
+ return reply.status(404).send({ error: "Deployment graph not found" });
268
+ }
269
+ if (graph.approvalMode !== "per-node") {
270
+ return reply.status(409).send({
271
+ error: "Graph is not in per-node approval mode",
272
+ });
273
+ }
274
+ const existingNode = graph.nodes.find((n) => n.id === nodeId);
275
+ if (!existingNode) {
276
+ return reply.status(404).send({ error: "Node not found" });
277
+ }
278
+ if (existingNode.status !== "awaiting_approval") {
279
+ return reply.status(409).send({
280
+ error: `Node is not awaiting approval (current status: ${existingNode.status})`,
281
+ });
282
+ }
283
+ // Mark as approved (ready to execute)
284
+ const node = graphStore.updateNode(id, nodeId, { status: "pending" });
285
+ return { node };
286
+ });
287
+ // Approve remaining nodes — switch from per-node to graph approval mid-execution
288
+ app.post("/api/deployment-graphs/:id/approve-remaining", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.approve")] }, async (request, reply) => {
289
+ const { id } = request.params;
290
+ const graph = graphStore.getById(id);
291
+ if (!graph) {
292
+ return reply.status(404).send({ error: "Deployment graph not found" });
293
+ }
294
+ if (graph.status !== "executing" && graph.status !== "awaiting_approval") {
295
+ return reply.status(409).send({
296
+ error: `Cannot approve remaining in status: ${graph.status}`,
297
+ });
298
+ }
299
+ graphStore.update(id, { approvalMode: "graph" });
300
+ return { graph: graphStore.getById(id) };
301
+ });
302
+ // Retry a failed node
303
+ app.post("/api/deployment-graphs/:id/nodes/:nodeId/retry", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.approve")] }, async (request, reply) => {
304
+ const { id, nodeId } = request.params;
305
+ const graph = graphStore.getById(id);
306
+ if (!graph) {
307
+ return reply.status(404).send({ error: "Deployment graph not found" });
308
+ }
309
+ if (graph.status !== "failed") {
310
+ return reply.status(409).send({
311
+ error: `Cannot retry node when graph status is: ${graph.status}`,
312
+ });
313
+ }
314
+ const existingNode = graph.nodes.find((n) => n.id === nodeId);
315
+ if (!existingNode) {
316
+ return reply.status(404).send({ error: "Node not found" });
317
+ }
318
+ if (existingNode.status !== "failed") {
319
+ return reply.status(409).send({
320
+ error: `Node is not in failed status (current status: ${existingNode.status})`,
321
+ });
322
+ }
323
+ // Re-queue for execution
324
+ const node = graphStore.updateNode(id, nodeId, { status: "pending" });
325
+ return { node };
326
+ });
327
+ // Skip a failed node — allows downstream to proceed
328
+ app.post("/api/deployment-graphs/:id/nodes/:nodeId/skip", { preHandler: [requireEdition("deployment-graphs"), requirePermission("deployment.approve")] }, async (request, reply) => {
329
+ const { id, nodeId } = request.params;
330
+ const graph = graphStore.getById(id);
331
+ if (!graph) {
332
+ return reply.status(404).send({ error: "Deployment graph not found" });
333
+ }
334
+ if (graph.status !== "failed") {
335
+ return reply.status(409).send({
336
+ error: `Cannot skip node when graph status is: ${graph.status}`,
337
+ });
338
+ }
339
+ const existingNode = graph.nodes.find((n) => n.id === nodeId);
340
+ if (!existingNode) {
341
+ return reply.status(404).send({ error: "Node not found" });
342
+ }
343
+ if (existingNode.status !== "failed") {
344
+ return reply.status(409).send({
345
+ error: `Node is not in failed status (current status: ${existingNode.status})`,
346
+ });
347
+ }
348
+ // Mark as completed (skipped) so downstream nodes can proceed
349
+ const node = graphStore.updateNode(id, nodeId, { status: "completed" });
350
+ // Set graph back to awaiting_approval so it can be re-executed
351
+ graphStore.updateStatus(id, "awaiting_approval");
352
+ return { node, skipped: true, graph: graphStore.getById(id) };
353
+ });
354
+ }
355
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/api/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type Database from "better-sqlite3";
3
+ import type { LlmClient } from "@synth-deploy/core";
4
+ interface HealthCheckOptions {
5
+ entityDb: Database.Database;
6
+ dataDir: string;
7
+ envoyUrl?: string;
8
+ llmApiKey?: string;
9
+ llmBaseUrl?: string;
10
+ mcpServers?: Array<{
11
+ name: string;
12
+ url: string;
13
+ }>;
14
+ llmClient?: LlmClient;
15
+ }
16
+ /** Call when the API key changes so the next health check runs fresh. */
17
+ export declare function invalidateLlmHealthCache(): void;
18
+ export declare function registerHealthRoutes(app: FastifyInstance, options?: HealthCheckOptions): void;
19
+ export {};
20
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/api/health.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGpD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAYD,yEAAyE;AACzE,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI,CAyNN"}
@@ -0,0 +1,248 @@
1
+ import fs from "node:fs";
2
+ import { VerifyTaskModelSchema } from "./schemas.js";
3
+ // Cached LLM health result (5-minute TTL)
4
+ let llmHealthCache = null;
5
+ const LLM_HEALTH_CACHE_TTL_MS = 300_000;
6
+ /** Call when the API key changes so the next health check runs fresh. */
7
+ export function invalidateLlmHealthCache() {
8
+ llmHealthCache = null;
9
+ }
10
+ export function registerHealthRoutes(app, options) {
11
+ app.get("/health", async () => {
12
+ if (!options) {
13
+ return { status: "ok", service: "synth-server", timestamp: new Date().toISOString() };
14
+ }
15
+ const checks = {};
16
+ // SQLite check
17
+ try {
18
+ const start = Date.now();
19
+ options.entityDb.prepare("SELECT 1").get();
20
+ checks.db = { ok: true, responseTimeMs: Date.now() - start };
21
+ }
22
+ catch (err) {
23
+ app.log.error(err, "Health check: database unavailable");
24
+ checks.db = { ok: false, error: "Database unavailable" };
25
+ }
26
+ // Filesystem check
27
+ try {
28
+ fs.accessSync(options.dataDir, fs.constants.R_OK | fs.constants.W_OK);
29
+ checks.fs = { ok: true };
30
+ }
31
+ catch (err) {
32
+ app.log.error(err, "Health check: filesystem unavailable");
33
+ checks.fs = { ok: false, error: "Filesystem unavailable" };
34
+ }
35
+ // Envoy check (optional)
36
+ if (options.envoyUrl) {
37
+ try {
38
+ const start = Date.now();
39
+ const controller = new AbortController();
40
+ const timeout = setTimeout(() => controller.abort(), 3000);
41
+ const res = await fetch(`${options.envoyUrl}/health`, { signal: controller.signal });
42
+ clearTimeout(timeout);
43
+ checks.envoy = {
44
+ ok: res.ok,
45
+ responseTimeMs: Date.now() - start,
46
+ ...(res.ok ? {} : { error: `HTTP ${res.status}` }),
47
+ };
48
+ }
49
+ catch (err) {
50
+ app.log.error(err, "Health check: envoy unreachable");
51
+ checks.envoy = { ok: false, error: "Envoy unreachable" };
52
+ }
53
+ }
54
+ // LLM API check (optional)
55
+ if (options.llmApiKey) {
56
+ try {
57
+ const start = Date.now();
58
+ const controller = new AbortController();
59
+ const timeout = setTimeout(() => controller.abort(), 5000);
60
+ const baseUrl = options.llmBaseUrl ?? "https://api.anthropic.com";
61
+ const res = await fetch(`${baseUrl}/v1/messages`, {
62
+ method: "POST",
63
+ headers: {
64
+ "x-api-key": options.llmApiKey,
65
+ "anthropic-version": "2023-06-01",
66
+ "content-type": "application/json",
67
+ },
68
+ body: JSON.stringify({
69
+ model: "claude-haiku-4-5-20251001",
70
+ max_tokens: 1,
71
+ messages: [{ role: "user", content: "health check" }],
72
+ }),
73
+ signal: controller.signal,
74
+ });
75
+ clearTimeout(timeout);
76
+ // Any non-5xx response means the API is reachable and the key is valid
77
+ checks.llm = {
78
+ ok: res.status < 500,
79
+ responseTimeMs: Date.now() - start,
80
+ ...(!res.ok && res.status >= 500 ? { error: `HTTP ${res.status}` } : {}),
81
+ };
82
+ }
83
+ catch (err) {
84
+ app.log.error(err, "Health check: LLM API unavailable");
85
+ checks.llm = { ok: false, error: "LLM API unavailable" };
86
+ }
87
+ }
88
+ // MCP server checks (optional)
89
+ if (options.mcpServers && options.mcpServers.length > 0) {
90
+ const mcpResults = {};
91
+ for (const server of options.mcpServers) {
92
+ try {
93
+ const start = Date.now();
94
+ const controller = new AbortController();
95
+ const timeout = setTimeout(() => controller.abort(), 3000);
96
+ const res = await fetch(server.url, { signal: controller.signal });
97
+ clearTimeout(timeout);
98
+ mcpResults[server.name] = {
99
+ ok: res.ok,
100
+ responseTimeMs: Date.now() - start,
101
+ ...(!res.ok ? { error: `HTTP ${res.status}` } : {}),
102
+ };
103
+ }
104
+ catch (err) {
105
+ app.log.error(err, `Health check: MCP server "${server.name}" unreachable`);
106
+ mcpResults[server.name] = { ok: false, error: "Unreachable" };
107
+ }
108
+ }
109
+ checks.mcpServers = mcpResults;
110
+ }
111
+ // Determine overall status
112
+ const dbOrFsFailed = !checks.db.ok || !checks.fs.ok;
113
+ const envoyFailed = checks.envoy && !checks.envoy.ok;
114
+ const llmFailed = checks.llm && !checks.llm.ok;
115
+ const mcpFailed = checks.mcpServers && Object.values(checks.mcpServers).some((c) => !c.ok);
116
+ let status;
117
+ if (dbOrFsFailed) {
118
+ status = "unhealthy";
119
+ }
120
+ else if (envoyFailed || llmFailed || mcpFailed) {
121
+ status = "degraded";
122
+ }
123
+ else {
124
+ status = "healthy";
125
+ }
126
+ return {
127
+ status,
128
+ service: "synth-server",
129
+ checks,
130
+ timestamp: new Date().toISOString(),
131
+ };
132
+ });
133
+ // --- Dedicated LLM health endpoint ---
134
+ // Used by the UI's LlmGate to determine if the LLM connection is configured and reachable.
135
+ app.get("/api/health/llm", async () => {
136
+ const llmClient = options?.llmClient;
137
+ // Check configuration via the LlmClient instance
138
+ const configured = llmClient ? llmClient.isAvailable() : false;
139
+ if (!configured) {
140
+ return {
141
+ configured: false,
142
+ healthy: false,
143
+ provider: detectProvider(),
144
+ };
145
+ }
146
+ // Check cached health result
147
+ const now = Date.now();
148
+ if (llmHealthCache && now - llmHealthCache.checkedAt < LLM_HEALTH_CACHE_TTL_MS) {
149
+ return {
150
+ configured: true,
151
+ healthy: llmHealthCache.healthy,
152
+ provider: detectProvider(),
153
+ };
154
+ }
155
+ // For Anthropic/Bedrock/Vertex: trust configuration — real failures surface in the debrief.
156
+ // For openai-compatible: do a free GET /models to verify the endpoint is reachable.
157
+ let healthy = true;
158
+ try {
159
+ const apiKey = options?.llmApiKey ?? process.env.SYNTH_LLM_API_KEY;
160
+ const baseUrl = options?.llmBaseUrl ?? process.env.SYNTH_LLM_BASE_URL ?? "https://api.anthropic.com";
161
+ const provider = detectProvider();
162
+ if (provider === "openai-compatible") {
163
+ const controller = new AbortController();
164
+ const timeout = setTimeout(() => controller.abort(), 5000);
165
+ const res = await fetch(baseUrl.replace(/\/+$/, "") + "/models", {
166
+ signal: controller.signal,
167
+ headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
168
+ });
169
+ clearTimeout(timeout);
170
+ healthy = res.status < 500;
171
+ }
172
+ }
173
+ catch (err) {
174
+ app.log.error(err, "LLM health check failed");
175
+ healthy = false;
176
+ }
177
+ llmHealthCache = { healthy, checkedAt: now };
178
+ return {
179
+ configured: true,
180
+ healthy,
181
+ provider: detectProvider(),
182
+ };
183
+ });
184
+ // --- Per-task model capability verification ---
185
+ // Sends a lightweight probe prompt to verify a model can handle a specific task.
186
+ app.post("/api/health/llm/verify-task", async (request, reply) => {
187
+ const llmClient = options?.llmClient;
188
+ if (!llmClient || !llmClient.isAvailable()) {
189
+ return reply.status(503).send({
190
+ error: "LLM client not configured or unavailable",
191
+ });
192
+ }
193
+ const parsed = VerifyTaskModelSchema.safeParse(request.body);
194
+ if (!parsed.success) {
195
+ return reply.status(400).send({
196
+ error: "Invalid input",
197
+ details: parsed.error.format(),
198
+ });
199
+ }
200
+ const { task, model } = parsed.data;
201
+ try {
202
+ const result = await runTaskModelVerification(llmClient, task, model);
203
+ return { result };
204
+ }
205
+ catch (err) {
206
+ app.log.error(err, "Task model verification failed");
207
+ return reply.status(500).send({
208
+ error: "Verification failed",
209
+ detail: err instanceof Error ? err.message : String(err),
210
+ });
211
+ }
212
+ });
213
+ }
214
+ function extractApiErrorMessage(raw) {
215
+ const match = raw.match(/"message"\s*:\s*"([^"]+)"/);
216
+ if (match)
217
+ return match[1];
218
+ const stripped = raw.replace(/\{[^}]{20,}\}/g, "").trim();
219
+ return stripped || raw;
220
+ }
221
+ async function runTaskModelVerification(client, task, model) {
222
+ // Connection test only — sends a minimal ping and checks for a response.
223
+ // Real capability evaluation is tracked in issue #211.
224
+ const result = await client.classify({
225
+ prompt: "ping",
226
+ systemPrompt: "Reply with the single word: pong",
227
+ promptSummary: `Connection test for ${task}`,
228
+ maxTokens: 16,
229
+ });
230
+ if (!result.ok) {
231
+ return {
232
+ task,
233
+ model,
234
+ status: "insufficient",
235
+ explanation: `Model could not be reached: ${extractApiErrorMessage(result.reason)}`,
236
+ };
237
+ }
238
+ return {
239
+ task,
240
+ model,
241
+ status: "verified",
242
+ explanation: `Model is reachable and responding.`,
243
+ };
244
+ }
245
+ function detectProvider() {
246
+ return process.env.SYNTH_LLM_PROVIDER ?? "anthropic";
247
+ }
248
+ //# sourceMappingURL=health.js.map