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,260 @@
1
+ /**
2
+ * mergen-inject.js — Mergen universal JS SDK
3
+ *
4
+ * Streams console, network, and crash events to the local Mergen server.
5
+ * Works in:
6
+ * - React Native (import './mergen-inject.js' at app entry)
7
+ * - Mobile webviews (inject via evaluateJavaScript)
8
+ * - Any JS environment with fetch or XMLHttpRequest
9
+ *
10
+ * Usage (React Native):
11
+ * import './mergen-inject.js'; // top of index.js — patches console + fetch globally
12
+ *
13
+ * Usage (webview):
14
+ * webView.evaluateJavaScript(fs.readFileSync('mergen-inject.js', 'utf8'));
15
+ *
16
+ * Config (optional — set before importing):
17
+ * global.mergenConfig = { port: 3000, secret: 'your-secret', name: 'my-app' };
18
+ */
19
+ (function mergenInject(opts) {
20
+ 'use strict';
21
+
22
+ var port = (opts && opts.port) || (typeof MERGEN_PORT !== 'undefined' ? MERGEN_PORT : 3000);
23
+ var secret = (opts && opts.secret) || (typeof MERGEN_SECRET !== 'undefined' ? MERGEN_SECRET : null);
24
+ var appName = (opts && opts.name) || (typeof MERGEN_NAME !== 'undefined' ? MERGEN_NAME : 'rn-app');
25
+ var base = 'http://127.0.0.1:' + port + '/ingest';
26
+ var appUrl = 'mergen://rn/' + appName;
27
+
28
+ // ── Safe serialiser ───────────────────────────────────────────────────────────
29
+
30
+ function safeArg(val) {
31
+ if (val === null || val === undefined) return String(val);
32
+ var t = typeof val;
33
+ if (t === 'string' || t === 'number' || t === 'boolean') return val;
34
+ if (val instanceof Error || (val && t === 'object' && typeof val.message === 'string' && typeof val.stack === 'string')) {
35
+ return { __error__: true, name: val.name || 'Error', message: val.message, stack: val.stack || '' };
36
+ }
37
+ try { return JSON.parse(JSON.stringify(val)); } catch (e) { return String(val); }
38
+ }
39
+
40
+ // ── Internal poster (fire-and-forget) ─────────────────────────────────────────
41
+
42
+ function post(event) {
43
+ try {
44
+ var body = JSON.stringify(event);
45
+ var headers = { 'Content-Type': 'application/json' };
46
+ if (secret) headers['x-mergen-secret'] = secret;
47
+
48
+ if (typeof fetch !== 'undefined') {
49
+ // Use _nativeFetch if available (after fetch patching below), else raw fetch
50
+ var poster = typeof _nativeFetch !== 'undefined' ? _nativeFetch : fetch;
51
+ poster(base, { method: 'POST', headers: headers, body: body }).catch(function () {});
52
+ return;
53
+ }
54
+ if (typeof XMLHttpRequest !== 'undefined') {
55
+ var xhr = new XMLHttpRequest();
56
+ xhr.open('POST', base, true);
57
+ for (var h in headers) if (Object.prototype.hasOwnProperty.call(headers, h)) {
58
+ xhr.setRequestHeader(h, headers[h]);
59
+ }
60
+ xhr.send(body);
61
+ }
62
+ } catch (e) { /* never break the host app */ }
63
+ }
64
+
65
+ // ── Traceparent generation ─────────────────────────────────────────────────────
66
+ // Uses crypto.getRandomValues when available (browser/RN), falls back to Math.random.
67
+
68
+ function generateTraceContext() {
69
+ try {
70
+ var bytes = new Uint8Array(24);
71
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
72
+ crypto.getRandomValues(bytes);
73
+ } else {
74
+ for (var i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
75
+ }
76
+ var hex = Array.prototype.map.call(bytes, function(b) {
77
+ return ('0' + b.toString(16)).slice(-2);
78
+ }).join('');
79
+ var traceId = hex.slice(0, 32);
80
+ var spanId = hex.slice(32, 48);
81
+ return { header: '00-' + traceId + '-' + spanId + '-01', traceId: traceId };
82
+ } catch (e) { return null; }
83
+ }
84
+
85
+ function isSameOriginOrLocal(url) {
86
+ try {
87
+ if (!url || url.charAt(0) === '/') return true;
88
+ var parsed = new URL(url);
89
+ var h = parsed.hostname;
90
+ return h === 'localhost' || h === '127.0.0.1' || h === '::1';
91
+ } catch (e) { return false; }
92
+ }
93
+
94
+ // ── Console patching ───────────────────────────────────────────────────────────
95
+
96
+ var _origConsole = {};
97
+ ['log', 'warn', 'error'].forEach(function (level) {
98
+ _origConsole[level] = console[level] && console[level].bind
99
+ ? console[level].bind(console)
100
+ : console[level];
101
+
102
+ console[level] = function mergenConsole() {
103
+ if (_origConsole[level]) _origConsole[level].apply(console, arguments);
104
+ try {
105
+ var args = Array.prototype.slice.call(arguments).map(safeArg);
106
+ var stack;
107
+ if (level === 'error') {
108
+ try { stack = new Error().stack.split('\n').slice(2).join('\n'); } catch (e) {}
109
+ }
110
+ post({
111
+ type: 'console',
112
+ level: level,
113
+ args: args,
114
+ stack: stack,
115
+ url: appUrl,
116
+ timestamp: Date.now(),
117
+ });
118
+ } catch (e) {}
119
+ };
120
+ });
121
+
122
+ // ── Fetch patching ─────────────────────────────────────────────────────────────
123
+
124
+ var _globalObj = typeof globalThis !== 'undefined' ? globalThis
125
+ : typeof global !== 'undefined' ? global
126
+ : typeof window !== 'undefined' ? window
127
+ : {};
128
+
129
+ var _nativeFetch = typeof _globalObj.fetch === 'function'
130
+ ? _globalObj.fetch.bind(_globalObj)
131
+ : null;
132
+
133
+ if (_nativeFetch) {
134
+ _globalObj.fetch = function mergenFetch(input, init) {
135
+ var url = typeof input === 'string' ? input : (input && input.url) || '';
136
+ var method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
137
+ var startTime = Date.now();
138
+
139
+ // Inject traceparent on local/same-origin requests
140
+ var ctx = isSameOriginOrLocal(url) ? generateTraceContext() : null;
141
+ var useInit = init;
142
+ if (ctx) {
143
+ try {
144
+ var existingHdrs = (init && init.headers) || (input && typeof input === 'object' && input.headers);
145
+ var newHdrs = new Headers(existingHdrs || {});
146
+ if (!newHdrs.has('traceparent')) newHdrs.set('traceparent', ctx.header);
147
+ useInit = Object.assign({}, init || {}, { headers: newHdrs });
148
+ } catch (e) { useInit = init; }
149
+ }
150
+
151
+ return _nativeFetch(input, useInit).then(function (response) {
152
+ var duration = Date.now() - startTime;
153
+ var traceId = (ctx && ctx.traceId) || null;
154
+
155
+ // Try to read traceId from response headers if we didn't inject one
156
+ if (!traceId) {
157
+ try {
158
+ var tp = response.headers.get('traceparent');
159
+ if (tp) { var tparts = tp.split('-'); if (tparts.length >= 4) traceId = tparts[1]; }
160
+ if (!traceId) {
161
+ traceId = response.headers.get('x-trace-id')
162
+ || response.headers.get('x-request-id')
163
+ || response.headers.get('x-correlation-id')
164
+ || null;
165
+ }
166
+ if (traceId) traceId = traceId.slice(0, 64);
167
+ } catch (e) {}
168
+ }
169
+
170
+ var ev = {
171
+ type: 'network',
172
+ method: method,
173
+ url: url,
174
+ status: response.status,
175
+ statusText: response.statusText || '',
176
+ duration: duration,
177
+ timestamp: startTime,
178
+ };
179
+ if (traceId) ev.traceId = traceId;
180
+
181
+ // Clone + read body only for non-streaming responses under 50KB
182
+ try {
183
+ response.clone().text().then(function (body) {
184
+ var ct = response.headers.get('content-type') || '';
185
+ ev.responseBody = ct.indexOf('application/json') !== -1
186
+ ? (function() { try { return JSON.parse(body); } catch(e) { return body.slice(0, 500); } })()
187
+ : body.slice(0, 500);
188
+ post(ev);
189
+ }).catch(function () { post(ev); });
190
+ } catch (e) { post(ev); }
191
+
192
+ return response;
193
+
194
+ }, function (err) {
195
+ post({
196
+ type: 'network',
197
+ method: method,
198
+ url: url,
199
+ status: 0,
200
+ statusText: '',
201
+ duration: Date.now() - startTime,
202
+ error: err ? (err.message || String(err)) : 'network error',
203
+ timestamp: startTime,
204
+ });
205
+ throw err;
206
+ });
207
+ };
208
+ }
209
+
210
+ // ── Crash capture (React Native) ───────────────────────────────────────────────
211
+ // React Native exposes ErrorUtils for global error handling.
212
+ // This is the RN equivalent of window.onerror.
213
+
214
+ try {
215
+ if (typeof ErrorUtils !== 'undefined' && ErrorUtils.setGlobalHandler) {
216
+ var _prevHandler = ErrorUtils.getGlobalHandler && ErrorUtils.getGlobalHandler();
217
+ ErrorUtils.setGlobalHandler(function mergenErrorHandler(err, isFatal) {
218
+ try {
219
+ post({
220
+ type: 'console',
221
+ level: 'error',
222
+ args: ['[' + (isFatal ? 'fatal' : 'error') + '] ' + (err && err.message ? err.message : String(err))],
223
+ stack: err && err.stack ? err.stack : undefined,
224
+ url: appUrl,
225
+ timestamp: Date.now(),
226
+ });
227
+ } catch (e) {}
228
+ if (_prevHandler) _prevHandler(err, isFatal);
229
+ });
230
+ }
231
+ } catch (e) { /* ErrorUtils not available — browser or non-RN env */ }
232
+
233
+ // ── Unhandled promise rejections ───────────────────────────────────────────────
234
+
235
+ try {
236
+ var _prevOnUnhandledRejection = _globalObj.onunhandledrejection;
237
+ _globalObj.onunhandledrejection = function mergenUnhandledRejection(event) {
238
+ try {
239
+ var reason = event && event.reason;
240
+ var message = reason instanceof Error ? reason.message : String(reason || 'unhandled rejection');
241
+ var stack = reason instanceof Error ? reason.stack : undefined;
242
+ post({
243
+ type: 'console',
244
+ level: 'error',
245
+ args: ['[unhandledRejection] ' + message],
246
+ stack: stack,
247
+ url: appUrl,
248
+ timestamp: Date.now(),
249
+ });
250
+ } catch (e) {}
251
+ if (_prevOnUnhandledRejection) _prevOnUnhandledRejection(event);
252
+ };
253
+ } catch (e) { /* ignore */ }
254
+
255
+ // ── Ready ──────────────────────────────────────────────────────────────────────
256
+ if (_origConsole.log) {
257
+ _origConsole.log('[Mergen] SDK active (' + appName + ') → http://127.0.0.1:' + port);
258
+ }
259
+
260
+ })(typeof mergenConfig !== 'undefined' ? mergenConfig : {});
package/sdk/node.js ADDED
@@ -0,0 +1,313 @@
1
+ /**
2
+ * sdk/node.js — Mergen Node.js SDK
3
+ *
4
+ * Drop-in instrumentation for any Node.js process. Works with Express,
5
+ * Fastify, NestJS, Next.js SSR, plain scripts, or any backend service.
6
+ *
7
+ * Usage — zero code change:
8
+ * node --require mergen-server/sdk/node app.js
9
+ *
10
+ * Usage — in code (import once at the top of your entry point):
11
+ * require('mergen-server/sdk/node')
12
+ *
13
+ * What gets instrumented automatically:
14
+ * • console.log / warn / error → captured and sent as console events
15
+ * • http.request / https.request → status + duration + traceparent injection
16
+ * • globalThis.fetch (Node 18+) → same as above
17
+ * • uncaughtException + unhandledRejection → captured as error events
18
+ *
19
+ * Config (env vars, all optional):
20
+ * MERGEN_PORT=3000 default: 3000
21
+ * MERGEN_SECRET=... must match server's MERGEN_SECRET if set
22
+ * MERGEN_NAME=backend process label shown in get_process_logs and get_unified_timeline
23
+ */
24
+
25
+ 'use strict';
26
+
27
+ const http = require('http');
28
+ const https = require('https');
29
+ const crypto = require('crypto');
30
+ const path = require('path');
31
+
32
+ // ── Config ─────────────────────────────────────────────────────────────────────
33
+
34
+ const MERGEN_PORT = parseInt(process.env.MERGEN_PORT ?? '3000', 10);
35
+ const MERGEN_HOST = process.env.MERGEN_HOST ?? '127.0.0.1';
36
+ // Docker / k8s: set MERGEN_HOST=host.docker.internal so the container can
37
+ // reach the Mergen server running on the host machine.
38
+ const MERGEN_SECRET = process.env.MERGEN_SECRET ?? null;
39
+ const PROCESS_NAME = process.env.MERGEN_NAME ?? _resolveProcessName();
40
+
41
+ // Pseudo-URL used as the "url" field on events so the server and MCP tools
42
+ // can distinguish Node events from browser tab events.
43
+ const PROCESS_URL = `mergen://node/${PROCESS_NAME}`;
44
+
45
+ function _resolveProcessName() {
46
+ try {
47
+ const pkg = require(path.join(process.cwd(), 'package.json'));
48
+ if (typeof pkg.name === 'string' && pkg.name) return pkg.name;
49
+ } catch { /* no package.json */ }
50
+ try {
51
+ if (process.argv[1]) return path.basename(process.argv[1], path.extname(process.argv[1]));
52
+ } catch { /* ignore */ }
53
+ return 'node';
54
+ }
55
+
56
+ // ── Internal poster ─────────────────────────────────────────────────────────────
57
+ // Captured BEFORE any patching so Mergen's own posts never go through the
58
+ // instrumented http.request (no infinite loops).
59
+
60
+ const _origHttpRequest = http.request.bind(http);
61
+
62
+ function _post(event) {
63
+ try {
64
+ const body = JSON.stringify(event);
65
+ const headers = {
66
+ 'Content-Type': 'application/json',
67
+ 'Content-Length': Buffer.byteLength(body),
68
+ };
69
+ if (MERGEN_SECRET) headers['x-mergen-secret'] = MERGEN_SECRET;
70
+
71
+ const req = _origHttpRequest({
72
+ hostname: MERGEN_HOST,
73
+ port: MERGEN_PORT,
74
+ path: '/ingest',
75
+ method: 'POST',
76
+ headers,
77
+ });
78
+ req.on('error', () => {}); // server not running — silently ignore
79
+ req.end(body);
80
+ } catch { /* serialization error or server down — never crash the host */ }
81
+ }
82
+
83
+ // ── Trace context ───────────────────────────────────────────────────────────────
84
+
85
+ function _traceCtx() {
86
+ const buf = crypto.randomBytes(24);
87
+ const traceId = buf.slice(0, 16).toString('hex'); // 32 hex chars
88
+ const spanId = buf.slice(16, 24).toString('hex'); // 16 hex chars
89
+ return { header: `00-${traceId}-${spanId}-01`, traceId };
90
+ }
91
+
92
+ // ── Recursion guard ─────────────────────────────────────────────────────────────
93
+ // Don't instrument Mergen's own posts to 127.0.0.1:MERGEN_PORT.
94
+
95
+ function _isMergenHost(hostname, port) {
96
+ const rightPort = Number(port || 0) === MERGEN_PORT;
97
+ const isLocal = hostname === '127.0.0.1' || hostname === 'localhost';
98
+ return rightPort && (isLocal || hostname === MERGEN_HOST);
99
+ }
100
+
101
+ function _extractHost(urlOrOpts) {
102
+ try {
103
+ if (typeof urlOrOpts === 'string' || urlOrOpts instanceof URL) {
104
+ const u = new URL(urlOrOpts instanceof URL ? urlOrOpts.href : urlOrOpts);
105
+ return { hostname: u.hostname, port: u.port };
106
+ }
107
+ if (urlOrOpts && typeof urlOrOpts === 'object') {
108
+ return {
109
+ hostname: urlOrOpts.hostname ?? (urlOrOpts.host ?? '').split(':')[0],
110
+ port: String(urlOrOpts.port ?? ''),
111
+ };
112
+ }
113
+ } catch { /* ignore */ }
114
+ return { hostname: '', port: '' };
115
+ }
116
+
117
+ // ── Console patching ────────────────────────────────────────────────────────────
118
+
119
+ const _origConsole = {
120
+ log: console.log.bind(console),
121
+ warn: console.warn.bind(console),
122
+ error: console.error.bind(console),
123
+ };
124
+
125
+ function _safeArg(a) {
126
+ if (a === null || a === undefined) return String(a);
127
+ if (typeof a === 'string' || typeof a === 'number' || typeof a === 'boolean') return a;
128
+ if (a instanceof Error) return { __error__: true, name: a.name, message: a.message, stack: a.stack ?? '' };
129
+ try { return JSON.parse(JSON.stringify(a)); } catch { return String(a); }
130
+ }
131
+
132
+ ['log', 'warn', 'error'].forEach((level) => {
133
+ console[level] = function mergenConsole(...args) {
134
+ _origConsole[level].apply(console, args);
135
+ try {
136
+ const stack = level === 'error'
137
+ ? new Error().stack?.split('\n').slice(2).join('\n')
138
+ : undefined;
139
+ _post({
140
+ type: 'console',
141
+ level,
142
+ args: args.map(_safeArg),
143
+ stack,
144
+ url: PROCESS_URL,
145
+ timestamp: Date.now(),
146
+ });
147
+ } catch { /* never crash */ }
148
+ };
149
+ });
150
+
151
+ // ── HTTP / HTTPS request patching ───────────────────────────────────────────────
152
+ // Injects traceparent on all outbound requests except those to the Mergen server.
153
+ // Captures status + duration on response close WITHOUT consuming the body —
154
+ // safe for all callers regardless of streaming/buffering strategy.
155
+
156
+ function _buildNetworkEvent(method, urlStr, startTime, statusCode, statusText, traceId, error) {
157
+ const ev = {
158
+ type: 'network',
159
+ method,
160
+ url: urlStr,
161
+ status: statusCode ?? 0,
162
+ statusText: statusText ?? '',
163
+ duration: Date.now() - startTime,
164
+ timestamp: startTime,
165
+ };
166
+ if (traceId) ev.traceId = traceId;
167
+ if (error) ev.error = error;
168
+ return ev;
169
+ }
170
+
171
+ function _wrapRequest(mod, origReq) {
172
+ return function mergenRequest(urlOrOpts, optsOrCb, cb) {
173
+ const { hostname, port } = _extractHost(urlOrOpts);
174
+ if (_isMergenHost(hostname, port)) return origReq(urlOrOpts, optsOrCb, cb);
175
+
176
+ const ctx = _traceCtx();
177
+ let urlStr = '';
178
+ let method = 'GET';
179
+
180
+ // Build a loggable URL and method
181
+ try {
182
+ if (typeof urlOrOpts === 'string') {
183
+ urlStr = urlOrOpts;
184
+ } else if (urlOrOpts instanceof URL) {
185
+ urlStr = urlOrOpts.href;
186
+ } else if (urlOrOpts && typeof urlOrOpts === 'object') {
187
+ const proto = urlOrOpts.protocol ?? (mod === https ? 'https:' : 'http:');
188
+ const host = hostname;
189
+ const p = port ? `:${port}` : '';
190
+ urlStr = `${proto}//${host}${p}${urlOrOpts.path ?? '/'}`;
191
+ if (urlOrOpts.method) method = urlOrOpts.method.toUpperCase();
192
+ }
193
+ if (typeof optsOrCb === 'object' && optsOrCb !== null && typeof optsOrCb !== 'function') {
194
+ if (optsOrCb.method) method = optsOrCb.method.toUpperCase();
195
+ }
196
+ } catch { /* ignore */ }
197
+
198
+ // Inject traceparent — patch whichever argument carries headers
199
+ let arg0 = urlOrOpts;
200
+ let arg1 = optsOrCb;
201
+ try {
202
+ if (typeof urlOrOpts === 'object' && !(urlOrOpts instanceof URL)) {
203
+ // options-only call: http.request({ hostname, headers, ... }, cb)
204
+ arg0 = { ...urlOrOpts, headers: { ...urlOrOpts.headers, traceparent: ctx.header } };
205
+ } else if (typeof optsOrCb === 'object' && optsOrCb !== null && typeof optsOrCb !== 'function') {
206
+ // URL + options call: http.request(url, { headers, ... }, cb)
207
+ arg1 = { ...optsOrCb, headers: { ...optsOrCb.headers, traceparent: ctx.header } };
208
+ }
209
+ // URL-only call (http.request(url, cb)): no options object to inject into
210
+ } catch { /* injection failed — proceed without traceparent */ }
211
+
212
+ const startTime = Date.now();
213
+ let req;
214
+ try {
215
+ req = origReq(arg0, arg1, cb);
216
+ } catch (err) {
217
+ _post(_buildNetworkEvent(method, urlStr, startTime, 0, '', ctx.traceId, err.message));
218
+ throw err;
219
+ }
220
+
221
+ // Attach non-consuming response capture
222
+ req.on('response', (res) => {
223
+ const statusCode = res.statusCode;
224
+ const statusText = res.statusMessage ?? '';
225
+ // 'close' fires after the caller fully consumes the response stream.
226
+ // We never call res.read() / res.resume() so the caller's stream is untouched.
227
+ res.on('close', () => {
228
+ _post(_buildNetworkEvent(method, urlStr, startTime, statusCode, statusText, ctx.traceId, null));
229
+ });
230
+ });
231
+
232
+ req.on('error', (err) => {
233
+ _post(_buildNetworkEvent(method, urlStr, startTime, 0, '', ctx.traceId, err.message));
234
+ });
235
+
236
+ return req;
237
+ };
238
+ }
239
+
240
+ const _origHttpsRequest = https.request.bind(https);
241
+ http.request = _wrapRequest(http, _origHttpRequest);
242
+ https.request = _wrapRequest(https, _origHttpsRequest);
243
+
244
+ // ── Global fetch (Node 18+) ─────────────────────────────────────────────────────
245
+
246
+ if (typeof globalThis.fetch === 'function') {
247
+ const _origFetch = globalThis.fetch.bind(globalThis);
248
+
249
+ globalThis.fetch = async function mergenFetch(input, init) {
250
+ const url = typeof input === 'string' ? input
251
+ : (input instanceof URL ? input.href : (input?.url ?? ''));
252
+ const method = ((init?.method) ?? (input?.method) ?? 'GET').toUpperCase();
253
+
254
+ const parsed = (() => { try { return new URL(url); } catch { return null; } })();
255
+ if (parsed && _isMergenHost(parsed.hostname, parsed.port)) {
256
+ return _origFetch(input, init);
257
+ }
258
+
259
+ const ctx = _traceCtx();
260
+ let patchedInit = init;
261
+ try {
262
+ const hdrs = new Headers(init?.headers ?? {});
263
+ if (!hdrs.has('traceparent')) hdrs.set('traceparent', ctx.header);
264
+ patchedInit = { ...init, headers: hdrs };
265
+ } catch { /* inject failed — proceed without traceparent */ }
266
+
267
+ const startTime = Date.now();
268
+ try {
269
+ const response = await _origFetch(input, patchedInit);
270
+ _post(_buildNetworkEvent(method, url, startTime, response.status, response.statusText, ctx.traceId, null));
271
+ return response;
272
+ } catch (err) {
273
+ _post(_buildNetworkEvent(method, url, startTime, 0, '', ctx.traceId, err instanceof Error ? err.message : String(err)));
274
+ throw err;
275
+ }
276
+ };
277
+ }
278
+
279
+ // ── Crash capture ───────────────────────────────────────────────────────────────
280
+ // uncaughtExceptionMonitor is read-only (Node 13+) — fires before the default
281
+ // crash handler, doesn't suppress it. Safe to add unconditionally.
282
+ //
283
+ // unhandledRejection: in Node 15+ the process still exits after all listeners
284
+ // have been called, so adding this listener doesn't change crash behavior.
285
+
286
+ process.on('uncaughtExceptionMonitor', (err) => {
287
+ try {
288
+ _post({
289
+ type: 'console', level: 'error',
290
+ args: [`[uncaughtException] ${err.message}`],
291
+ stack: err.stack,
292
+ url: PROCESS_URL,
293
+ timestamp: Date.now(),
294
+ });
295
+ } catch { /* ignore */ }
296
+ });
297
+
298
+ process.on('unhandledRejection', (reason) => {
299
+ try {
300
+ const message = reason instanceof Error ? reason.message : String(reason ?? 'unhandled rejection');
301
+ const stack = reason instanceof Error ? reason.stack : undefined;
302
+ _post({
303
+ type: 'console', level: 'error',
304
+ args: [`[unhandledRejection] ${message}`],
305
+ stack,
306
+ url: PROCESS_URL,
307
+ timestamp: Date.now(),
308
+ });
309
+ } catch { /* ignore */ }
310
+ });
311
+
312
+ // ── Ready ───────────────────────────────────────────────────────────────────────
313
+ _origConsole.log(`[Mergen] Node SDK active — ${PROCESS_NAME} → http://${MERGEN_HOST}:${MERGEN_PORT}`);
@@ -0,0 +1,57 @@
1
+ /**
2
+ * mergenPlugin() — Vite plugin for automatic build SHA injection.
3
+ *
4
+ * Injects <meta name="mergen:sha" content="<sha>"> into the built HTML so
5
+ * the Mergen browser extension automatically links every browser error to the
6
+ * exact CI run and deployment that shipped it.
7
+ *
8
+ * Usage (vite.config.ts):
9
+ * import { mergenPlugin } from 'mergen-server/sdk/vite-plugin';
10
+ * export default defineConfig({ plugins: [mergenPlugin()] });
11
+ *
12
+ * SHA resolution order:
13
+ * 1. MERGEN_SHA env var (explicit override)
14
+ * 2. GITHUB_SHA / CI_COMMIT_SHA / GIT_COMMIT (CI environment variables)
15
+ * 3. git rev-parse HEAD (local dev — always works in a git repo)
16
+ */
17
+
18
+ import { execSync } from 'child_process';
19
+ import type { Plugin, IndexHtmlTransformResult } from 'vite';
20
+
21
+ function resolveSha(): string {
22
+ const fromEnv =
23
+ process.env.MERGEN_SHA ??
24
+ process.env.GITHUB_SHA ??
25
+ process.env.CI_COMMIT_SHA ?? // GitLab
26
+ process.env.GIT_COMMIT ?? // Jenkins
27
+ process.env.CIRCLE_SHA1; // CircleCI
28
+
29
+ if (fromEnv) return fromEnv.slice(0, 40);
30
+
31
+ try {
32
+ return execSync('git rev-parse HEAD', { encoding: 'utf8', timeout: 2000 }).trim();
33
+ } catch {
34
+ return '';
35
+ }
36
+ }
37
+
38
+ export function mergenPlugin(): Plugin {
39
+ const sha = resolveSha();
40
+
41
+ return {
42
+ name: 'mergen-vite',
43
+
44
+ transformIndexHtml(html): IndexHtmlTransformResult {
45
+ if (!sha) return html;
46
+ return [{ tag: 'meta', attrs: { name: 'mergen:sha', content: sha }, injectTo: 'head-prepend' }];
47
+ },
48
+
49
+ configResolved() {
50
+ if (sha) {
51
+ console.log(`[mergen] injecting build SHA: ${sha.slice(0, 7)}`);
52
+ } else {
53
+ console.warn('[mergen] could not resolve SHA — set MERGEN_SHA env var or run in a git repo');
54
+ }
55
+ },
56
+ };
57
+ }