@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,319 @@
1
+ import crypto from "node:crypto";
2
+ import { EnvoyClient } from "./envoy-client.js";
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers to convert between persisted and domain types
5
+ // ---------------------------------------------------------------------------
6
+ function fromPersisted(p) {
7
+ return {
8
+ id: p.id,
9
+ name: p.name,
10
+ url: p.url,
11
+ token: p.token,
12
+ assignedEnvironments: p.assignedEnvironments,
13
+ assignedPartitions: p.assignedPartitions,
14
+ registeredAt: p.registeredAt,
15
+ lastHealthCheck: p.lastHealthCheck,
16
+ lastHealthStatus: p.lastHealthStatus,
17
+ cachedHostname: p.cachedHostname,
18
+ cachedOs: p.cachedOs,
19
+ cachedSummary: p.cachedSummary,
20
+ cachedReadiness: p.cachedReadiness,
21
+ };
22
+ }
23
+ function toPersisted(r) {
24
+ return {
25
+ id: r.id,
26
+ name: r.name,
27
+ url: r.url,
28
+ token: r.token,
29
+ assignedEnvironments: r.assignedEnvironments,
30
+ assignedPartitions: r.assignedPartitions,
31
+ registeredAt: r.registeredAt,
32
+ lastHealthCheck: r.lastHealthCheck,
33
+ lastHealthStatus: r.lastHealthStatus,
34
+ cachedHostname: r.cachedHostname,
35
+ cachedOs: r.cachedOs,
36
+ cachedSummary: r.cachedSummary,
37
+ cachedReadiness: r.cachedReadiness,
38
+ };
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // EnvoyRegistry — SQLite-backed registry for multiple Envoy instances
42
+ // ---------------------------------------------------------------------------
43
+ export class EnvoyRegistry {
44
+ store;
45
+ constructor(store) {
46
+ this.store = store;
47
+ }
48
+ /**
49
+ * Register a new Envoy. Returns the registration with a generated token.
50
+ */
51
+ register(params) {
52
+ const id = crypto.randomUUID();
53
+ const token = crypto.randomBytes(32).toString("hex");
54
+ const registration = {
55
+ id,
56
+ name: params.name,
57
+ url: params.url,
58
+ token,
59
+ assignedEnvironments: params.assignedEnvironments ?? [],
60
+ assignedPartitions: params.assignedPartitions ?? [],
61
+ registeredAt: new Date().toISOString(),
62
+ lastHealthCheck: null,
63
+ lastHealthStatus: null,
64
+ cachedHostname: null,
65
+ cachedOs: null,
66
+ cachedSummary: null,
67
+ cachedReadiness: null,
68
+ };
69
+ this.store?.insert(toPersisted(registration));
70
+ return registration;
71
+ }
72
+ /**
73
+ * Deregister an Envoy by ID.
74
+ */
75
+ deregister(id) {
76
+ if (this.store) {
77
+ return this.store.delete(id);
78
+ }
79
+ return false;
80
+ }
81
+ /**
82
+ * List all registered Envoys.
83
+ */
84
+ list() {
85
+ if (this.store) {
86
+ return this.store.list().map(fromPersisted);
87
+ }
88
+ return [];
89
+ }
90
+ /**
91
+ * Update an Envoy's assignment or name.
92
+ */
93
+ update(id, updates) {
94
+ if (!this.store)
95
+ return undefined;
96
+ const existing = this.store.getById(id);
97
+ if (!existing)
98
+ return undefined;
99
+ this.store.update(id, {
100
+ name: updates.name,
101
+ url: updates.url,
102
+ assignedEnvironments: updates.assignedEnvironments,
103
+ assignedPartitions: updates.assignedPartitions,
104
+ });
105
+ return fromPersisted(this.store.getById(id));
106
+ }
107
+ /**
108
+ * Ensure an envoy at `url` is registered with a specific `token`.
109
+ * If already registered, updates its token. If not, creates a new entry.
110
+ * Used to bootstrap the default envoy from environment variables (SYNTH_ENVOY_TOKEN).
111
+ */
112
+ ensureRegisteredWithToken(params, token) {
113
+ const existing = this.list().find((r) => r.url === params.url);
114
+ if (existing) {
115
+ this.store?.updateToken(existing.id, token);
116
+ return { ...existing, token };
117
+ }
118
+ const id = crypto.randomUUID();
119
+ const registration = {
120
+ id,
121
+ name: params.name,
122
+ url: params.url,
123
+ token,
124
+ assignedEnvironments: [],
125
+ assignedPartitions: [],
126
+ registeredAt: new Date().toISOString(),
127
+ lastHealthCheck: null,
128
+ lastHealthStatus: null,
129
+ cachedHostname: null,
130
+ cachedOs: null,
131
+ cachedSummary: null,
132
+ cachedReadiness: null,
133
+ };
134
+ this.store?.insert(toPersisted(registration));
135
+ return registration;
136
+ }
137
+ /**
138
+ * Rotate the token for an Envoy. Returns the new token.
139
+ */
140
+ rotateToken(id) {
141
+ if (!this.store)
142
+ return undefined;
143
+ const existing = this.store.getById(id);
144
+ if (!existing)
145
+ return undefined;
146
+ const newToken = crypto.randomBytes(32).toString("hex");
147
+ this.store.updateToken(id, newToken);
148
+ return newToken;
149
+ }
150
+ /**
151
+ * Validate a token against a registered Envoy.
152
+ */
153
+ validateToken(token) {
154
+ if (!this.store)
155
+ return undefined;
156
+ const persisted = this.store.getByToken(token);
157
+ return persisted ? fromPersisted(persisted) : undefined;
158
+ }
159
+ /**
160
+ * Update health status for an Envoy (called after probing).
161
+ */
162
+ updateHealth(id, status) {
163
+ this.store?.updateHealth(id, status, new Date().toISOString());
164
+ }
165
+ /**
166
+ * Find the best Envoy for a given environment.
167
+ * Priority: assigned + healthy > assigned + degraded > unassigned + healthy > fallback
168
+ */
169
+ findForEnvironment(environmentName) {
170
+ const all = this.list();
171
+ if (all.length === 0)
172
+ return undefined;
173
+ // Assigned and healthy
174
+ const assignedHealthy = all.find((e) => e.assignedEnvironments.includes(environmentName) &&
175
+ e.lastHealthStatus === "healthy");
176
+ if (assignedHealthy)
177
+ return assignedHealthy;
178
+ // Assigned (any status)
179
+ const assigned = all.find((e) => e.assignedEnvironments.includes(environmentName));
180
+ if (assigned)
181
+ return assigned;
182
+ // Unassigned and healthy (available for any environment)
183
+ const unassignedHealthy = all.find((e) => e.assignedEnvironments.length === 0 &&
184
+ e.lastHealthStatus === "healthy");
185
+ if (unassignedHealthy)
186
+ return unassignedHealthy;
187
+ // Unassigned (any status)
188
+ const unassigned = all.find((e) => e.assignedEnvironments.length === 0);
189
+ if (unassigned)
190
+ return unassigned;
191
+ // Fallback: any envoy
192
+ return all[0];
193
+ }
194
+ /**
195
+ * Return cached registry entry without probing (instant).
196
+ */
197
+ get(id) {
198
+ if (!this.store)
199
+ return undefined;
200
+ const persisted = this.store.getById(id);
201
+ if (!persisted)
202
+ return undefined;
203
+ const reg = fromPersisted(persisted);
204
+ const healthMap = {
205
+ healthy: "OK",
206
+ degraded: "Degraded",
207
+ unreachable: "Unreachable",
208
+ };
209
+ return {
210
+ ...reg,
211
+ health: healthMap[reg.lastHealthStatus ?? "unreachable"] ?? "Unreachable",
212
+ hostname: reg.cachedHostname,
213
+ os: reg.cachedOs,
214
+ lastSeen: reg.lastHealthCheck,
215
+ summary: reg.cachedSummary,
216
+ readiness: reg.cachedReadiness,
217
+ };
218
+ }
219
+ /**
220
+ * Return all cached registry entries without probing (instant).
221
+ */
222
+ listEntries() {
223
+ return this.list().map((reg) => {
224
+ const healthMap = {
225
+ healthy: "OK",
226
+ degraded: "Degraded",
227
+ unreachable: "Unreachable",
228
+ };
229
+ return {
230
+ ...reg,
231
+ health: healthMap[reg.lastHealthStatus ?? "unreachable"] ?? "Unreachable",
232
+ hostname: reg.cachedHostname,
233
+ os: reg.cachedOs,
234
+ lastSeen: reg.lastHealthCheck,
235
+ summary: reg.cachedSummary,
236
+ readiness: reg.cachedReadiness,
237
+ };
238
+ });
239
+ }
240
+ /**
241
+ * Probe an Envoy's health and update its registry entry.
242
+ */
243
+ async probe(id) {
244
+ if (!this.store)
245
+ return undefined;
246
+ const persisted = this.store.getById(id);
247
+ if (!persisted)
248
+ return undefined;
249
+ const registration = fromPersisted(persisted);
250
+ const client = new EnvoyClient(registration.url, 5000);
251
+ try {
252
+ const health = await client.checkHealth();
253
+ const status = health.status === "healthy" ? "healthy" : "degraded";
254
+ const timestamp = new Date().toISOString();
255
+ this.store.updateCachedProbe(id, {
256
+ lastHealthCheck: timestamp,
257
+ lastHealthStatus: status,
258
+ cachedHostname: health.hostname,
259
+ cachedOs: health.os ?? null,
260
+ cachedSummary: health.summary,
261
+ cachedReadiness: health.readiness,
262
+ });
263
+ return {
264
+ ...registration,
265
+ lastHealthCheck: timestamp,
266
+ lastHealthStatus: status,
267
+ cachedHostname: health.hostname,
268
+ cachedOs: health.os ?? null,
269
+ cachedSummary: health.summary,
270
+ cachedReadiness: health.readiness,
271
+ health: status === "healthy" ? "OK" : "Degraded",
272
+ hostname: health.hostname,
273
+ os: health.os ?? null,
274
+ lastSeen: health.timestamp,
275
+ summary: health.summary,
276
+ readiness: health.readiness,
277
+ };
278
+ }
279
+ catch {
280
+ const timestamp = new Date().toISOString();
281
+ this.store.updateCachedProbe(id, {
282
+ lastHealthCheck: timestamp,
283
+ lastHealthStatus: "unreachable",
284
+ cachedHostname: null,
285
+ cachedOs: null,
286
+ cachedSummary: null,
287
+ cachedReadiness: null,
288
+ });
289
+ return {
290
+ ...registration,
291
+ lastHealthCheck: timestamp,
292
+ lastHealthStatus: "unreachable",
293
+ cachedHostname: null,
294
+ cachedOs: null,
295
+ cachedSummary: null,
296
+ cachedReadiness: null,
297
+ health: "Unreachable",
298
+ hostname: null,
299
+ os: null,
300
+ lastSeen: null,
301
+ summary: null,
302
+ readiness: null,
303
+ };
304
+ }
305
+ }
306
+ /**
307
+ * Probe all registered Envoys and return their status.
308
+ */
309
+ async probeAll() {
310
+ const entries = [];
311
+ for (const envoy of this.list()) {
312
+ const entry = await this.probe(envoy.id);
313
+ if (entry)
314
+ entries.push(entry);
315
+ }
316
+ return entries;
317
+ }
318
+ }
319
+ //# sourceMappingURL=envoy-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envoy-registry.js","sourceRoot":"","sources":["../../src/agent/envoy-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Service health check abstraction.
3
+ *
4
+ * Injected into the Command Agent so tests can control health check outcomes
5
+ * without real network calls. In later phases, a real implementation will
6
+ * make HTTP calls to Envoy health endpoints.
7
+ */
8
+ export interface HealthCheckResult {
9
+ reachable: boolean;
10
+ responseTimeMs: number | null;
11
+ error: string | null;
12
+ }
13
+ export interface ServiceHealthChecker {
14
+ /**
15
+ * Check if the target service/environment is healthy and reachable.
16
+ */
17
+ check(serviceId: string, context: {
18
+ partitionId: string;
19
+ environmentName: string;
20
+ }): Promise<HealthCheckResult>;
21
+ }
22
+ /**
23
+ * Default health checker — always reports healthy.
24
+ * Used in development and when no real infrastructure is connected.
25
+ */
26
+ export declare class DefaultHealthChecker implements ServiceHealthChecker {
27
+ check(): Promise<HealthCheckResult>;
28
+ }
29
+ /**
30
+ * Real health checker that makes HTTP requests to an Envoy health endpoint.
31
+ * Used when an Envoy URL is configured.
32
+ */
33
+ export declare class EnvoyHealthChecker implements ServiceHealthChecker {
34
+ private readonly envoyUrl;
35
+ private readonly timeoutMs;
36
+ constructor(envoyUrl: string, timeoutMs?: number);
37
+ check(): Promise<HealthCheckResult>;
38
+ }
39
+ //# sourceMappingURL=health-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-checker.d.ts","sourceRoot":"","sources":["../../src/agent/health-checker.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GACxD,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,oBAAoB;IACzD,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAG1C;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,oBAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAU,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAA5C,QAAQ,EAAE,MAAM,EAAmB,SAAS,SAAO;IAE1E,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;CA0B1C"}
@@ -0,0 +1,49 @@
1
+ import { serverLog, serverWarn, serverError } from "../logger.js";
2
+ /**
3
+ * Default health checker — always reports healthy.
4
+ * Used in development and when no real infrastructure is connected.
5
+ */
6
+ export class DefaultHealthChecker {
7
+ async check() {
8
+ return { reachable: true, responseTimeMs: 1, error: null };
9
+ }
10
+ }
11
+ /**
12
+ * Real health checker that makes HTTP requests to an Envoy health endpoint.
13
+ * Used when an Envoy URL is configured.
14
+ */
15
+ export class EnvoyHealthChecker {
16
+ envoyUrl;
17
+ timeoutMs;
18
+ constructor(envoyUrl, timeoutMs = 5000) {
19
+ this.envoyUrl = envoyUrl;
20
+ this.timeoutMs = timeoutMs;
21
+ }
22
+ async check() {
23
+ serverLog("HEALTH-CHECK", { url: this.envoyUrl });
24
+ const start = Date.now();
25
+ try {
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
28
+ const res = await fetch(`${this.envoyUrl}/health`, { signal: controller.signal });
29
+ clearTimeout(timeout);
30
+ const responseTimeMs = Date.now() - start;
31
+ if (res.ok) {
32
+ serverLog("HEALTH-OK", { url: this.envoyUrl, responseTimeMs });
33
+ return { reachable: true, responseTimeMs, error: null };
34
+ }
35
+ serverWarn("HEALTH-FAILED", { url: this.envoyUrl, status: res.status, responseTimeMs });
36
+ return { reachable: false, responseTimeMs, error: `HTTP ${res.status}` };
37
+ }
38
+ catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ serverError("HEALTH-ERROR", { url: this.envoyUrl, error: message });
41
+ return {
42
+ reachable: false,
43
+ responseTimeMs: null,
44
+ error: message,
45
+ };
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=health-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-checker.js","sourceRoot":"","sources":["../../src/agent/health-checker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA0BlE;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAC/B,KAAK,CAAC,KAAK;QACT,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC7D,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACA;IAAmC;IAAhE,YAA6B,QAAgB,EAAmB,YAAY,IAAI;QAAnD,aAAQ,GAAR,QAAQ,CAAQ;QAAmB,cAAS,GAAT,SAAS,CAAO;IAAG,CAAC;IAEpF,KAAK,CAAC,KAAK;QACT,SAAS,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACrE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAE1C,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,SAAS,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;gBAC/D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC1D,CAAC;YACD,UAAU,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;YACxF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,WAAW,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACpE,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,cAAc,EAAE,IAAI;gBACpB,KAAK,EAAE,OAAO;aACf,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ import type { McpServerConfig } from "@synth-deploy/core";
2
+ export interface McpToolResult {
3
+ serverName: string;
4
+ toolName: string;
5
+ result: unknown;
6
+ error?: string;
7
+ }
8
+ /**
9
+ * Manages connections to external MCP servers (monitoring, incident management,
10
+ * etc.) and exposes their tools/resources for pre-deployment checks and
11
+ * diagnostic investigations.
12
+ *
13
+ * Design principles:
14
+ * - Graceful degradation: unreachable servers are logged and skipped, never block deployments
15
+ * - All external data access must be recorded to the Debrief by the caller
16
+ * - Lightweight: if no servers are configured, the manager is a no-op
17
+ */
18
+ export declare class McpClientManager {
19
+ private clients;
20
+ connect(config: McpServerConfig): Promise<void>;
21
+ connectAll(configs: McpServerConfig[]): Promise<void>;
22
+ listTools(): Promise<Array<{
23
+ server: string;
24
+ name: string;
25
+ description?: string;
26
+ }>>;
27
+ callTool(serverName: string, toolName: string, args: Record<string, unknown>): Promise<McpToolResult>;
28
+ listResources(serverName: string): Promise<Array<{
29
+ uri: string;
30
+ name?: string;
31
+ }>>;
32
+ getConnectedServers(): string[];
33
+ disconnect(serverName: string): Promise<void>;
34
+ disconnectAll(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=mcp-client-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-manager.d.ts","sourceRoot":"","sources":["../../src/agent/mcp-client-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CACH;IAEN,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/C,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,SAAS,IAAI,OAAO,CACxB,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAC9D;IAuBK,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;IAqBnB,aAAa,CACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAWjD,mBAAmB,IAAI,MAAM,EAAE;IAIzB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7C,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAKrC"}
@@ -0,0 +1,106 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+ /**
4
+ * Manages connections to external MCP servers (monitoring, incident management,
5
+ * etc.) and exposes their tools/resources for pre-deployment checks and
6
+ * diagnostic investigations.
7
+ *
8
+ * Design principles:
9
+ * - Graceful degradation: unreachable servers are logged and skipped, never block deployments
10
+ * - All external data access must be recorded to the Debrief by the caller
11
+ * - Lightweight: if no servers are configured, the manager is a no-op
12
+ */
13
+ export class McpClientManager {
14
+ clients = new Map();
15
+ async connect(config) {
16
+ try {
17
+ const transport = new StreamableHTTPClientTransport(new URL(config.url));
18
+ const client = new Client({
19
+ name: "synth-server",
20
+ version: "0.1.0",
21
+ });
22
+ await client.connect(transport);
23
+ this.clients.set(config.name, { client, config });
24
+ }
25
+ catch (error) {
26
+ // Graceful degradation -- log but don't throw
27
+ console.warn(`[MCP] Failed to connect to ${config.name} at ${config.url}: ${error}`);
28
+ }
29
+ }
30
+ async connectAll(configs) {
31
+ await Promise.allSettled(configs.map((c) => this.connect(c)));
32
+ }
33
+ async listTools() {
34
+ const tools = [];
35
+ for (const [name, { client }] of this.clients) {
36
+ try {
37
+ const result = await client.listTools();
38
+ for (const tool of result.tools) {
39
+ tools.push({
40
+ server: name,
41
+ name: tool.name,
42
+ description: tool.description,
43
+ });
44
+ }
45
+ }
46
+ catch {
47
+ // Server unreachable -- skip
48
+ }
49
+ }
50
+ return tools;
51
+ }
52
+ async callTool(serverName, toolName, args) {
53
+ const entry = this.clients.get(serverName);
54
+ if (!entry) {
55
+ return {
56
+ serverName,
57
+ toolName,
58
+ result: null,
59
+ error: `Server '${serverName}' not connected`,
60
+ };
61
+ }
62
+ try {
63
+ const result = await entry.client.callTool({
64
+ name: toolName,
65
+ arguments: args,
66
+ });
67
+ return { serverName, toolName, result: result.content };
68
+ }
69
+ catch (error) {
70
+ return { serverName, toolName, result: null, error: String(error) };
71
+ }
72
+ }
73
+ async listResources(serverName) {
74
+ const entry = this.clients.get(serverName);
75
+ if (!entry)
76
+ return [];
77
+ try {
78
+ const result = await entry.client.listResources();
79
+ return result.resources.map((r) => ({ uri: r.uri, name: r.name }));
80
+ }
81
+ catch {
82
+ return [];
83
+ }
84
+ }
85
+ getConnectedServers() {
86
+ return Array.from(this.clients.keys());
87
+ }
88
+ async disconnect(serverName) {
89
+ const entry = this.clients.get(serverName);
90
+ if (entry) {
91
+ try {
92
+ await entry.client.close();
93
+ }
94
+ catch {
95
+ /* ignore */
96
+ }
97
+ this.clients.delete(serverName);
98
+ }
99
+ }
100
+ async disconnectAll() {
101
+ for (const name of this.clients.keys()) {
102
+ await this.disconnect(name);
103
+ }
104
+ }
105
+ }
106
+ //# sourceMappingURL=mcp-client-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-manager.js","sourceRoot":"","sources":["../../src/agent/mcp-client-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { DebriefWriter } from "@synth-deploy/core";
2
+ import type { DeploymentStore } from "./synth-agent.js";
3
+ /**
4
+ * Scans for deployments stuck in "running" status beyond the stale threshold
5
+ * and marks them as failed with a clear explanation.
6
+ *
7
+ * Returns the number of deployments marked as stale.
8
+ */
9
+ export declare function markStaleDeployments(deployments: DeploymentStore, debrief: DebriefWriter, thresholdMs?: number): number;
10
+ /**
11
+ * Starts a periodic scan for stale deployments.
12
+ * Returns a cleanup function to stop the interval.
13
+ */
14
+ export declare function startStaleDeploymentScanner(deployments: DeploymentStore, debrief: DebriefWriter, intervalMs?: number, thresholdMs?: number): () => void;
15
+ //# sourceMappingURL=stale-deployment-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-deployment-detector.d.ts","sourceRoot":"","sources":["../../src/agent/stale-deployment-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUxD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,aAAa,EACtB,WAAW,GAAE,MAAmC,GAC/C,MAAM,CAqCR;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,aAAa,EACtB,UAAU,GAAE,MAAiC,EAC7C,WAAW,GAAE,MAAmC,GAC/C,MAAM,IAAI,CASZ"}
@@ -0,0 +1,50 @@
1
+ const DEFAULT_STALE_THRESHOLD_MS = Number(process.env.SYNTH_STALE_DEPLOYMENT_TIMEOUT_MS ?? 30 * 60 * 1000);
2
+ const DEFAULT_SCAN_INTERVAL_MS = Number(process.env.SYNTH_STALE_SCAN_INTERVAL_MS ?? 60 * 1000);
3
+ /**
4
+ * Scans for deployments stuck in "running" status beyond the stale threshold
5
+ * and marks them as failed with a clear explanation.
6
+ *
7
+ * Returns the number of deployments marked as stale.
8
+ */
9
+ export function markStaleDeployments(deployments, debrief, thresholdMs = DEFAULT_STALE_THRESHOLD_MS) {
10
+ const now = Date.now();
11
+ const stale = deployments.list().filter((d) => d.status === "running" &&
12
+ now - new Date(d.createdAt).getTime() > thresholdMs);
13
+ for (const deployment of stale) {
14
+ deployment.status = "failed";
15
+ deployments.save(deployment);
16
+ const staleDurationMin = Math.round((now - new Date(deployment.createdAt).getTime()) / 60_000);
17
+ debrief.record({
18
+ partitionId: deployment.partitionId ?? null,
19
+ deploymentId: deployment.id,
20
+ agent: "server",
21
+ decisionType: "deployment-failure",
22
+ decision: `Marked deployment as failed: exceeded ${Math.round(thresholdMs / 60_000)} minute stale threshold`,
23
+ reasoning: `Deployment ${deployment.id.slice(0, 8)} has been in "running" status for ${staleDurationMin} minutes ` +
24
+ `without receiving a completion report. This typically indicates Command lost connection to the Envoy ` +
25
+ `or the Envoy process crashed during execution. The deployment has been marked as failed to prevent ` +
26
+ `indefinite "running" status.`,
27
+ context: {
28
+ deploymentId: deployment.id,
29
+ partitionId: deployment.partitionId ?? null,
30
+ staleDurationMinutes: staleDurationMin,
31
+ thresholdMinutes: Math.round(thresholdMs / 60_000),
32
+ },
33
+ });
34
+ }
35
+ return stale.length;
36
+ }
37
+ /**
38
+ * Starts a periodic scan for stale deployments.
39
+ * Returns a cleanup function to stop the interval.
40
+ */
41
+ export function startStaleDeploymentScanner(deployments, debrief, intervalMs = DEFAULT_SCAN_INTERVAL_MS, thresholdMs = DEFAULT_STALE_THRESHOLD_MS) {
42
+ const timer = setInterval(() => {
43
+ const count = markStaleDeployments(deployments, debrief, thresholdMs);
44
+ if (count > 0) {
45
+ console.log(`[stale-detector] Marked ${count} stale deployment(s) as failed`);
46
+ }
47
+ }, intervalMs);
48
+ return () => clearInterval(timer);
49
+ }
50
+ //# sourceMappingURL=stale-deployment-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-deployment-detector.js","sourceRoot":"","sources":["../../src/agent/stale-deployment-detector.ts"],"names":[],"mappings":"AAGA,MAAM,0BAA0B,GAAG,MAAM,CACvC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAChE,CAAC;AAEF,MAAM,wBAAwB,GAAG,MAAM,CACrC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,GAAG,IAAI,CACtD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAA4B,EAC5B,OAAsB,EACtB,cAAsB,0BAA0B;IAEhD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,CACrC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,SAAS;QACtB,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,WAAW,CACtD,CAAC;IAEF,KAAK,MAAM,UAAU,IAAI,KAAK,EAAE,CAAC;QAC/B,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC7B,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CACjC,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAC1D,CAAC;QAEF,OAAO,CAAC,MAAM,CAAC;YACb,WAAW,EAAE,UAAU,CAAC,WAAW,IAAI,IAAI;YAC3C,YAAY,EAAE,UAAU,CAAC,EAAE;YAC3B,KAAK,EAAE,QAAQ;YACf,YAAY,EAAE,oBAAoB;YAClC,QAAQ,EAAE,yCAAyC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,yBAAyB;YAC5G,SAAS,EACP,cAAc,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,qCAAqC,gBAAgB,WAAW;gBACvG,uGAAuG;gBACvG,qGAAqG;gBACrG,8BAA8B;YAChC,OAAO,EAAE;gBACP,YAAY,EAAE,UAAU,CAAC,EAAE;gBAC3B,WAAW,EAAE,UAAU,CAAC,WAAW,IAAI,IAAI;gBAC3C,oBAAoB,EAAE,gBAAgB;gBACtC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC;aACnD;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,WAA4B,EAC5B,OAAsB,EACtB,aAAqB,wBAAwB,EAC7C,cAAsB,0BAA0B;IAEhD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,KAAK,GAAG,oBAAoB,CAAC,WAAW,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;QACtE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,gCAAgC,CAAC,CAAC;QAChF,CAAC;IACH,CAAC,EAAE,UAAU,CAAC,CAAC;IAEf,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC"}