mergen-server 1.4.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 (308) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +96 -0
  3. package/dist/__stubs__/calibration.js +471 -0
  4. package/dist/__stubs__/causal.js +601 -0
  5. package/dist/__stubs__/closed-source.js +150 -0
  6. package/dist/__stubs__/license.js +35 -0
  7. package/dist/__stubs__/plans.js +197 -0
  8. package/dist/app.js +467 -0
  9. package/dist/cli.js +250 -0
  10. package/dist/commands/agent-identity.js +79 -0
  11. package/dist/commands/gate.js +107 -0
  12. package/dist/commands/github.js +632 -0
  13. package/dist/commands/incident.js +644 -0
  14. package/dist/commands/policy.js +710 -0
  15. package/dist/commands/scan.js +243 -0
  16. package/dist/commands/setup.js +1218 -0
  17. package/dist/commands/shared.js +109 -0
  18. package/dist/commands/team.js +809 -0
  19. package/dist/datadog/blame-attribution.js +228 -0
  20. package/dist/datadog/client.js +159 -0
  21. package/dist/datadog/compactor.js +169 -0
  22. package/dist/datadog/fingerprinter.js +34 -0
  23. package/dist/datadog/incident-state.js +21 -0
  24. package/dist/datadog/line-matcher.js +56 -0
  25. package/dist/datadog/memory-store.js +399 -0
  26. package/dist/datadog/otel-trace.js +67 -0
  27. package/dist/index.js +505 -0
  28. package/dist/intelligence/action-risk.js +132 -0
  29. package/dist/intelligence/activity-feed.js +40 -0
  30. package/dist/intelligence/agent-identity.js +115 -0
  31. package/dist/intelligence/agent-pipeline.js +329 -0
  32. package/dist/intelligence/agent-profiles.js +81 -0
  33. package/dist/intelligence/ai-commit.js +16 -0
  34. package/dist/intelligence/approval-analytics.js +42 -0
  35. package/dist/intelligence/approval-events.js +5 -0
  36. package/dist/intelligence/arch-boundaries.js +94 -0
  37. package/dist/intelligence/arch-graph.js +117 -0
  38. package/dist/intelligence/autonomy.js +397 -0
  39. package/dist/intelligence/azure-broker.js +57 -0
  40. package/dist/intelligence/azure-proxy-sessions.js +49 -0
  41. package/dist/intelligence/baseline.js +1 -0
  42. package/dist/intelligence/behavior-baseline.js +134 -0
  43. package/dist/intelligence/billing.js +1 -0
  44. package/dist/intelligence/blast-radius.js +224 -0
  45. package/dist/intelligence/bypass.js +157 -0
  46. package/dist/intelligence/calibration-classifier.js +77 -0
  47. package/dist/intelligence/calibration-git-sync.js +74 -0
  48. package/dist/intelligence/calibration.js +1 -0
  49. package/dist/intelligence/cascade-detector.js +58 -0
  50. package/dist/intelligence/case-study-generator.js +126 -0
  51. package/dist/intelligence/causal-graph.js +126 -0
  52. package/dist/intelligence/causal.js +1 -0
  53. package/dist/intelligence/change-risk.js +102 -0
  54. package/dist/intelligence/compliance-report.js +134 -0
  55. package/dist/intelligence/confidence-report.js +85 -0
  56. package/dist/intelligence/corpus-to-policy.js +140 -0
  57. package/dist/intelligence/credential-broker.js +112 -0
  58. package/dist/intelligence/credential-misuse.js +39 -0
  59. package/dist/intelligence/debug-sessions.js +108 -0
  60. package/dist/intelligence/decision-escalation.js +36 -0
  61. package/dist/intelligence/decision-memory.js +47 -0
  62. package/dist/intelligence/decision-similarity.js +85 -0
  63. package/dist/intelligence/default-safety-tests.js +53 -0
  64. package/dist/intelligence/degradation-watcher.js +108 -0
  65. package/dist/intelligence/detector-plugins.js +70 -0
  66. package/dist/intelligence/detectors.js +1 -0
  67. package/dist/intelligence/device-auth.js +138 -0
  68. package/dist/intelligence/diff-size.js +42 -0
  69. package/dist/intelligence/ebpf-verify.js +45 -0
  70. package/dist/intelligence/enterprise-policy-engine.js +1122 -0
  71. package/dist/intelligence/error-fingerprint.js +1 -0
  72. package/dist/intelligence/execution-gate.js +100 -0
  73. package/dist/intelligence/execution-mode.js +22 -0
  74. package/dist/intelligence/gate-analytics.js +256 -0
  75. package/dist/intelligence/gate-decision.js +64 -0
  76. package/dist/intelligence/gcp-broker.js +50 -0
  77. package/dist/intelligence/git-adr-sync.js +207 -0
  78. package/dist/intelligence/hitl-hold.js +460 -0
  79. package/dist/intelligence/hypothesis-history.js +93 -0
  80. package/dist/intelligence/idp-identity.js +83 -0
  81. package/dist/intelligence/impl-critic.js +216 -0
  82. package/dist/intelligence/incident-autopilot.js +622 -0
  83. package/dist/intelligence/incident-replay.js +162 -0
  84. package/dist/intelligence/incident-result-cache.js +33 -0
  85. package/dist/intelligence/incident-similarity.js +58 -0
  86. package/dist/intelligence/infra-detectors.js +312 -0
  87. package/dist/intelligence/license.js +185 -0
  88. package/dist/intelligence/llm-spokesperson.js +62 -0
  89. package/dist/intelligence/mcp-prompts.js +320 -0
  90. package/dist/intelligence/mcp-resources.js +171 -0
  91. package/dist/intelligence/normalize.js +108 -0
  92. package/dist/intelligence/notifications.js +83 -0
  93. package/dist/intelligence/oidc-issuer.js +87 -0
  94. package/dist/intelligence/override-corpus.js +597 -0
  95. package/dist/intelligence/planning-gate.js +84 -0
  96. package/dist/intelligence/plans.js +223 -0
  97. package/dist/intelligence/platt-scaling.js +164 -0
  98. package/dist/intelligence/policy-proposals.js +84 -0
  99. package/dist/intelligence/policy-suggester.js +161 -0
  100. package/dist/intelligence/policy-sync.js +103 -0
  101. package/dist/intelligence/policy-test-runner.js +35 -0
  102. package/dist/intelligence/postmortem-parser.js +181 -0
  103. package/dist/intelligence/postmortem-retrieval.js +224 -0
  104. package/dist/intelligence/postmortem-store.js +596 -0
  105. package/dist/intelligence/pr-commenter.js +81 -0
  106. package/dist/intelligence/pr-policy-gate.js +38 -0
  107. package/dist/intelligence/pr-shadow-analyzer.js +229 -0
  108. package/dist/intelligence/process-tree.js +108 -0
  109. package/dist/intelligence/prompts.js +1 -0
  110. package/dist/intelligence/replay.js +38 -0
  111. package/dist/intelligence/repro-steps.js +1 -0
  112. package/dist/intelligence/resource-extractors.js +0 -0
  113. package/dist/intelligence/resource-file-context.js +138 -0
  114. package/dist/intelligence/risk-score.js +22 -0
  115. package/dist/intelligence/rollback.js +137 -0
  116. package/dist/intelligence/runbook-updater.js +143 -0
  117. package/dist/intelligence/sandbox.js +194 -0
  118. package/dist/intelligence/script-trust.js +104 -0
  119. package/dist/intelligence/session-metrics.js +67 -0
  120. package/dist/intelligence/session-threat-tracker.js +231 -0
  121. package/dist/intelligence/shadow-digest-cron.js +81 -0
  122. package/dist/intelligence/shadow-log.js +284 -0
  123. package/dist/intelligence/shell-ast.js +145 -0
  124. package/dist/intelligence/siem-forward.js +104 -0
  125. package/dist/intelligence/slack-digest.js +151 -0
  126. package/dist/intelligence/slack-override-loop.js +217 -0
  127. package/dist/intelligence/slack-routing.js +67 -0
  128. package/dist/intelligence/slack.js +900 -0
  129. package/dist/intelligence/sql-ast.js +31 -0
  130. package/dist/intelligence/team.js +1 -0
  131. package/dist/intelligence/telemetry.js +76 -0
  132. package/dist/intelligence/threshold-optimizer.js +75 -0
  133. package/dist/intelligence/token-budget.js +66 -0
  134. package/dist/intelligence/tool-guard.js +708 -0
  135. package/dist/intelligence/tool-manifest 2.js +74 -0
  136. package/dist/intelligence/tool-manifest.js +125 -0
  137. package/dist/intelligence/tools-analysis.js +857 -0
  138. package/dist/intelligence/tools-arch.js +152 -0
  139. package/dist/intelligence/tools-autonomy.js +495 -0
  140. package/dist/intelligence/tools-blast-radius.js +140 -0
  141. package/dist/intelligence/tools-browser.js +431 -0
  142. package/dist/intelligence/tools-change-timeline.js +154 -0
  143. package/dist/intelligence/tools-credentials.js +117 -0
  144. package/dist/intelligence/tools-datadog.js +233 -0
  145. package/dist/intelligence/tools-debug-sessions.js +209 -0
  146. package/dist/intelligence/tools-discovery.js +284 -0
  147. package/dist/intelligence/tools-infra.js +612 -0
  148. package/dist/intelligence/tools-intent.js +128 -0
  149. package/dist/intelligence/tools-memory.js +514 -0
  150. package/dist/intelligence/tools-runbook.js +951 -0
  151. package/dist/intelligence/tools-sessions.js +107 -0
  152. package/dist/intelligence/tools-state.js +232 -0
  153. package/dist/intelligence/tools-utility.js +430 -0
  154. package/dist/intelligence/tools-validate.js +215 -0
  155. package/dist/intelligence/tools.js +1 -0
  156. package/dist/intelligence/trace-context.js +11 -0
  157. package/dist/intelligence/unclassified-clusters.js +87 -0
  158. package/dist/intelligence/usage.js +195 -0
  159. package/dist/routes/active-authors.js +63 -0
  160. package/dist/routes/activity-feed.js +21 -0
  161. package/dist/routes/adr.js +117 -0
  162. package/dist/routes/agent-activity.js +184 -0
  163. package/dist/routes/agent-blunders.js +163 -0
  164. package/dist/routes/agents.js +175 -0
  165. package/dist/routes/api-keys.js +65 -0
  166. package/dist/routes/arch.js +75 -0
  167. package/dist/routes/audit-export.js +93 -0
  168. package/dist/routes/billing-dashboard.js +158 -0
  169. package/dist/routes/billing-outcome.js +77 -0
  170. package/dist/routes/calibration.js +305 -0
  171. package/dist/routes/ci-gate-history.js +20 -0
  172. package/dist/routes/ci-gate.js +232 -0
  173. package/dist/routes/ci.js +293 -0
  174. package/dist/routes/compliance-report.js +24 -0
  175. package/dist/routes/confidence.js +30 -0
  176. package/dist/routes/credentials.js +119 -0
  177. package/dist/routes/dashboard.js +1407 -0
  178. package/dist/routes/decision-memory.js +42 -0
  179. package/dist/routes/demo.js +844 -0
  180. package/dist/routes/device-auth-stub.js +173 -0
  181. package/dist/routes/explain-why.js +39 -0
  182. package/dist/routes/gate-analytics.js +213 -0
  183. package/dist/routes/gate.js +28 -0
  184. package/dist/routes/github-webhook.js +364 -0
  185. package/dist/routes/habituation.js +30 -0
  186. package/dist/routes/health-integrations.js +243 -0
  187. package/dist/routes/heartbeats.js +45 -0
  188. package/dist/routes/hitl.js +364 -0
  189. package/dist/routes/impact-report.js +645 -0
  190. package/dist/routes/incident-webhook.js +63 -0
  191. package/dist/routes/incidents.js +366 -0
  192. package/dist/routes/layers.js +62 -0
  193. package/dist/routes/license.js +187 -0
  194. package/dist/routes/oidc-issuer.js +30 -0
  195. package/dist/routes/onboarding.js +170 -0
  196. package/dist/routes/otel.js +67 -0
  197. package/dist/routes/otlp-receiver.js +179 -0
  198. package/dist/routes/overrides.js +241 -0
  199. package/dist/routes/pagerduty.js +258 -0
  200. package/dist/routes/policies.js +809 -0
  201. package/dist/routes/policy-nl.js +169 -0
  202. package/dist/routes/postmortem.js +102 -0
  203. package/dist/routes/pr-shadow.js +47 -0
  204. package/dist/routes/rbac.js +61 -0
  205. package/dist/routes/replay.js +17 -0
  206. package/dist/routes/risk-report.js +164 -0
  207. package/dist/routes/runbooks.js +125 -0
  208. package/dist/routes/safety-test.js +174 -0
  209. package/dist/routes/sdk.js +222 -0
  210. package/dist/routes/sensor.js +819 -0
  211. package/dist/routes/sentry.js +140 -0
  212. package/dist/routes/sessions.js +43 -0
  213. package/dist/routes/setup-ui.js +594 -0
  214. package/dist/routes/shadow-report.js +107 -0
  215. package/dist/routes/slack-routing.js +171 -0
  216. package/dist/routes/team-usage.js +71 -0
  217. package/dist/routes/telemetry.js +28 -0
  218. package/dist/routes/tenants.js +199 -0
  219. package/dist/routes/tickets.js +167 -0
  220. package/dist/routes/validate.js +13 -0
  221. package/dist/routes/war-room.js +113 -0
  222. package/dist/scripts/check-arch.js +22 -0
  223. package/dist/seeds/community-corpus.js +176 -0
  224. package/dist/seeds/corpus.js +1224 -0
  225. package/dist/sensor/action-ledger.js +327 -0
  226. package/dist/sensor/adr-store.js +140 -0
  227. package/dist/sensor/agent-blunder-store.js +370 -0
  228. package/dist/sensor/agent-context-store.js +225 -0
  229. package/dist/sensor/agent-memory-store.js +231 -0
  230. package/dist/sensor/audit-fetch.js +23 -0
  231. package/dist/sensor/audit-log.js +273 -0
  232. package/dist/sensor/buffer-schemas.js +225 -0
  233. package/dist/sensor/buffer.js +599 -0
  234. package/dist/sensor/bypass-tracker.js +93 -0
  235. package/dist/sensor/ci-gate-history.js +70 -0
  236. package/dist/sensor/cloud-auth.js +183 -0
  237. package/dist/sensor/commit-context-store.js +235 -0
  238. package/dist/sensor/docker-log-stream.js +164 -0
  239. package/dist/sensor/docker-monitor.js +122 -0
  240. package/dist/sensor/extended-buffer.js +46 -0
  241. package/dist/sensor/feedback-token.js +41 -0
  242. package/dist/sensor/file-lock.js +6 -0
  243. package/dist/sensor/fs-watcher.js +106 -0
  244. package/dist/sensor/gate-heartbeat.js +122 -0
  245. package/dist/sensor/git-suspect.js +136 -0
  246. package/dist/sensor/habituation-store.js +77 -0
  247. package/dist/sensor/heartbeat-monitor.js +133 -0
  248. package/dist/sensor/incident-store.js +340 -0
  249. package/dist/sensor/infra-normalizer.js +513 -0
  250. package/dist/sensor/ingest.js +339 -0
  251. package/dist/sensor/jest-reporter.js +52 -0
  252. package/dist/sensor/k8s-events.js +132 -0
  253. package/dist/sensor/layer2-store.js +111 -0
  254. package/dist/sensor/layer3-store.js +230 -0
  255. package/dist/sensor/layer4-store.js +147 -0
  256. package/dist/sensor/logger.js +13 -0
  257. package/dist/sensor/otel-exporter.js +161 -0
  258. package/dist/sensor/paths.js +71 -0
  259. package/dist/sensor/policy-history.js +147 -0
  260. package/dist/sensor/pr-shadow-store.js +83 -0
  261. package/dist/sensor/process-watcher.js +162 -0
  262. package/dist/sensor/rbac.js +92 -0
  263. package/dist/sensor/redact.js +114 -0
  264. package/dist/sensor/redis-store.js +191 -0
  265. package/dist/sensor/route-reachability.js +64 -0
  266. package/dist/sensor/security-utils.js +18 -0
  267. package/dist/sensor/service-graph.js +170 -0
  268. package/dist/sensor/service-topology.js +227 -0
  269. package/dist/sensor/session-history.js +74 -0
  270. package/dist/sensor/session-persist.js +38 -0
  271. package/dist/sensor/shadow-promote.js +122 -0
  272. package/dist/sensor/sourcemap.js +281 -0
  273. package/dist/sensor/sqlite-store.js +205 -0
  274. package/dist/sensor/sso.js +164 -0
  275. package/dist/sensor/vitest-reporter.js +65 -0
  276. package/dist/sensor/watcher.js +48 -0
  277. package/dist/storage/interfaces.js +0 -0
  278. package/dist/storage/pg/pg-action-ledger.js +138 -0
  279. package/dist/storage/pg/pg-approval-store.js +107 -0
  280. package/dist/storage/pg/pg-blunder-store.js +193 -0
  281. package/dist/storage/pg/pg-ci-gate-history.js +83 -0
  282. package/dist/storage/pg/pg-client.js +24 -0
  283. package/dist/storage/pg/pg-event-store.js +63 -0
  284. package/dist/storage/pg/pg-incident-store.js +190 -0
  285. package/dist/storage/pg/pg-migrations.js +26 -0
  286. package/dist/storage/pg/pg-override-corpus.js +447 -0
  287. package/dist/storage/pg/pg-shadow-log.js +129 -0
  288. package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
  289. package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
  290. package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
  291. package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
  292. package/dist/storage/sqlite/sqlite-event-store.js +29 -0
  293. package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
  294. package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
  295. package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
  296. package/dist/storage/store-factory.js +55 -0
  297. package/dist/storage/store-registry.js +31 -0
  298. package/dist/update-checker.js +107 -0
  299. package/dist/workers/autopilot-worker.js +28 -0
  300. package/dist/workers/notification-worker.js +28 -0
  301. package/dist/workers/queues.js +58 -0
  302. package/dist/workers/validation-worker.js +24 -0
  303. package/dist/workers/worker-registry.js +18 -0
  304. package/package.json +123 -0
  305. package/sdk/mergen-inject.js +260 -0
  306. package/sdk/node.js +313 -0
  307. package/sdk/vite-plugin.ts +57 -0
  308. package/sdk/webpack-plugin.js +106 -0
