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,230 @@
1
+ import { randomBytes } from "crypto";
2
+ import { store } from "./buffer.js";
3
+ const MAX_SNAPSHOTS = 50;
4
+ class Layer3Store {
5
+ breakpoints = /* @__PURE__ */ new Map();
6
+ mocks = /* @__PURE__ */ new Map();
7
+ injectedLogs = /* @__PURE__ */ new Map();
8
+ pendingCommands = /* @__PURE__ */ new Map();
9
+ snapshots = [];
10
+ /** Set a conditional breakpoint */
11
+ setBreakpoint(condition, eventType, pattern) {
12
+ const id = randomBytes(8).toString("hex");
13
+ this.breakpoints.set(id, {
14
+ id,
15
+ condition,
16
+ eventType,
17
+ pattern,
18
+ createdAt: Date.now(),
19
+ hitCount: 0
20
+ });
21
+ return id;
22
+ }
23
+ /** Remove a breakpoint */
24
+ removeBreakpoint(id) {
25
+ return this.breakpoints.delete(id);
26
+ }
27
+ /** List all active breakpoints */
28
+ listBreakpoints() {
29
+ return Array.from(this.breakpoints.values());
30
+ }
31
+ /** Check if event matches any breakpoint. Captures a snapshot on hit. */
32
+ checkBreakpoint(event) {
33
+ for (const bp of this.breakpoints.values()) {
34
+ if (this.matchesBreakpoint(event, bp)) {
35
+ bp.hitCount++;
36
+ this._captureSnapshot(bp, event);
37
+ return bp;
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ _captureSnapshot(bp, triggeringEvent) {
43
+ const recentLogs = store.getLogs(20);
44
+ const recentNetwork = store.getNetwork(10);
45
+ const contextSnaps = store.getContext(1);
46
+ const summary = triggeringEvent.type === "console" ? (triggeringEvent.args ?? []).map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ").slice(0, 200) : triggeringEvent.type === "network" ? `${triggeringEvent.method} ${triggeringEvent.url} \u2192 ${triggeringEvent.status}` : triggeringEvent.type;
47
+ const snap = {
48
+ id: randomBytes(8).toString("hex"),
49
+ capturedAt: Date.now(),
50
+ trigger: { breakpointId: bp.id, eventType: triggeringEvent.type, summary },
51
+ recentLogs,
52
+ recentNetwork,
53
+ contextSnapshot: contextSnaps[0] ?? null,
54
+ stack: triggeringEvent.stack
55
+ };
56
+ this.snapshots.push(snap);
57
+ if (this.snapshots.length > MAX_SNAPSHOTS) this.snapshots.shift();
58
+ }
59
+ /** Return all captured snapshots, newest first. */
60
+ listSnapshots() {
61
+ return [...this.snapshots].reverse();
62
+ }
63
+ /** Get a single snapshot by id. */
64
+ getSnapshot(id) {
65
+ return this.snapshots.find((s) => s.id === id);
66
+ }
67
+ /** Clear all snapshots. */
68
+ clearSnapshots() {
69
+ this.snapshots = [];
70
+ }
71
+ /** Register a mock response */
72
+ setMock(url, method, status, body, headers) {
73
+ const id = randomBytes(8).toString("hex");
74
+ this.mocks.set(id, {
75
+ id,
76
+ url,
77
+ method: method.toUpperCase(),
78
+ status,
79
+ body,
80
+ headers,
81
+ createdAt: Date.now(),
82
+ hitCount: 0
83
+ });
84
+ this.queueCommand("SET_MOCK", { id, url, method, status, body, headers });
85
+ return id;
86
+ }
87
+ /** Remove a mock */
88
+ removeMock(id) {
89
+ const mock = this.mocks.get(id);
90
+ if (mock) {
91
+ this.queueCommand("REMOVE_MOCK", { id });
92
+ return this.mocks.delete(id);
93
+ }
94
+ return false;
95
+ }
96
+ /** List all active mocks */
97
+ listMocks() {
98
+ return Array.from(this.mocks.values());
99
+ }
100
+ /** Check if request should be mocked */
101
+ getMock(url, method) {
102
+ for (const mock of this.mocks.values()) {
103
+ if (this.urlMatches(url, mock.url) && method.toUpperCase() === mock.method) {
104
+ mock.hitCount++;
105
+ return mock;
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+ /** Inject a temporary log */
111
+ injectLog(selector, event, expression) {
112
+ const id = randomBytes(8).toString("hex");
113
+ this.injectedLogs.set(id, {
114
+ id,
115
+ selector,
116
+ event,
117
+ expression,
118
+ createdAt: Date.now(),
119
+ captured: []
120
+ });
121
+ this.queueCommand("INJECT_LOG", { id, selector, event, expression });
122
+ return id;
123
+ }
124
+ /** Remove an injected log */
125
+ removeInjectedLog(id) {
126
+ const log = this.injectedLogs.get(id);
127
+ if (log) {
128
+ this.queueCommand("REMOVE_LOG", { id });
129
+ return this.injectedLogs.delete(id);
130
+ }
131
+ return false;
132
+ }
133
+ /** List all injected logs */
134
+ listInjectedLogs() {
135
+ return Array.from(this.injectedLogs.values());
136
+ }
137
+ /** Store captured data from injected log */
138
+ captureLogData(id, data) {
139
+ const log = this.injectedLogs.get(id);
140
+ if (log) {
141
+ log.captured.push({ timestamp: Date.now(), data });
142
+ if (log.captured.length >= 1) {
143
+ this.removeInjectedLog(id);
144
+ }
145
+ }
146
+ }
147
+ /** Queue a command to be sent to extension */
148
+ queueCommand(type, payload) {
149
+ const id = randomBytes(8).toString("hex");
150
+ this.pendingCommands.set(id, { type, payload, queuedAt: Date.now() });
151
+ }
152
+ /** Get and clear pending commands for extension polling */
153
+ getPendingCommands() {
154
+ const commands = Array.from(this.pendingCommands.values());
155
+ this.pendingCommands.clear();
156
+ return commands;
157
+ }
158
+ matchesBreakpoint(event, bp) {
159
+ if (event.type !== bp.eventType) return false;
160
+ try {
161
+ const regex = new RegExp(bp.pattern, "i");
162
+ if (event.type === "console") {
163
+ const message = event.args?.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") || "";
164
+ if (!regex.test(message)) return false;
165
+ }
166
+ if (event.type === "network") {
167
+ if (!regex.test(event.url)) return false;
168
+ }
169
+ if (bp.condition) {
170
+ return this.evaluateCondition(bp.condition, event);
171
+ }
172
+ return true;
173
+ } catch {
174
+ return false;
175
+ }
176
+ }
177
+ evaluateCondition(condition, event) {
178
+ try {
179
+ const match = condition.match(/(\w+)\s*(===|!==|>|<|>=|<=)\s*(.+)/);
180
+ if (!match) return true;
181
+ const [, key, op, value] = match;
182
+ const actualValue = event[key];
183
+ let expectedValue = value.trim();
184
+ if (expectedValue.startsWith("'") || expectedValue.startsWith('"')) {
185
+ expectedValue = expectedValue.slice(1, -1);
186
+ } else if (!isNaN(Number(expectedValue))) {
187
+ expectedValue = Number(expectedValue);
188
+ }
189
+ switch (op) {
190
+ case "===":
191
+ return actualValue === expectedValue;
192
+ case "!==":
193
+ return actualValue !== expectedValue;
194
+ case ">":
195
+ return actualValue > expectedValue;
196
+ case "<":
197
+ return actualValue < expectedValue;
198
+ case ">=":
199
+ return actualValue >= expectedValue;
200
+ case "<=":
201
+ return actualValue <= expectedValue;
202
+ default:
203
+ return true;
204
+ }
205
+ } catch {
206
+ return true;
207
+ }
208
+ }
209
+ urlMatches(url, pattern) {
210
+ const regex = new RegExp(
211
+ "^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
212
+ "i"
213
+ );
214
+ return regex.test(url);
215
+ }
216
+ /** Clear old commands to prevent memory leak */
217
+ pruneOldCommands() {
218
+ const MAX_AGE_MS = 6e4;
219
+ const now = Date.now();
220
+ for (const [id, cmd] of this.pendingCommands) {
221
+ if (now - cmd.queuedAt > MAX_AGE_MS) {
222
+ this.pendingCommands.delete(id);
223
+ }
224
+ }
225
+ }
226
+ }
227
+ const layer3Store = new Layer3Store();
228
+ export {
229
+ layer3Store
230
+ };
@@ -0,0 +1,147 @@
1
+ import { createHash } from "crypto";
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ const ERROR_INDEX_PATH = join(homedir(), ".mergen", "error-index.json");
6
+ const MAX_ERROR_ENTRIES = 1e3;
7
+ class Layer4Store {
8
+ errorIndex = /* @__PURE__ */ new Map();
9
+ constructor() {
10
+ this.load();
11
+ }
12
+ /** Generate fingerprint from error message and stack */
13
+ fingerprint(message, stack) {
14
+ const stackLines = stack?.split("\n").slice(0, 3).join("\n") || "";
15
+ const input = `${message}
16
+ ${stackLines}`;
17
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
18
+ }
19
+ /** Record an error occurrence */
20
+ recordError(message, stack) {
21
+ const fp = this.fingerprint(message, stack);
22
+ const existing = this.errorIndex.get(fp);
23
+ if (existing) {
24
+ existing.count++;
25
+ existing.lastSeen = Date.now();
26
+ } else {
27
+ this.errorIndex.set(fp, {
28
+ fingerprint: fp,
29
+ message: message.slice(0, 500),
30
+ stack: stack?.slice(0, 2e3),
31
+ count: 1,
32
+ firstSeen: Date.now(),
33
+ lastSeen: Date.now(),
34
+ fixes: []
35
+ });
36
+ }
37
+ this.persist();
38
+ return fp;
39
+ }
40
+ /** Link a fix to an error */
41
+ linkFix(fingerprint, commitSha, description, verdict = "correct") {
42
+ const entry = this.errorIndex.get(fingerprint);
43
+ if (!entry) return false;
44
+ const existing = entry.fixes.find((f) => f.commitSha === commitSha);
45
+ if (existing) {
46
+ existing.verdict = verdict;
47
+ existing.confidence = this.calculateConfidence(verdict);
48
+ } else {
49
+ entry.fixes.push({
50
+ commitSha,
51
+ description: description.slice(0, 500),
52
+ linkedAt: Date.now(),
53
+ verdict,
54
+ confidence: this.calculateConfidence(verdict)
55
+ });
56
+ }
57
+ this.persist();
58
+ return true;
59
+ }
60
+ /** Get error history for a fingerprint */
61
+ getError(fingerprint) {
62
+ return this.errorIndex.get(fingerprint);
63
+ }
64
+ /** Search for similar errors */
65
+ searchErrors(query, limit = 10) {
66
+ const results = [];
67
+ const queryLower = query.toLowerCase();
68
+ for (const entry of this.errorIndex.values()) {
69
+ const messageLower = entry.message.toLowerCase();
70
+ if (messageLower.includes(queryLower)) {
71
+ const score = messageLower === queryLower ? 1 : 0.5;
72
+ results.push({ entry, score });
73
+ }
74
+ }
75
+ return results.sort((a, b) => b.score - a.score || b.entry.count - a.entry.count).slice(0, limit).map((r) => r.entry);
76
+ }
77
+ /** Get top fix for an error */
78
+ getTopFix(fingerprint) {
79
+ const entry = this.errorIndex.get(fingerprint);
80
+ if (!entry || entry.fixes.length === 0) return void 0;
81
+ return entry.fixes.sort((a, b) => b.confidence - a.confidence)[0];
82
+ }
83
+ /** List all errors, sorted by most recent */
84
+ listErrors(limit = 50) {
85
+ return Array.from(this.errorIndex.values()).sort((a, b) => b.lastSeen - a.lastSeen).slice(0, limit);
86
+ }
87
+ /** Load from disk */
88
+ load() {
89
+ try {
90
+ if (!existsSync(ERROR_INDEX_PATH)) return;
91
+ const data = JSON.parse(readFileSync(ERROR_INDEX_PATH, "utf-8"));
92
+ if (Array.isArray(data)) {
93
+ for (const entry of data) {
94
+ this.errorIndex.set(entry.fingerprint, entry);
95
+ }
96
+ }
97
+ } catch (err) {
98
+ console.error("[layer4] Failed to load error index:", err);
99
+ }
100
+ }
101
+ /** Persist to disk */
102
+ persist() {
103
+ try {
104
+ const dir = join(homedir(), ".mergen");
105
+ if (!existsSync(dir)) {
106
+ mkdirSync(dir, { recursive: true });
107
+ }
108
+ if (this.errorIndex.size > MAX_ERROR_ENTRIES) {
109
+ const sorted = Array.from(this.errorIndex.values()).sort((a, b) => b.lastSeen - a.lastSeen).slice(0, MAX_ERROR_ENTRIES);
110
+ this.errorIndex.clear();
111
+ for (const entry of sorted) {
112
+ this.errorIndex.set(entry.fingerprint, entry);
113
+ }
114
+ }
115
+ const data = Array.from(this.errorIndex.values());
116
+ writeFileSync(ERROR_INDEX_PATH, JSON.stringify(data, null, 2), "utf-8");
117
+ } catch (err) {
118
+ console.error("[layer4] Failed to persist error index:", err);
119
+ }
120
+ }
121
+ calculateConfidence(verdict) {
122
+ switch (verdict) {
123
+ case "correct":
124
+ return 1;
125
+ case "partial":
126
+ return 0.6;
127
+ case "wrong":
128
+ return 0.2;
129
+ default:
130
+ return 0.5;
131
+ }
132
+ }
133
+ /** Get statistics */
134
+ getStats() {
135
+ const totalErrors = this.errorIndex.size;
136
+ const totalFixes = Array.from(this.errorIndex.values()).reduce(
137
+ (sum, e) => sum + e.fixes.length,
138
+ 0
139
+ );
140
+ const avgFixesPerError = totalErrors > 0 ? totalFixes / totalErrors : 0;
141
+ return { totalErrors, totalFixes, avgFixesPerError };
142
+ }
143
+ }
144
+ const layer4Store = new Layer4Store();
145
+ export {
146
+ layer4Store
147
+ };
@@ -0,0 +1,13 @@
1
+ import pino from "pino";
2
+ const logger = pino(
3
+ {
4
+ name: "mergen",
5
+ level: process.env.LOG_LEVEL ?? "info"
6
+ },
7
+ pino.destination(2)
8
+ // stderr
9
+ );
10
+ var logger_default = logger;
11
+ export {
12
+ logger_default as default
13
+ };
@@ -0,0 +1,161 @@
1
+ import { trace, context as otelCtx, TraceFlags } from "@opentelemetry/api";
2
+ import logger from "./logger.js";
3
+ const _m = {
4
+ errorCount: 0,
5
+ networkErrorCount: 0,
6
+ requestCount: 0,
7
+ requestDurationTotalMs: 0,
8
+ requestDurationSamples: 0
9
+ };
10
+ function _recordCounters(event) {
11
+ if (event.type === "console" && event.level === "error") {
12
+ _m.errorCount++;
13
+ } else if (event.type === "network") {
14
+ _m.requestCount++;
15
+ _m.requestDurationTotalMs += event.duration;
16
+ _m.requestDurationSamples++;
17
+ if (event.status >= 400 || event.status === 0 || event.error) _m.networkErrorCount++;
18
+ }
19
+ }
20
+ function getPrometheusMetrics() {
21
+ const avg = _m.requestDurationSamples > 0 ? Math.round(_m.requestDurationTotalMs / _m.requestDurationSamples) : 0;
22
+ return [
23
+ "# HELP mergen_browser_error_total Total browser console errors since server start",
24
+ "# TYPE mergen_browser_error_total counter",
25
+ `mergen_browser_error_total ${_m.errorCount}`,
26
+ "# HELP mergen_browser_network_error_total Total failed network requests (4xx/5xx/network error)",
27
+ "# TYPE mergen_browser_network_error_total counter",
28
+ `mergen_browser_network_error_total ${_m.networkErrorCount}`,
29
+ "# HELP mergen_browser_request_total Total network requests captured",
30
+ "# TYPE mergen_browser_request_total counter",
31
+ `mergen_browser_request_total ${_m.requestCount}`,
32
+ "# HELP mergen_browser_request_duration_avg_ms Rolling average request duration in milliseconds",
33
+ "# TYPE mergen_browser_request_duration_avg_ms gauge",
34
+ `mergen_browser_request_duration_avg_ms ${avg}`
35
+ ].join("\n") + "\n";
36
+ }
37
+ function getMetricCounters() {
38
+ return _m;
39
+ }
40
+ let _config = null;
41
+ let _loggerProvider = null;
42
+ let _otelLogger = null;
43
+ function getOtelConfig() {
44
+ return _config;
45
+ }
46
+ function isOtelEnabled() {
47
+ return _config?.enabled === true;
48
+ }
49
+ async function createProvider(cfg) {
50
+ if (_loggerProvider) {
51
+ await _loggerProvider.shutdown().catch(() => {
52
+ });
53
+ _loggerProvider = null;
54
+ _otelLogger = null;
55
+ }
56
+ const { OTLPLogExporter } = await import("@opentelemetry/exporter-logs-otlp-http");
57
+ const { LoggerProvider, SimpleLogRecordProcessor } = await import("@opentelemetry/sdk-logs");
58
+ const resourcesModule = await import("@opentelemetry/resources");
59
+ const Resource = resourcesModule.Resource;
60
+ const exporter = new OTLPLogExporter({
61
+ url: cfg.endpoint,
62
+ headers: cfg.headers ?? {}
63
+ });
64
+ const resource = Resource ? new Resource({
65
+ "service.name": cfg.serviceName ?? "mergen",
66
+ "service.version": "1.0.0",
67
+ "deployment.environment": "development"
68
+ }) : void 0;
69
+ _loggerProvider = new LoggerProvider({ resource });
70
+ _loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(exporter));
71
+ const { logs } = await import("@opentelemetry/api-logs");
72
+ logs.setGlobalLoggerProvider(_loggerProvider);
73
+ _otelLogger = _loggerProvider.getLogger("mergen.browser");
74
+ logger.info({ endpoint: cfg.endpoint, service: cfg.serviceName ?? "mergen" }, "OTel log exporter configured");
75
+ }
76
+ async function configureOtel(cfg) {
77
+ _config = cfg;
78
+ if (cfg.enabled) {
79
+ await createProvider(cfg);
80
+ }
81
+ }
82
+ async function disableOtel() {
83
+ _config = null;
84
+ if (_loggerProvider) {
85
+ await _loggerProvider.shutdown().catch(() => {
86
+ });
87
+ _loggerProvider = null;
88
+ _otelLogger = null;
89
+ }
90
+ logger.info("OTel log exporter disabled");
91
+ }
92
+ function exportToOtel(event) {
93
+ _recordCounters(event);
94
+ if (!_otelLogger || !_config?.enabled) return;
95
+ try {
96
+ const severityMap = {
97
+ error: 16,
98
+ // SeverityNumber.ERROR
99
+ warn: 13,
100
+ // SeverityNumber.WARN
101
+ log: 9,
102
+ // SeverityNumber.INFO
103
+ info: 9,
104
+ // SeverityNumber.INFO
105
+ debug: 5
106
+ // SeverityNumber.DEBUG
107
+ };
108
+ const isNetwork = event.type === "network";
109
+ const isConsole = event.type === "console";
110
+ const eventUrl = "url" in event ? event.url : void 0;
111
+ const severityText = isConsole ? event.level ?? "log" : "info";
112
+ const severityNumber = isConsole ? severityMap[event.level ?? "log"] ?? 9 : 9;
113
+ const body = isConsole ? event.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") : isNetwork ? `${event.method} ${event.url} \u2192 ${event.status}` : event.type === "context" ? `context: ${event.url}` : event.type === "diagnostic" ? `${event.source} ${event.file}:${event.line}:${event.column} ${event.message}` : event.type === "terminal" ? `${event.terminalName}: ${event.data}` : event.type === "test_result" ? `${event.runner} ${event.status} ${event.name}` : event.type === "process_exit" ? `${event.process} exited (${event.reason})` : `${event.type} event`;
114
+ let logContext = void 0;
115
+ const networkTraceId = isNetwork ? event.traceId : void 0;
116
+ if (networkTraceId && networkTraceId.length === 32) {
117
+ try {
118
+ const spanContext = {
119
+ traceId: networkTraceId,
120
+ spanId: networkTraceId.slice(16),
121
+ // deterministic span ID derived from traceId
122
+ traceFlags: TraceFlags.SAMPLED,
123
+ isRemote: true
124
+ };
125
+ logContext = trace.setSpanContext(otelCtx.active(), spanContext);
126
+ } catch {
127
+ }
128
+ }
129
+ _otelLogger.emit({
130
+ severityNumber,
131
+ severityText,
132
+ body,
133
+ timestamp: new Date(event.timestamp),
134
+ ...logContext ? { context: logContext } : {},
135
+ attributes: {
136
+ ...eventUrl ? { "browser.url": eventUrl } : {},
137
+ "mergen.event_type": event.type,
138
+ ...isConsole && event.stack ? { "exception.stacktrace": event.stack } : {},
139
+ ...isNetwork ? {
140
+ "http.method": event.method,
141
+ "http.url": event.url,
142
+ "http.status_code": event.status,
143
+ ...event.traceId ? { "trace_id": event.traceId } : {},
144
+ ...event.tracestate ? { "w3c.tracestate": event.tracestate } : {},
145
+ ...event.baggage ? { "w3c.baggage": JSON.stringify(event.baggage) } : {}
146
+ } : {}
147
+ }
148
+ });
149
+ } catch (err) {
150
+ logger.warn({ err }, "OTel export failed");
151
+ }
152
+ }
153
+ export {
154
+ configureOtel,
155
+ disableOtel,
156
+ exportToOtel,
157
+ getMetricCounters,
158
+ getOtelConfig,
159
+ getPrometheusMetrics,
160
+ isOtelEnabled
161
+ };
@@ -0,0 +1,71 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ import fs from "fs";
4
+ const DATA_DIR = process.env.MERGEN_DATA_DIR ?? path.join(os.homedir(), ".mergen");
5
+ const LICENSE_FILE = path.join(DATA_DIR, "license.json");
6
+ const USAGE_FILE = path.join(DATA_DIR, "usage.json");
7
+ const TEAM_FILE = path.join(DATA_DIR, "team.json");
8
+ const TELEMETRY_FILE = path.join(DATA_DIR, "telemetry.json");
9
+ function getLocalOrGlobal(filename) {
10
+ try {
11
+ const localDir = path.join(process.cwd(), ".mergen");
12
+ if (fs.existsSync(localDir)) {
13
+ return path.join(localDir, filename);
14
+ }
15
+ } catch {
16
+ }
17
+ return path.join(DATA_DIR, filename);
18
+ }
19
+ const SECRET_FILE = path.join(DATA_DIR, "secret");
20
+ const HISTORY_DB = path.join(DATA_DIR, "history.db");
21
+ const SESSION_FILE = path.join(DATA_DIR, "session.json");
22
+ const SESSIONS_DIR = path.join(DATA_DIR, "sessions");
23
+ const AUDIT_LOG = path.join(DATA_DIR, "audit.log");
24
+ const AUDIT_DB = path.join(DATA_DIR, "audit.db");
25
+ const TOPOLOGY_FILE = path.join(DATA_DIR, "topology.json");
26
+ const CONFIG_FILE = path.join(DATA_DIR, "config.json");
27
+ const RBAC_FILE = path.join(DATA_DIR, "rbac.json");
28
+ const OVERRIDE_CORPUS_FILE = getLocalOrGlobal("override-corpus.json");
29
+ const SHADOW_LOG_FILE = path.join(DATA_DIR, "shadow-log.json");
30
+ const POSTMORTEMS_DB = path.join(DATA_DIR, "postmortems.db");
31
+ const CI_GATE_HISTORY_FILE = path.join(DATA_DIR, "ci-gate-history.json");
32
+ const PR_SHADOW_FILE = path.join(DATA_DIR, "pr-shadow.json");
33
+ const CALIBRATION_FILE = getLocalOrGlobal("calibration.json");
34
+ const USER_RUNBOOKS_DIR = path.join(DATA_DIR, "runbooks");
35
+ const APPROVALS_FILE = path.join(DATA_DIR, "approval-pending.json");
36
+ const SAFETY_POLICY_FILE = path.join(DATA_DIR, "safety-policy.json");
37
+ const BYPASS_PENDING_FILE = path.join(DATA_DIR, "bypass-pending.json");
38
+ const HITL_HOLDS_FILE = path.join(DATA_DIR, "hitl-holds.json");
39
+ const OIDC_SIGNING_KEY_FILE = path.join(DATA_DIR, "oidc-signing-key.json");
40
+ function zeroRetentionMode() {
41
+ return process.env.MERGEN_ZERO_RETENTION === "true";
42
+ }
43
+ export {
44
+ APPROVALS_FILE,
45
+ AUDIT_DB,
46
+ AUDIT_LOG,
47
+ BYPASS_PENDING_FILE,
48
+ CALIBRATION_FILE,
49
+ CI_GATE_HISTORY_FILE,
50
+ CONFIG_FILE,
51
+ DATA_DIR,
52
+ HISTORY_DB,
53
+ HITL_HOLDS_FILE,
54
+ LICENSE_FILE,
55
+ OIDC_SIGNING_KEY_FILE,
56
+ OVERRIDE_CORPUS_FILE,
57
+ POSTMORTEMS_DB,
58
+ PR_SHADOW_FILE,
59
+ RBAC_FILE,
60
+ SAFETY_POLICY_FILE,
61
+ SECRET_FILE,
62
+ SESSIONS_DIR,
63
+ SESSION_FILE,
64
+ SHADOW_LOG_FILE,
65
+ TEAM_FILE,
66
+ TELEMETRY_FILE,
67
+ TOPOLOGY_FILE,
68
+ USAGE_FILE,
69
+ USER_RUNBOOKS_DIR,
70
+ zeroRetentionMode
71
+ };