@@ -0,0 +1,644 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { resolve, join } from "path";
3
+ import { log, success, error, hr, ask, sleep, SERVER_ENTRY } from "./shared.js";
4
+ async function postmortemCommand(args) {
5
+ let port = 3e3;
6
+ for (let p = 3e3; p <= 3010; p++) {
7
+ try {
8
+ const r = await fetch(`http://127.0.0.1:${p}/health`, { signal: AbortSignal.timeout(600) });
9
+ if (r.ok) {
10
+ port = p;
11
+ break;
12
+ }
13
+ } catch {
14
+ }
15
+ }
16
+ const hours = parseFloat(args[1] ?? "1") || 1;
17
+ const sha = args[2] ?? "";
18
+ const to = Date.now();
19
+ const from = to - hours * 60 * 60 * 1e3;
20
+ const params = new URLSearchParams({ from: String(Math.round(from)), to: String(to), format: "md" });
21
+ if (sha) params.set("sha", sha);
22
+ console.log(`Generating postmortem (last ${hours}h)...
23
+ `);
24
+ try {
25
+ const r = await fetch(`http://127.0.0.1:${port}/export/incident?${params}`, { signal: AbortSignal.timeout(5e3) });
26
+ if (!r.ok) {
27
+ error(`Server returned ${r.status}`);
28
+ process.exit(1);
29
+ }
30
+ const md = await r.text();
31
+ const { writeFileSync: writeFileSync2 } = await import("fs");
32
+ const filename = `postmortem-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}.md`;
33
+ writeFileSync2(filename, md, "utf8");
34
+ process.stdout.write(md + "\n");
35
+ hr();
36
+ success(`Saved to: ${filename}`);
37
+ } catch (err) {
38
+ error(err instanceof Error ? err.message : String(err));
39
+ process.exit(1);
40
+ }
41
+ }
42
+ async function timelineCommand(args) {
43
+ const seconds = parseInt(args[1] ?? "300", 10) || 300;
44
+ let port = 0;
45
+ for (let p = 3e3; p <= 3010; p++) {
46
+ try {
47
+ const r = await fetch(`http://127.0.0.1:${p}/health`, { signal: AbortSignal.timeout(600) });
48
+ if (r.ok) {
49
+ port = p;
50
+ break;
51
+ }
52
+ } catch {
53
+ }
54
+ }
55
+ if (!port) {
56
+ error("Server not running. Start with: mergen-server start");
57
+ process.exit(1);
58
+ }
59
+ let data = {};
60
+ try {
61
+ const r = await fetch(`http://127.0.0.1:${port}/timeline/unified?seconds=${seconds}&limit=100`, { signal: AbortSignal.timeout(3e3) });
62
+ data = await r.json();
63
+ } catch {
64
+ error("Failed to fetch timeline");
65
+ process.exit(1);
66
+ }
67
+ const rows = data.rows ?? [];
68
+ const rc = data.rootCause ?? null;
69
+ const KIND_ICON = {
70
+ error: "\u{1F534}",
71
+ warn: "\u{1F7E1}",
72
+ log: "\u2B1C",
73
+ request: "\u{1F7E0}",
74
+ context: "\u2B1C",
75
+ terminal: "\u{1F4BB}",
76
+ process_exit: "\u{1F4A5}",
77
+ ci_failure: "\u274C",
78
+ ci_success: "\u2705",
79
+ deployment: "\u{1F680}"
80
+ };
81
+ const SRC_LABEL = {
82
+ browser: "BROWSER",
83
+ backend: "BACKEND",
84
+ ci: "CI",
85
+ deploy: "DEPLOY"
86
+ };
87
+ hr();
88
+ console.log(`\u2B21 Mergen Unified Timeline (last ${Math.round(seconds / 60)}m \xB7 ${rows.length} events)
89
+ `);
90
+ if (rc) {
91
+ const pct = Math.round((rc.confidence ?? 0) * 100);
92
+ console.log(`\u250C\u2500 Root Cause ${pct}% confidence`);
93
+ console.log(`\u2502 ${rc.hypothesis}`);
94
+ if (rc.fixHint) console.log(`\u2502 \u{1F4A1} ${rc.fixHint}`);
95
+ console.log("\u2514" + "\u2500".repeat(58));
96
+ console.log("");
97
+ }
98
+ if (rows.length === 0) {
99
+ log("No significant events in this window.");
100
+ log("Connect CI: POST /ci/github or POST /ci/generic");
101
+ log("Stream logs: mergen-server watch npm start");
102
+ } else {
103
+ for (const r of rows) {
104
+ const time = new Date(r.ts).toISOString().slice(11, 19);
105
+ const icon = KIND_ICON[r.kind] ?? "\u2B1C";
106
+ const src = r.source ? ` [${SRC_LABEL[r.source] ?? r.source}]` : "";
107
+ const sha = r.sha ? ` {${r.sha}}` : "";
108
+ const summary = r.summary.slice(0, 90);
109
+ console.log(`${time} ${icon}${src.padEnd(12)} ${summary}${sha}`);
110
+ }
111
+ }
112
+ hr();
113
+ }
114
+ async function watchCommand(args) {
115
+ let name = "";
116
+ let port = 3e3;
117
+ const rest = [];
118
+ for (let i = 1; i < args.length; i++) {
119
+ if (args[i] === "--name" && args[i + 1]) {
120
+ name = args[++i];
121
+ } else if (args[i] === "--port" && args[i + 1]) {
122
+ port = parseInt(args[++i], 10);
123
+ } else if (args[i] === "--") {
124
+ rest.push(...args.slice(i + 1));
125
+ break;
126
+ } else {
127
+ rest.push(...args.slice(i));
128
+ break;
129
+ }
130
+ }
131
+ if (rest.length === 0) {
132
+ error("Usage: mergen-server watch [--name <name>] [--port <port>] <command> [args...]");
133
+ console.log(" Example: mergen-server watch npm start");
134
+ console.log(" Example: mergen-server watch --name api python manage.py runserver");
135
+ process.exit(1);
136
+ }
137
+ const command = rest[0];
138
+ const cmdArgs = rest.slice(1);
139
+ const processName = name || command;
140
+ const mergenHost = process.env.MERGEN_HOST ?? "127.0.0.1";
141
+ let serverPort = port;
142
+ let serverFound = false;
143
+ for (let p = 3e3; p <= 3010; p++) {
144
+ try {
145
+ const r = await fetch(`http://${mergenHost}:${p}/health`, { signal: AbortSignal.timeout(500) });
146
+ if (r.ok) {
147
+ serverPort = p;
148
+ serverFound = true;
149
+ break;
150
+ }
151
+ } catch {
152
+ }
153
+ }
154
+ if (!serverFound) {
155
+ const serverPath = SERVER_ENTRY;
156
+ if (existsSync(serverPath)) {
157
+ process.stdout.write("\u2139 Starting Mergen server...");
158
+ const { spawn: spawnSrv } = await import("child_process");
159
+ const srv = spawnSrv("node", [serverPath], {
160
+ stdio: "ignore",
161
+ detached: true,
162
+ env: { ...process.env, NODE_ENV: "production" }
163
+ });
164
+ srv.unref();
165
+ for (let attempt = 0; attempt < 16 && !serverFound; attempt++) {
166
+ await sleep(500);
167
+ for (let p = 3e3; p <= 3010; p++) {
168
+ try {
169
+ const r = await fetch(`http://${mergenHost}:${p}/health`, { signal: AbortSignal.timeout(300) });
170
+ if (r.ok) {
171
+ serverPort = p;
172
+ serverFound = true;
173
+ break;
174
+ }
175
+ } catch {
176
+ }
177
+ }
178
+ }
179
+ console.log(serverFound ? ` \u2713 :${serverPort}` : " \u2717 (proceeding without server)");
180
+ } else {
181
+ log("Server not found \u2014 run: mergen-server setup", "\u26A0");
182
+ }
183
+ }
184
+ const ingestUrl = `http://${mergenHost}:${serverPort}/ingest`;
185
+ log(`Watching: ${[command, ...cmdArgs].join(" ")}`);
186
+ log(`Streaming to Mergen on ${mergenHost}:${serverPort} as process "${processName}"
187
+ `);
188
+ const { spawn: spawnChild } = await import("child_process");
189
+ const child = spawnChild(command, cmdArgs, {
190
+ stdio: ["inherit", "pipe", "pipe"],
191
+ env: process.env,
192
+ shell: process.platform === "win32"
193
+ });
194
+ let lineCount = 0;
195
+ let windowStart = Date.now();
196
+ let analysisTimer = null;
197
+ let firstInsightShown = false;
198
+ function scheduleInlineAnalysis() {
199
+ if (analysisTimer) clearTimeout(analysisTimer);
200
+ analysisTimer = setTimeout(async () => {
201
+ analysisTimer = null;
202
+ try {
203
+ const r = await fetch(`http://${mergenHost}:${serverPort}/health`, { signal: AbortSignal.timeout(3e3) });
204
+ if (!r.ok) return;
205
+ const h = await r.json();
206
+ const sigs = h.signals ?? [];
207
+ if (sigs.length === 0) return;
208
+ const top = sigs[0];
209
+ const pct = Math.round(top.confidence * 100);
210
+ const div = "\u2500".repeat(58);
211
+ process.stdout.write(`
212
+ ${div}
213
+ `);
214
+ process.stdout.write(`\u2B21 Mergen ${top.message} [${pct}%]
215
+ `);
216
+ process.stdout.write(` \u2192 ${top.action}
217
+ `);
218
+ if (!firstInsightShown) {
219
+ firstInsightShown = true;
220
+ process.stdout.write(` \u2726 First insight. Run reconstruct_context in your AI IDE for root cause + fix.
221
+ `);
222
+ process.stdout.write(` Or: mergen-server explain < your-error.log
223
+ `);
224
+ }
225
+ process.stdout.write(`${div}
226
+
227
+ `);
228
+ } catch {
229
+ }
230
+ }, 2e3);
231
+ }
232
+ const ERROR_LINE_RE = /\berror\b|exception|fatal|panic|segfault|ETIMEDOUT|ECONNREFUSED|ENOSPC|unhandled/i;
233
+ function postLine(data, isErr) {
234
+ const now = Date.now();
235
+ if (now - windowStart > 1e3) {
236
+ lineCount = 0;
237
+ windowStart = now;
238
+ }
239
+ if (lineCount >= 30) return;
240
+ lineCount++;
241
+ if (ERROR_LINE_RE.test(data)) scheduleInlineAnalysis();
242
+ const payload = JSON.stringify({
243
+ type: "terminal",
244
+ terminalName: processName,
245
+ data: (isErr ? "[stderr] " : "") + data.slice(0, 2e3),
246
+ timestamp: Date.now()
247
+ });
248
+ try {
249
+ const url = new URL(ingestUrl);
250
+ const req = require("http").request({
251
+ hostname: url.hostname,
252
+ port: parseInt(url.port || "3000"),
253
+ path: url.pathname,
254
+ method: "POST",
255
+ headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
256
+ });
257
+ req.on("error", () => {
258
+ });
259
+ req.write(payload);
260
+ req.end();
261
+ } catch {
262
+ }
263
+ }
264
+ function pipeProcStream(stream, isErr, out) {
265
+ let buf = "";
266
+ stream.on("data", (chunk) => {
267
+ const text = chunk.toString("utf8");
268
+ out.write(text);
269
+ buf += text;
270
+ const lines = buf.split("\n");
271
+ buf = lines.pop() ?? "";
272
+ for (const l of lines) if (l.trim()) postLine(l, isErr);
273
+ });
274
+ }
275
+ pipeProcStream(child.stdout, false, process.stdout);
276
+ pipeProcStream(child.stderr, true, process.stderr);
277
+ child.on("error", (err) => {
278
+ error(`Failed to start: ${err.message}`);
279
+ process.exit(1);
280
+ });
281
+ child.on("close", (code, signal) => {
282
+ const reason = code === 137 ? "oom" : signal ? "signal" : code !== 0 ? "crash" : "normal";
283
+ const payload = JSON.stringify({
284
+ type: "process_exit",
285
+ process: processName,
286
+ exitCode: code ?? -1,
287
+ reason,
288
+ signal: signal ?? void 0,
289
+ timestamp: Date.now()
290
+ });
291
+ try {
292
+ const url = new URL(ingestUrl);
293
+ const req = require("http").request({
294
+ hostname: url.hostname,
295
+ port: parseInt(url.port || "3000"),
296
+ path: url.pathname,
297
+ method: "POST",
298
+ headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }
299
+ });
300
+ req.on("error", () => {
301
+ });
302
+ req.write(payload);
303
+ req.end();
304
+ } catch {
305
+ }
306
+ process.exit(code ?? 0);
307
+ });
308
+ process.on("SIGINT", () => {
309
+ child.kill("SIGINT");
310
+ });
311
+ process.on("SIGTERM", () => {
312
+ child.kill("SIGTERM");
313
+ });
314
+ }
315
+ async function explainCommand(args) {
316
+ const filePath = args[0] && args[0] !== "-" ? args[0] : null;
317
+ let text = "";
318
+ if (filePath) {
319
+ if (!existsSync(filePath)) {
320
+ error(`File not found: ${filePath}`);
321
+ process.exit(1);
322
+ }
323
+ text = readFileSync(filePath, "utf8");
324
+ } else {
325
+ if (process.stdin.isTTY) {
326
+ error("No input. Usage:");
327
+ console.log(" cat error.log | mergen-server explain");
328
+ console.log(" mergen-server explain error.log");
329
+ process.exit(1);
330
+ }
331
+ const chunks = [];
332
+ process.stdin.resume();
333
+ for await (const chunk of process.stdin) chunks.push(chunk);
334
+ text = Buffer.concat(chunks).toString("utf8");
335
+ }
336
+ const rawLines = text.split("\n").filter((l) => l.trim());
337
+ if (rawLines.length === 0) {
338
+ error("Empty input.");
339
+ process.exit(1);
340
+ }
341
+ const now = Date.now();
342
+ const logs = rawLines.map((line, i) => ({
343
+ type: "console",
344
+ level: /\berror\b|exception|fatal|panic|traceback|segfault|ETIMEDOUT|ECONNREFUSED|ENOSPC/i.test(line) ? "error" : /\bwarn/i.test(line) ? "warn" : "log",
345
+ args: [line],
346
+ url: filePath ?? "stdin",
347
+ timestamp: now - (rawLines.length - i) * 500
348
+ }));
349
+ const errorCount = logs.filter((l) => l.level === "error").length;
350
+ if (errorCount === 0) {
351
+ log(`${rawLines.length} lines read \u2014 no error-level patterns detected.`);
352
+ log("Lines matching: error, exception, fatal, panic, ETIMEDOUT, ECONNREFUSED are classified as errors.");
353
+ process.exit(0);
354
+ }
355
+ process.stdout.write(`Analyzing ${rawLines.length} lines (${errorCount} error(s))...`);
356
+ try {
357
+ const { buildCausalChain } = await import("../intelligence/causal.js");
358
+ const causal = await buildCausalChain(logs, [], [], void 0, [], [], [], []);
359
+ console.log(" \u2713\n");
360
+ if (causal.hypotheses.length === 0) {
361
+ log("No hypothesis matched \u2014 patterns did not align with known failure modes.");
362
+ log("For richer analysis with network context: mergen-server watch <your-command>");
363
+ process.exit(0);
364
+ }
365
+ const top = causal.hypotheses[0];
366
+ const pct = Math.round((top.confidenceScore ?? 0) * 100);
367
+ hr();
368
+ console.log(`Root cause ${top.confidence} [${pct}%]
369
+ `);
370
+ console.log(` ${top.summary}
371
+ `);
372
+ if (top.causalPath?.length) {
373
+ console.log("Causal chain:");
374
+ top.causalPath.forEach((step, i) => console.log(` ${i + 1}. ${step}`));
375
+ console.log("");
376
+ }
377
+ if (top.fixHint) {
378
+ console.log(`Fix: ${top.fixHint.split(".")[0]}.
379
+ `);
380
+ }
381
+ hr();
382
+ if (causal.hypotheses.length > 1) {
383
+ log(`${causal.hypotheses.length - 1} alternative hypothesis(es) considered.`, "\u2139");
384
+ }
385
+ log("For real-time analysis with network context: mergen-server watch <your-command>", "\u2139");
386
+ } catch (err) {
387
+ console.log(" \u2717");
388
+ error(err instanceof Error ? err.message : String(err));
389
+ process.exit(1);
390
+ }
391
+ }
392
+ async function statusCommand() {
393
+ let port = 0;
394
+ let health = null;
395
+ for (let p = 3e3; p <= 3010; p++) {
396
+ try {
397
+ const r = await fetch(`http://127.0.0.1:${p}/health`, { signal: AbortSignal.timeout(800) });
398
+ if (r.ok) {
399
+ health = await r.json();
400
+ port = p;
401
+ break;
402
+ }
403
+ } catch {
404
+ }
405
+ }
406
+ console.log("\u2B21 Mergen Status\n");
407
+ hr();
408
+ if (!health) {
409
+ error("Server not running (tried :3000\u2013:3010)");
410
+ console.log("\n Start it: mergen-server start");
411
+ console.log(" Diagnose: mergen-server doctor");
412
+ process.exit(1);
413
+ }
414
+ const buffered = health.buffered ?? 0;
415
+ const maxBuffer = health.maxBuffer ?? 2e3;
416
+ const errors = health.errors ?? 0;
417
+ const warns = health.warnings ?? 0;
418
+ const netErrors = health.networkErrors ?? 0;
419
+ const lastAt = health.lastEventAt ?? null;
420
+ const mcpAt = health.lastMcpCallAt ?? null;
421
+ const version = health.version ?? "?";
422
+ const signals = health.signals ?? [];
423
+ const bufPct = maxBuffer > 0 ? Math.round(buffered / maxBuffer * 100) : 0;
424
+ const lastStr = lastAt ? `${Math.round((Date.now() - lastAt) / 1e3)}s ago` : "none yet";
425
+ const mcpStr = mcpAt ? `${Math.round((Date.now() - mcpAt) / 1e3)}s ago` : "not yet";
426
+ const errLabel = [
427
+ errors > 0 ? `${errors} console` : "",
428
+ netErrors > 0 ? `${netErrors} network` : ""
429
+ ].filter(Boolean).join(", ") || "none";
430
+ console.log(` Server http://127.0.0.1:${port} v${version}`);
431
+ console.log(` Buffer ${buffered} / ${maxBuffer} (${bufPct}% full)`);
432
+ console.log(` Errors ${errLabel}`);
433
+ if (warns > 0) console.log(` Warnings ${warns}`);
434
+ console.log(` Last event ${lastStr}`);
435
+ console.log(` MCP ${mcpStr}`);
436
+ if (signals.length > 0) {
437
+ console.log(`
438
+ Signals (${signals.length} active):`);
439
+ for (const s of signals.slice(0, 3)) {
440
+ console.log(` [${Math.round(s.confidence * 100)}%] ${s.message}`);
441
+ }
442
+ }
443
+ hr();
444
+ if (!lastAt) {
445
+ console.log(" \u26A0 No events yet \u2014 send OpenTelemetry events to the gateway to start");
446
+ console.log(" Or for Node apps: node --require mergen-server/sdk/node your-app.js");
447
+ } else if (errors === 0 && netErrors === 0) {
448
+ success(`Healthy \u2014 server running, events flowing`);
449
+ } else {
450
+ console.log(` \u26A0 ${errors + netErrors} error(s) in buffer`);
451
+ console.log(' Ask your AI: "quick_check"');
452
+ }
453
+ }
454
+ async function resolvedCommand(args) {
455
+ const summaryIdx = args.indexOf("--summary");
456
+ const summary = summaryIdx !== -1 ? args[summaryIdx + 1] : void 0;
457
+ const prUrl = args.find((a) => a.startsWith("--pr="))?.slice(5);
458
+ if (!summary && !prUrl) {
459
+ error('Usage: mergen-server resolved --summary "rolled back enable-bulk-capture flag"');
460
+ error(' mergen-server resolved --pr=https://github.com/... --summary "hotfix"');
461
+ process.exit(1);
462
+ }
463
+ let port = 3e3;
464
+ for (let p = 3e3; p <= 3010; p++) {
465
+ try {
466
+ const r = await fetch(`http://127.0.0.1:${p}/health`, { signal: AbortSignal.timeout(600) });
467
+ if (r.ok) {
468
+ port = p;
469
+ break;
470
+ }
471
+ } catch {
472
+ }
473
+ }
474
+ const body = { resolvedAt: String(Date.now()) };
475
+ if (summary) body.fixSummary = summary;
476
+ if (prUrl) body.fixPrUrl = prUrl;
477
+ try {
478
+ const r = await fetch(`http://127.0.0.1:${port}/incidents/resolve-active`, {
479
+ method: "POST",
480
+ headers: { "Content-Type": "application/json" },
481
+ body: JSON.stringify(body),
482
+ signal: AbortSignal.timeout(3e3)
483
+ });
484
+ if (!r.ok) {
485
+ error(`Server returned ${r.status}`);
486
+ process.exit(1);
487
+ }
488
+ const data = await r.json();
489
+ success("Incident marked resolved. MTTR recorded.");
490
+ if (summary) log(`Summary: ${summary}`);
491
+ if (!prUrl && data.attributionSha && data.attributionConfidence !== void 0) {
492
+ const pct = Math.round(data.attributionConfidence * 100);
493
+ const sha8 = data.attributionSha.slice(0, 8);
494
+ console.log("");
495
+ console.log(`\u2B21 Mergen attributed this incident to deploy \`${sha8}\` (${pct}% confidence).`);
496
+ const answer = await ask(" Was that correct? [y/n/skip]: ");
497
+ const lower = answer.toLowerCase();
498
+ if (lower === "y" || lower === "yes" || lower === "n" || lower === "no") {
499
+ const attributionCorrect = lower === "y" || lower === "yes";
500
+ try {
501
+ await fetch(`http://127.0.0.1:${port}/incidents/resolve-active/attribution-feedback`, {
502
+ method: "POST",
503
+ headers: { "Content-Type": "application/json" },
504
+ body: JSON.stringify({ id: data.id, attributionCorrect }),
505
+ signal: AbortSignal.timeout(3e3)
506
+ });
507
+ success(`Attribution feedback recorded: ${attributionCorrect ? "correct" : "incorrect"}`);
508
+ if (!attributionCorrect) {
509
+ log("Feedback stored. Attribution weights will improve as more incidents are validated.");
510
+ }
511
+ } catch {
512
+ }
513
+ } else {
514
+ log("Attribution feedback skipped.");
515
+ }
516
+ }
517
+ } catch {
518
+ error("Server not running or unreachable. Start with: mergen-server start");
519
+ process.exit(1);
520
+ }
521
+ }
522
+ async function replayCommand(args) {
523
+ const dir = args[0];
524
+ if (!dir) {
525
+ error("Usage: mergen-server replay <directory>");
526
+ console.log("\nRun historical incidents through the causal pipeline and score accuracy.");
527
+ console.log("\nIncident file format (any .json file in the directory):");
528
+ console.log(" {");
529
+ console.log(' "name": "db-pool-exhaustion-2024-01-15",');
530
+ console.log(' "expected_tag": "infra_db_connection_pool",');
531
+ console.log(' "log_lines": ["ERROR: Too many connections to postgres:5432"]');
532
+ console.log(" }");
533
+ process.exit(1);
534
+ }
535
+ const { readdirSync, readFileSync: readFileSync2, existsSync: existsSync2, statSync } = await import("fs");
536
+ const { basename } = await import("path");
537
+ const dirPath = resolve(dir);
538
+ if (!existsSync2(dirPath) || !statSync(dirPath).isDirectory()) {
539
+ error(`Not a directory: ${dirPath}`);
540
+ process.exit(1);
541
+ }
542
+ const files = readdirSync(dirPath).filter((f) => f.endsWith(".json")).sort();
543
+ if (files.length === 0) {
544
+ error(`No .json incident files found in ${dirPath}`);
545
+ console.log("\nCreate an incident file like this:");
546
+ console.log(' { "name": "my-incident", "expected_tag": "infra_db_connection_pool",');
547
+ console.log(' "log_lines": ["ERROR: Too many connections"] }');
548
+ process.exit(1);
549
+ }
550
+ const { buildCausalChain } = await import("../intelligence/causal.js");
551
+ console.log(`
552
+ Mergen Production Replay \u2014 ${files.length} incident${files.length !== 1 ? "s" : ""}
553
+ `);
554
+ const rows = [];
555
+ let passed = 0, failed = 0, unscored = 0;
556
+ for (const file of files) {
557
+ const filePath = join(dirPath, file);
558
+ let incident;
559
+ try {
560
+ incident = JSON.parse(readFileSync2(filePath, "utf8"));
561
+ } catch {
562
+ rows.push({ name: file, status: "ERR", actual_tag: null, expected_tag: null, confidence: null, fix_hint: null, err_msg: "invalid JSON" });
563
+ continue;
564
+ }
565
+ const name = typeof incident.name === "string" ? incident.name : basename(file, ".json");
566
+ const expectedTag = typeof incident.expected_tag === "string" ? incident.expected_tag : null;
567
+ const firedAt = typeof incident.firedAt === "number" ? incident.firedAt : Date.now();
568
+ let logs = Array.isArray(incident.logs) ? incident.logs : [];
569
+ if (Array.isArray(incident.log_lines)) {
570
+ const lineEvents = incident.log_lines.map((line, i) => ({
571
+ level: /error|fatal|critical/i.test(line) ? "error" : /warn/i.test(line) ? "warn" : "log",
572
+ args: [line],
573
+ timestamp: firedAt - (incident.log_lines.length - i) * 1e3,
574
+ url: "replay"
575
+ }));
576
+ logs = [...logs, ...lineEvents];
577
+ }
578
+ const network = Array.isArray(incident.network) ? incident.network : [];
579
+ const infraEvents = Array.isArray(incident.infra_events) ? incident.infra_events : [];
580
+ try {
581
+ const causal = await buildCausalChain(logs, network, [], firedAt, [], [], [], [], infraEvents);
582
+ const top = causal.hypotheses[0] ?? null;
583
+ const actual = top?.tag ?? null;
584
+ const conf = top?.confidenceScore ?? null;
585
+ const hint = top?.fixHint ?? null;
586
+ if (!expectedTag) {
587
+ unscored++;
588
+ rows.push({ name, status: "INFO", actual_tag: actual, expected_tag: null, confidence: conf, fix_hint: hint });
589
+ } else if (actual === expectedTag) {
590
+ passed++;
591
+ rows.push({ name, status: "PASS", actual_tag: actual, expected_tag: expectedTag, confidence: conf, fix_hint: hint });
592
+ } else {
593
+ failed++;
594
+ rows.push({ name, status: "FAIL", actual_tag: actual, expected_tag: expectedTag, confidence: conf, fix_hint: hint });
595
+ }
596
+ } catch (e) {
597
+ rows.push({ name, status: "ERR", actual_tag: null, expected_tag: expectedTag, confidence: null, fix_hint: null, err_msg: e instanceof Error ? e.message : String(e) });
598
+ }
599
+ }
600
+ const nameW = Math.min(Math.max(...rows.map((r) => r.name.length), 20), 40);
601
+ for (const r of rows) {
602
+ const icon = r.status === "PASS" ? "\u2713" : r.status === "FAIL" ? "\u2717" : r.status === "ERR" ? "!" : "\u25CB";
603
+ const label = r.name.slice(0, nameW).padEnd(nameW);
604
+ const pct = r.confidence !== null ? ` ${Math.round(r.confidence * 100)}%` : "";
605
+ const pad = " ".repeat(nameW + 10);
606
+ if (r.status === "PASS") {
607
+ console.log(` ${icon} PASS ${label} ${r.actual_tag}${pct}`);
608
+ } else if (r.status === "FAIL") {
609
+ console.log(` ${icon} FAIL ${label} expected: ${r.expected_tag}`);
610
+ console.log(` ${pad} got: ${r.actual_tag ?? "(no diagnosis)"}${pct}`);
611
+ if (r.fix_hint) console.log(` ${pad} hint: ${r.fix_hint.slice(0, 80)}`);
612
+ } else if (r.status === "INFO") {
613
+ console.log(` ${icon} INFO ${label} ${r.actual_tag ?? "(no diagnosis)"}${pct}`);
614
+ } else {
615
+ console.log(` ${icon} ERR ${label} ${r.err_msg}`);
616
+ }
617
+ }
618
+ const scored = passed + failed;
619
+ const accuracyPct = scored > 0 ? Math.round(passed / scored * 100) : null;
620
+ const hr2 = "\u2500".repeat(60);
621
+ console.log(`
622
+ ${hr2}`);
623
+ if (scored > 0) {
624
+ const marker = accuracyPct !== null && accuracyPct >= 90 ? "\u2713" : accuracyPct !== null && accuracyPct >= 70 ? "~" : "\u2717";
625
+ const unscoredNote = unscored > 0 ? ` | ${unscored} unscored` : "";
626
+ console.log(`${marker} Accuracy: ${passed}/${scored} scored (${accuracyPct}%)${unscoredNote}`);
627
+ } else {
628
+ console.log(`\u25CB ${unscored} incident${unscored !== 1 ? "s" : ""} \u2014 add "expected_tag" fields to score accuracy`);
629
+ }
630
+ if (accuracyPct !== null && accuracyPct < 90 && failed > 0) {
631
+ console.log("\n To improve: share failing incidents via mergen-server feedback <id>");
632
+ }
633
+ console.log("");
634
+ process.exit(failed > 0 ? 1 : 0);
635
+ }
636
+ export {
637
+ explainCommand,
638
+ postmortemCommand,
639
+ replayCommand,
640
+ resolvedCommand,
641
+ statusCommand,
642
+ timelineCommand,
643
+ watchCommand
644
+ };