@newrelic/preflight 0.0.1-pre.1 → 1.0.1

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 (578) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +498 -0
  3. package/dist/alerts/alert-log.d.ts +24 -0
  4. package/dist/alerts/alert-log.d.ts.map +1 -0
  5. package/dist/alerts/alert-log.js +159 -0
  6. package/dist/alerts/alert-log.js.map +1 -0
  7. package/dist/alerts/alert-snapshot-collector.d.ts +168 -0
  8. package/dist/alerts/alert-snapshot-collector.d.ts.map +1 -0
  9. package/dist/alerts/alert-snapshot-collector.js +243 -0
  10. package/dist/alerts/alert-snapshot-collector.js.map +1 -0
  11. package/dist/alerts/local-alert-engine.d.ts +86 -0
  12. package/dist/alerts/local-alert-engine.d.ts.map +1 -0
  13. package/dist/alerts/local-alert-engine.js +466 -0
  14. package/dist/alerts/local-alert-engine.js.map +1 -0
  15. package/dist/alerts/local-alert-rule.d.ts +439 -0
  16. package/dist/alerts/local-alert-rule.d.ts.map +1 -0
  17. package/dist/alerts/local-alert-rule.js +139 -0
  18. package/dist/alerts/local-alert-rule.js.map +1 -0
  19. package/dist/alerts/os-notifier.d.ts +39 -0
  20. package/dist/alerts/os-notifier.d.ts.map +1 -0
  21. package/dist/alerts/os-notifier.js +170 -0
  22. package/dist/alerts/os-notifier.js.map +1 -0
  23. package/dist/alerts/types.d.ts +35 -0
  24. package/dist/alerts/types.d.ts.map +1 -0
  25. package/dist/alerts/types.js +8 -0
  26. package/dist/alerts/types.js.map +1 -0
  27. package/dist/config.d.ts +169 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +868 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/dashboard/dashboard-server.d.ts +38 -0
  32. package/dist/dashboard/dashboard-server.d.ts.map +1 -0
  33. package/dist/dashboard/dashboard-server.js +207 -0
  34. package/dist/dashboard/dashboard-server.js.map +1 -0
  35. package/dist/dashboard/index.d.ts +3 -0
  36. package/dist/dashboard/index.d.ts.map +1 -0
  37. package/dist/dashboard/index.js +2 -0
  38. package/dist/dashboard/index.js.map +1 -0
  39. package/dist/dashboard/live-event-bus.d.ts +99 -0
  40. package/dist/dashboard/live-event-bus.d.ts.map +1 -0
  41. package/dist/dashboard/live-event-bus.js +56 -0
  42. package/dist/dashboard/live-event-bus.js.map +1 -0
  43. package/dist/dashboard/routes/api-handler.d.ts +122 -0
  44. package/dist/dashboard/routes/api-handler.d.ts.map +1 -0
  45. package/dist/dashboard/routes/api-handler.js +1414 -0
  46. package/dist/dashboard/routes/api-handler.js.map +1 -0
  47. package/dist/dashboard/routes/replay-analyzer.d.ts +15 -0
  48. package/dist/dashboard/routes/replay-analyzer.d.ts.map +1 -0
  49. package/dist/dashboard/routes/replay-analyzer.js +227 -0
  50. package/dist/dashboard/routes/replay-analyzer.js.map +1 -0
  51. package/dist/dashboard/routes/sse-handler.d.ts +4 -0
  52. package/dist/dashboard/routes/sse-handler.d.ts.map +1 -0
  53. package/dist/dashboard/routes/sse-handler.js +122 -0
  54. package/dist/dashboard/routes/sse-handler.js.map +1 -0
  55. package/dist/dashboard/routes/static-handler.d.ts +3 -0
  56. package/dist/dashboard/routes/static-handler.d.ts.map +1 -0
  57. package/dist/dashboard/routes/static-handler.js +123 -0
  58. package/dist/dashboard/routes/static-handler.js.map +1 -0
  59. package/dist/data/alerts/conditions/01-daily-cost-spike.json +16 -0
  60. package/dist/data/alerts/conditions/02-low-efficiency-score.json +16 -0
  61. package/dist/data/alerts/conditions/03-stuck-loop-rate.json +16 -0
  62. package/dist/data/alerts/conditions/04-anti-pattern-rate.json +16 -0
  63. package/dist/data/alerts/conditions/05-session-cost-budget.json +16 -0
  64. package/dist/data/alerts/conditions-personal/01-personal-daily-cost.json +16 -0
  65. package/dist/data/alerts/conditions-personal/02-personal-session-cost.json +16 -0
  66. package/dist/data/alerts/conditions-personal/03-personal-low-efficiency.json +16 -0
  67. package/dist/data/alerts/conditions-personal/04-personal-anti-pattern-rate.json +16 -0
  68. package/dist/data/alerts/conditions-personal/05-personal-stuck-loop.json +16 -0
  69. package/dist/data/alerts/policy.json +4 -0
  70. package/dist/data/dashboards/ai-coding-assistant-manager-view.json +103 -0
  71. package/dist/data/dashboards/ai-coding-assistant-overview.json +239 -0
  72. package/dist/data/dashboards/ai-coding-assistant-personal.json +442 -0
  73. package/dist/data/dashboards/ai-coding-assistant-platform-comparison.json +320 -0
  74. package/dist/data/dashboards/ai-coding-assistant-security.json +275 -0
  75. package/dist/data/dashboards/ai-coding-assistant-session-detail.json +296 -0
  76. package/dist/data/dashboards/ai-coding-assistant-team-view.json +345 -0
  77. package/dist/deploy/data-paths.d.ts +22 -0
  78. package/dist/deploy/data-paths.d.ts.map +1 -0
  79. package/dist/deploy/data-paths.js +69 -0
  80. package/dist/deploy/data-paths.js.map +1 -0
  81. package/dist/deploy/deploy-alerts.d.ts +58 -0
  82. package/dist/deploy/deploy-alerts.d.ts.map +1 -0
  83. package/dist/deploy/deploy-alerts.js +371 -0
  84. package/dist/deploy/deploy-alerts.js.map +1 -0
  85. package/dist/deploy/deploy-dashboards.d.ts +92 -0
  86. package/dist/deploy/deploy-dashboards.d.ts.map +1 -0
  87. package/dist/deploy/deploy-dashboards.js +282 -0
  88. package/dist/deploy/deploy-dashboards.js.map +1 -0
  89. package/dist/digest/digest-formatter.d.ts +3 -0
  90. package/dist/digest/digest-formatter.d.ts.map +1 -0
  91. package/dist/digest/digest-formatter.js +37 -0
  92. package/dist/digest/digest-formatter.js.map +1 -0
  93. package/dist/digest/digest-sender.d.ts +2 -0
  94. package/dist/digest/digest-sender.d.ts.map +1 -0
  95. package/dist/digest/digest-sender.js +29 -0
  96. package/dist/digest/digest-sender.js.map +1 -0
  97. package/dist/hooks/bash-classifier.d.ts +26 -0
  98. package/dist/hooks/bash-classifier.d.ts.map +1 -0
  99. package/dist/hooks/bash-classifier.js +409 -0
  100. package/dist/hooks/bash-classifier.js.map +1 -0
  101. package/dist/hooks/collector-script.d.ts +47 -0
  102. package/dist/hooks/collector-script.d.ts.map +1 -0
  103. package/dist/hooks/collector-script.js +662 -0
  104. package/dist/hooks/collector-script.js.map +1 -0
  105. package/dist/hooks/event-processor.d.ts +65 -0
  106. package/dist/hooks/event-processor.d.ts.map +1 -0
  107. package/dist/hooks/event-processor.js +342 -0
  108. package/dist/hooks/event-processor.js.map +1 -0
  109. package/dist/hooks/index.d.ts +7 -0
  110. package/dist/hooks/index.d.ts.map +1 -0
  111. package/dist/hooks/index.js +5 -0
  112. package/dist/hooks/index.js.map +1 -0
  113. package/dist/hooks/session-resolver.d.ts +66 -0
  114. package/dist/hooks/session-resolver.d.ts.map +1 -0
  115. package/dist/hooks/session-resolver.js +196 -0
  116. package/dist/hooks/session-resolver.js.map +1 -0
  117. package/dist/hooks/tool-parsers.d.ts +19 -0
  118. package/dist/hooks/tool-parsers.d.ts.map +1 -0
  119. package/dist/hooks/tool-parsers.js +260 -0
  120. package/dist/hooks/tool-parsers.js.map +1 -0
  121. package/dist/index.d.ts +107 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +1505 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/install/cli.d.ts +11 -0
  126. package/dist/install/cli.d.ts.map +1 -0
  127. package/dist/install/cli.js +365 -0
  128. package/dist/install/cli.js.map +1 -0
  129. package/dist/install/index.d.ts +4 -0
  130. package/dist/install/index.d.ts.map +1 -0
  131. package/dist/install/index.js +3 -0
  132. package/dist/install/index.js.map +1 -0
  133. package/dist/install/install-helper.d.ts +35 -0
  134. package/dist/install/install-helper.d.ts.map +1 -0
  135. package/dist/install/install-helper.js +227 -0
  136. package/dist/install/install-helper.js.map +1 -0
  137. package/dist/install/key-validator.d.ts +19 -0
  138. package/dist/install/key-validator.d.ts.map +1 -0
  139. package/dist/install/key-validator.js +122 -0
  140. package/dist/install/key-validator.js.map +1 -0
  141. package/dist/install/migrate.d.ts +12 -0
  142. package/dist/install/migrate.d.ts.map +1 -0
  143. package/dist/install/migrate.js +115 -0
  144. package/dist/install/migrate.js.map +1 -0
  145. package/dist/install/schedule.d.ts +11 -0
  146. package/dist/install/schedule.d.ts.map +1 -0
  147. package/dist/install/schedule.js +114 -0
  148. package/dist/install/schedule.js.map +1 -0
  149. package/dist/install/setup-wizard.d.ts +40 -0
  150. package/dist/install/setup-wizard.d.ts.map +1 -0
  151. package/dist/install/setup-wizard.js +489 -0
  152. package/dist/install/setup-wizard.js.map +1 -0
  153. package/dist/lib/date.d.ts +54 -0
  154. package/dist/lib/date.d.ts.map +1 -0
  155. package/dist/lib/date.js +85 -0
  156. package/dist/lib/date.js.map +1 -0
  157. package/dist/metrics/anti-patterns.d.ts +62 -0
  158. package/dist/metrics/anti-patterns.d.ts.map +1 -0
  159. package/dist/metrics/anti-patterns.js +301 -0
  160. package/dist/metrics/anti-patterns.js.map +1 -0
  161. package/dist/metrics/api-failure-tracker.d.ts +82 -0
  162. package/dist/metrics/api-failure-tracker.d.ts.map +1 -0
  163. package/dist/metrics/api-failure-tracker.js +202 -0
  164. package/dist/metrics/api-failure-tracker.js.map +1 -0
  165. package/dist/metrics/budget-tracker.d.ts +60 -0
  166. package/dist/metrics/budget-tracker.d.ts.map +1 -0
  167. package/dist/metrics/budget-tracker.js +130 -0
  168. package/dist/metrics/budget-tracker.js.map +1 -0
  169. package/dist/metrics/claudemd-tracker.d.ts +108 -0
  170. package/dist/metrics/claudemd-tracker.d.ts.map +1 -0
  171. package/dist/metrics/claudemd-tracker.js +337 -0
  172. package/dist/metrics/claudemd-tracker.js.map +1 -0
  173. package/dist/metrics/collaboration-profile.d.ts +65 -0
  174. package/dist/metrics/collaboration-profile.d.ts.map +1 -0
  175. package/dist/metrics/collaboration-profile.js +231 -0
  176. package/dist/metrics/collaboration-profile.js.map +1 -0
  177. package/dist/metrics/context-composition-tracker.d.ts +74 -0
  178. package/dist/metrics/context-composition-tracker.d.ts.map +1 -0
  179. package/dist/metrics/context-composition-tracker.js +202 -0
  180. package/dist/metrics/context-composition-tracker.js.map +1 -0
  181. package/dist/metrics/context-tracker.d.ts +78 -0
  182. package/dist/metrics/context-tracker.d.ts.map +1 -0
  183. package/dist/metrics/context-tracker.js +222 -0
  184. package/dist/metrics/context-tracker.js.map +1 -0
  185. package/dist/metrics/context-window-tracker.d.ts +18 -0
  186. package/dist/metrics/context-window-tracker.d.ts.map +1 -0
  187. package/dist/metrics/context-window-tracker.js +35 -0
  188. package/dist/metrics/context-window-tracker.js.map +1 -0
  189. package/dist/metrics/cost-forecast.d.ts +36 -0
  190. package/dist/metrics/cost-forecast.d.ts.map +1 -0
  191. package/dist/metrics/cost-forecast.js +91 -0
  192. package/dist/metrics/cost-forecast.js.map +1 -0
  193. package/dist/metrics/cost-per-outcome.d.ts +102 -0
  194. package/dist/metrics/cost-per-outcome.d.ts.map +1 -0
  195. package/dist/metrics/cost-per-outcome.js +266 -0
  196. package/dist/metrics/cost-per-outcome.js.map +1 -0
  197. package/dist/metrics/cost-tracker.d.ts +78 -0
  198. package/dist/metrics/cost-tracker.d.ts.map +1 -0
  199. package/dist/metrics/cost-tracker.js +169 -0
  200. package/dist/metrics/cost-tracker.js.map +1 -0
  201. package/dist/metrics/decision-tracker.d.ts +49 -0
  202. package/dist/metrics/decision-tracker.d.ts.map +1 -0
  203. package/dist/metrics/decision-tracker.js +161 -0
  204. package/dist/metrics/decision-tracker.js.map +1 -0
  205. package/dist/metrics/efficiency-score.d.ts +80 -0
  206. package/dist/metrics/efficiency-score.d.ts.map +1 -0
  207. package/dist/metrics/efficiency-score.js +219 -0
  208. package/dist/metrics/efficiency-score.js.map +1 -0
  209. package/dist/metrics/git-efficiency-tracker.d.ts +165 -0
  210. package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -0
  211. package/dist/metrics/git-efficiency-tracker.js +1056 -0
  212. package/dist/metrics/git-efficiency-tracker.js.map +1 -0
  213. package/dist/metrics/index.d.ts +26 -0
  214. package/dist/metrics/index.d.ts.map +1 -0
  215. package/dist/metrics/index.js +14 -0
  216. package/dist/metrics/index.js.map +1 -0
  217. package/dist/metrics/instruction-drift-tracker.d.ts +69 -0
  218. package/dist/metrics/instruction-drift-tracker.d.ts.map +1 -0
  219. package/dist/metrics/instruction-drift-tracker.js +213 -0
  220. package/dist/metrics/instruction-drift-tracker.js.map +1 -0
  221. package/dist/metrics/latency-decomposition.d.ts +50 -0
  222. package/dist/metrics/latency-decomposition.d.ts.map +1 -0
  223. package/dist/metrics/latency-decomposition.js +112 -0
  224. package/dist/metrics/latency-decomposition.js.map +1 -0
  225. package/dist/metrics/latency-tracker.d.ts +33 -0
  226. package/dist/metrics/latency-tracker.d.ts.map +1 -0
  227. package/dist/metrics/latency-tracker.js +93 -0
  228. package/dist/metrics/latency-tracker.js.map +1 -0
  229. package/dist/metrics/live-session-registry.d.ts +29 -0
  230. package/dist/metrics/live-session-registry.d.ts.map +1 -0
  231. package/dist/metrics/live-session-registry.js +103 -0
  232. package/dist/metrics/live-session-registry.js.map +1 -0
  233. package/dist/metrics/model-usage-tracker.d.ts +21 -0
  234. package/dist/metrics/model-usage-tracker.d.ts.map +1 -0
  235. package/dist/metrics/model-usage-tracker.js +53 -0
  236. package/dist/metrics/model-usage-tracker.js.map +1 -0
  237. package/dist/metrics/percentile.d.ts +5 -0
  238. package/dist/metrics/percentile.d.ts.map +1 -0
  239. package/dist/metrics/percentile.js +10 -0
  240. package/dist/metrics/percentile.js.map +1 -0
  241. package/dist/metrics/personal-coach.d.ts +47 -0
  242. package/dist/metrics/personal-coach.d.ts.map +1 -0
  243. package/dist/metrics/personal-coach.js +241 -0
  244. package/dist/metrics/personal-coach.js.map +1 -0
  245. package/dist/metrics/prompt-feedback.d.ts +75 -0
  246. package/dist/metrics/prompt-feedback.d.ts.map +1 -0
  247. package/dist/metrics/prompt-feedback.js +286 -0
  248. package/dist/metrics/prompt-feedback.js.map +1 -0
  249. package/dist/metrics/proxy-metrics.d.ts +54 -0
  250. package/dist/metrics/proxy-metrics.d.ts.map +1 -0
  251. package/dist/metrics/proxy-metrics.js +228 -0
  252. package/dist/metrics/proxy-metrics.js.map +1 -0
  253. package/dist/metrics/quality-proxy-tracker.d.ts +51 -0
  254. package/dist/metrics/quality-proxy-tracker.d.ts.map +1 -0
  255. package/dist/metrics/quality-proxy-tracker.js +162 -0
  256. package/dist/metrics/quality-proxy-tracker.js.map +1 -0
  257. package/dist/metrics/recommendation-engine.d.ts +72 -0
  258. package/dist/metrics/recommendation-engine.d.ts.map +1 -0
  259. package/dist/metrics/recommendation-engine.js +207 -0
  260. package/dist/metrics/recommendation-engine.js.map +1 -0
  261. package/dist/metrics/retry-detector.d.ts +43 -0
  262. package/dist/metrics/retry-detector.d.ts.map +1 -0
  263. package/dist/metrics/retry-detector.js +179 -0
  264. package/dist/metrics/retry-detector.js.map +1 -0
  265. package/dist/metrics/session-tracker.d.ts +75 -0
  266. package/dist/metrics/session-tracker.d.ts.map +1 -0
  267. package/dist/metrics/session-tracker.js +249 -0
  268. package/dist/metrics/session-tracker.js.map +1 -0
  269. package/dist/metrics/task-completion-tracker.d.ts +15 -0
  270. package/dist/metrics/task-completion-tracker.d.ts.map +1 -0
  271. package/dist/metrics/task-completion-tracker.js +27 -0
  272. package/dist/metrics/task-completion-tracker.js.map +1 -0
  273. package/dist/metrics/task-detector.d.ts +84 -0
  274. package/dist/metrics/task-detector.d.ts.map +1 -0
  275. package/dist/metrics/task-detector.js +302 -0
  276. package/dist/metrics/task-detector.js.map +1 -0
  277. package/dist/metrics/tool-selection-scorer.d.ts +39 -0
  278. package/dist/metrics/tool-selection-scorer.d.ts.map +1 -0
  279. package/dist/metrics/tool-selection-scorer.js +193 -0
  280. package/dist/metrics/tool-selection-scorer.js.map +1 -0
  281. package/dist/metrics/trend-analyzer.d.ts +92 -0
  282. package/dist/metrics/trend-analyzer.d.ts.map +1 -0
  283. package/dist/metrics/trend-analyzer.js +293 -0
  284. package/dist/metrics/trend-analyzer.js.map +1 -0
  285. package/dist/metrics/turn-cost-attributor.d.ts +41 -0
  286. package/dist/metrics/turn-cost-attributor.d.ts.map +1 -0
  287. package/dist/metrics/turn-cost-attributor.js +118 -0
  288. package/dist/metrics/turn-cost-attributor.js.map +1 -0
  289. package/dist/metrics/turn-tracker.d.ts +49 -0
  290. package/dist/metrics/turn-tracker.d.ts.map +1 -0
  291. package/dist/metrics/turn-tracker.js +192 -0
  292. package/dist/metrics/turn-tracker.js.map +1 -0
  293. package/dist/platforms/amazon-q-adapter.d.ts +10 -0
  294. package/dist/platforms/amazon-q-adapter.d.ts.map +1 -0
  295. package/dist/platforms/amazon-q-adapter.js +75 -0
  296. package/dist/platforms/amazon-q-adapter.js.map +1 -0
  297. package/dist/platforms/claude-code-adapter.d.ts +10 -0
  298. package/dist/platforms/claude-code-adapter.d.ts.map +1 -0
  299. package/dist/platforms/claude-code-adapter.js +48 -0
  300. package/dist/platforms/claude-code-adapter.js.map +1 -0
  301. package/dist/platforms/continue-adapter.d.ts +10 -0
  302. package/dist/platforms/continue-adapter.d.ts.map +1 -0
  303. package/dist/platforms/continue-adapter.js +73 -0
  304. package/dist/platforms/continue-adapter.js.map +1 -0
  305. package/dist/platforms/copilot-adapter.d.ts +37 -0
  306. package/dist/platforms/copilot-adapter.d.ts.map +1 -0
  307. package/dist/platforms/copilot-adapter.js +66 -0
  308. package/dist/platforms/copilot-adapter.js.map +1 -0
  309. package/dist/platforms/cursor-adapter.d.ts +10 -0
  310. package/dist/platforms/cursor-adapter.d.ts.map +1 -0
  311. package/dist/platforms/cursor-adapter.js +60 -0
  312. package/dist/platforms/cursor-adapter.js.map +1 -0
  313. package/dist/platforms/generic-mcp-adapter.d.ts +113 -0
  314. package/dist/platforms/generic-mcp-adapter.d.ts.map +1 -0
  315. package/dist/platforms/generic-mcp-adapter.js +139 -0
  316. package/dist/platforms/generic-mcp-adapter.js.map +1 -0
  317. package/dist/platforms/index.d.ts +15 -0
  318. package/dist/platforms/index.d.ts.map +1 -0
  319. package/dist/platforms/index.js +12 -0
  320. package/dist/platforms/index.js.map +1 -0
  321. package/dist/platforms/platform-registry.d.ts +11 -0
  322. package/dist/platforms/platform-registry.d.ts.map +1 -0
  323. package/dist/platforms/platform-registry.js +54 -0
  324. package/dist/platforms/platform-registry.js.map +1 -0
  325. package/dist/platforms/types.d.ts +36 -0
  326. package/dist/platforms/types.d.ts.map +1 -0
  327. package/dist/platforms/types.js +2 -0
  328. package/dist/platforms/types.js.map +1 -0
  329. package/dist/platforms/windsurf-adapter.d.ts +10 -0
  330. package/dist/platforms/windsurf-adapter.d.ts.map +1 -0
  331. package/dist/platforms/windsurf-adapter.js +63 -0
  332. package/dist/platforms/windsurf-adapter.js.map +1 -0
  333. package/dist/platforms/zed-adapter.d.ts +10 -0
  334. package/dist/platforms/zed-adapter.d.ts.map +1 -0
  335. package/dist/platforms/zed-adapter.js +72 -0
  336. package/dist/platforms/zed-adapter.js.map +1 -0
  337. package/dist/proxy/index.d.ts +7 -0
  338. package/dist/proxy/index.d.ts.map +1 -0
  339. package/dist/proxy/index.js +5 -0
  340. package/dist/proxy/index.js.map +1 -0
  341. package/dist/proxy/otlp-receiver.d.ts +28 -0
  342. package/dist/proxy/otlp-receiver.d.ts.map +1 -0
  343. package/dist/proxy/otlp-receiver.js +319 -0
  344. package/dist/proxy/otlp-receiver.js.map +1 -0
  345. package/dist/proxy/proxy-manager.d.ts +47 -0
  346. package/dist/proxy/proxy-manager.d.ts.map +1 -0
  347. package/dist/proxy/proxy-manager.js +338 -0
  348. package/dist/proxy/proxy-manager.js.map +1 -0
  349. package/dist/proxy/types.d.ts +72 -0
  350. package/dist/proxy/types.d.ts.map +1 -0
  351. package/dist/proxy/types.js +33 -0
  352. package/dist/proxy/types.js.map +1 -0
  353. package/dist/proxy/upstream-http.d.ts +26 -0
  354. package/dist/proxy/upstream-http.d.ts.map +1 -0
  355. package/dist/proxy/upstream-http.js +209 -0
  356. package/dist/proxy/upstream-http.js.map +1 -0
  357. package/dist/proxy/upstream-stdio.d.ts +25 -0
  358. package/dist/proxy/upstream-stdio.d.ts.map +1 -0
  359. package/dist/proxy/upstream-stdio.js +256 -0
  360. package/dist/proxy/upstream-stdio.js.map +1 -0
  361. package/dist/security/audit-trail.d.ts +74 -0
  362. package/dist/security/audit-trail.d.ts.map +1 -0
  363. package/dist/security/audit-trail.js +338 -0
  364. package/dist/security/audit-trail.js.map +1 -0
  365. package/dist/security/index.d.ts +5 -0
  366. package/dist/security/index.d.ts.map +1 -0
  367. package/dist/security/index.js +4 -0
  368. package/dist/security/index.js.map +1 -0
  369. package/dist/security/ssrf.d.ts +2 -0
  370. package/dist/security/ssrf.d.ts.map +1 -0
  371. package/dist/security/ssrf.js +126 -0
  372. package/dist/security/ssrf.js.map +1 -0
  373. package/dist/server.d.ts +14 -0
  374. package/dist/server.d.ts.map +1 -0
  375. package/dist/server.js +117 -0
  376. package/dist/server.js.map +1 -0
  377. package/dist/shared/__test-utils__/log-output.d.ts +49 -0
  378. package/dist/shared/__test-utils__/log-output.d.ts.map +1 -0
  379. package/dist/shared/__test-utils__/log-output.js +38 -0
  380. package/dist/shared/__test-utils__/log-output.js.map +1 -0
  381. package/dist/shared/config.d.ts +56 -0
  382. package/dist/shared/config.d.ts.map +1 -0
  383. package/dist/shared/config.js +290 -0
  384. package/dist/shared/config.js.map +1 -0
  385. package/dist/shared/errors.d.ts +139 -0
  386. package/dist/shared/errors.d.ts.map +1 -0
  387. package/dist/shared/errors.js +406 -0
  388. package/dist/shared/errors.js.map +1 -0
  389. package/dist/shared/events/factory.d.ts +143 -0
  390. package/dist/shared/events/factory.d.ts.map +1 -0
  391. package/dist/shared/events/factory.js +351 -0
  392. package/dist/shared/events/factory.js.map +1 -0
  393. package/dist/shared/events/index.d.ts +6 -0
  394. package/dist/shared/events/index.d.ts.map +1 -0
  395. package/dist/shared/events/index.js +3 -0
  396. package/dist/shared/events/index.js.map +1 -0
  397. package/dist/shared/events/serialize.d.ts +87 -0
  398. package/dist/shared/events/serialize.d.ts.map +1 -0
  399. package/dist/shared/events/serialize.js +510 -0
  400. package/dist/shared/events/serialize.js.map +1 -0
  401. package/dist/shared/events/types.d.ts +139 -0
  402. package/dist/shared/events/types.d.ts.map +1 -0
  403. package/dist/shared/events/types.js +2 -0
  404. package/dist/shared/events/types.js.map +1 -0
  405. package/dist/shared/harvest/event-buffer.d.ts +59 -0
  406. package/dist/shared/harvest/event-buffer.d.ts.map +1 -0
  407. package/dist/shared/harvest/event-buffer.js +100 -0
  408. package/dist/shared/harvest/event-buffer.js.map +1 -0
  409. package/dist/shared/harvest/harvest-scheduler.d.ts +200 -0
  410. package/dist/shared/harvest/harvest-scheduler.d.ts.map +1 -0
  411. package/dist/shared/harvest/harvest-scheduler.js +647 -0
  412. package/dist/shared/harvest/harvest-scheduler.js.map +1 -0
  413. package/dist/shared/harvest/index.d.ts +7 -0
  414. package/dist/shared/harvest/index.d.ts.map +1 -0
  415. package/dist/shared/harvest/index.js +4 -0
  416. package/dist/shared/harvest/index.js.map +1 -0
  417. package/dist/shared/harvest/metric-aggregator.d.ts +115 -0
  418. package/dist/shared/harvest/metric-aggregator.d.ts.map +1 -0
  419. package/dist/shared/harvest/metric-aggregator.js +247 -0
  420. package/dist/shared/harvest/metric-aggregator.js.map +1 -0
  421. package/dist/shared/index.d.ts +22 -0
  422. package/dist/shared/index.d.ts.map +1 -0
  423. package/dist/shared/index.js +13 -0
  424. package/dist/shared/index.js.map +1 -0
  425. package/dist/shared/logger.d.ts +57 -0
  426. package/dist/shared/logger.d.ts.map +1 -0
  427. package/dist/shared/logger.js +166 -0
  428. package/dist/shared/logger.js.map +1 -0
  429. package/dist/shared/pricing-data.d.ts +4 -0
  430. package/dist/shared/pricing-data.d.ts.map +1 -0
  431. package/dist/shared/pricing-data.js +473 -0
  432. package/dist/shared/pricing-data.js.map +1 -0
  433. package/dist/shared/pricing.d.ts +148 -0
  434. package/dist/shared/pricing.d.ts.map +1 -0
  435. package/dist/shared/pricing.js +528 -0
  436. package/dist/shared/pricing.js.map +1 -0
  437. package/dist/shared/redact.d.ts +33 -0
  438. package/dist/shared/redact.d.ts.map +1 -0
  439. package/dist/shared/redact.js +110 -0
  440. package/dist/shared/redact.js.map +1 -0
  441. package/dist/shared/timing.d.ts +96 -0
  442. package/dist/shared/timing.d.ts.map +1 -0
  443. package/dist/shared/timing.js +173 -0
  444. package/dist/shared/timing.js.map +1 -0
  445. package/dist/shared/tokens.d.ts +145 -0
  446. package/dist/shared/tokens.d.ts.map +1 -0
  447. package/dist/shared/tokens.js +492 -0
  448. package/dist/shared/tokens.js.map +1 -0
  449. package/dist/shared/transport/events-api.d.ts +14 -0
  450. package/dist/shared/transport/events-api.d.ts.map +1 -0
  451. package/dist/shared/transport/events-api.js +29 -0
  452. package/dist/shared/transport/events-api.js.map +1 -0
  453. package/dist/shared/transport/http-client.d.ts +49 -0
  454. package/dist/shared/transport/http-client.d.ts.map +1 -0
  455. package/dist/shared/transport/http-client.js +381 -0
  456. package/dist/shared/transport/http-client.js.map +1 -0
  457. package/dist/shared/transport/index.d.ts +10 -0
  458. package/dist/shared/transport/index.d.ts.map +1 -0
  459. package/dist/shared/transport/index.js +6 -0
  460. package/dist/shared/transport/index.js.map +1 -0
  461. package/dist/shared/transport/logs-api.d.ts +29 -0
  462. package/dist/shared/transport/logs-api.d.ts.map +1 -0
  463. package/dist/shared/transport/logs-api.js +40 -0
  464. package/dist/shared/transport/logs-api.js.map +1 -0
  465. package/dist/shared/transport/metric-api.d.ts +9 -0
  466. package/dist/shared/transport/metric-api.d.ts.map +1 -0
  467. package/dist/shared/transport/metric-api.js +39 -0
  468. package/dist/shared/transport/metric-api.js.map +1 -0
  469. package/dist/shared/transport/otlp-event-bridge.d.ts +22 -0
  470. package/dist/shared/transport/otlp-event-bridge.d.ts.map +1 -0
  471. package/dist/shared/transport/otlp-event-bridge.js +50 -0
  472. package/dist/shared/transport/otlp-event-bridge.js.map +1 -0
  473. package/dist/shared/transport/otlp-shared.d.ts +14 -0
  474. package/dist/shared/transport/otlp-shared.d.ts.map +1 -0
  475. package/dist/shared/transport/otlp-shared.js +49 -0
  476. package/dist/shared/transport/otlp-shared.js.map +1 -0
  477. package/dist/shared/transport/otlp-transport.d.ts +58 -0
  478. package/dist/shared/transport/otlp-transport.d.ts.map +1 -0
  479. package/dist/shared/transport/otlp-transport.js +236 -0
  480. package/dist/shared/transport/otlp-transport.js.map +1 -0
  481. package/dist/shared/transport/types.d.ts +129 -0
  482. package/dist/shared/transport/types.d.ts.map +1 -0
  483. package/dist/shared/transport/types.js +2 -0
  484. package/dist/shared/transport/types.js.map +1 -0
  485. package/dist/shared/version.d.ts +2 -0
  486. package/dist/shared/version.d.ts.map +1 -0
  487. package/dist/shared/version.js +2 -0
  488. package/dist/shared/version.js.map +1 -0
  489. package/dist/storage/index.d.ts +7 -0
  490. package/dist/storage/index.d.ts.map +1 -0
  491. package/dist/storage/index.js +4 -0
  492. package/dist/storage/index.js.map +1 -0
  493. package/dist/storage/local-store.d.ts +153 -0
  494. package/dist/storage/local-store.d.ts.map +1 -0
  495. package/dist/storage/local-store.js +719 -0
  496. package/dist/storage/local-store.js.map +1 -0
  497. package/dist/storage/retention.d.ts +2 -0
  498. package/dist/storage/retention.d.ts.map +1 -0
  499. package/dist/storage/retention.js +53 -0
  500. package/dist/storage/retention.js.map +1 -0
  501. package/dist/storage/session-store.d.ts +97 -0
  502. package/dist/storage/session-store.d.ts.map +1 -0
  503. package/dist/storage/session-store.js +391 -0
  504. package/dist/storage/session-store.js.map +1 -0
  505. package/dist/storage/types.d.ts +64 -0
  506. package/dist/storage/types.d.ts.map +1 -0
  507. package/dist/storage/types.js +2 -0
  508. package/dist/storage/types.js.map +1 -0
  509. package/dist/storage/weekly-summary.d.ts +61 -0
  510. package/dist/storage/weekly-summary.d.ts.map +1 -0
  511. package/dist/storage/weekly-summary.js +243 -0
  512. package/dist/storage/weekly-summary.js.map +1 -0
  513. package/dist/tools/analytics-tools.d.ts +101 -0
  514. package/dist/tools/analytics-tools.d.ts.map +1 -0
  515. package/dist/tools/analytics-tools.js +71 -0
  516. package/dist/tools/analytics-tools.js.map +1 -0
  517. package/dist/tools/cost-tools.d.ts +121 -0
  518. package/dist/tools/cost-tools.d.ts.map +1 -0
  519. package/dist/tools/cost-tools.js +174 -0
  520. package/dist/tools/cost-tools.js.map +1 -0
  521. package/dist/tools/cross-session-tools.d.ts +376 -0
  522. package/dist/tools/cross-session-tools.d.ts.map +1 -0
  523. package/dist/tools/cross-session-tools.js +820 -0
  524. package/dist/tools/cross-session-tools.js.map +1 -0
  525. package/dist/tools/extended-analytics-tools.d.ts +164 -0
  526. package/dist/tools/extended-analytics-tools.d.ts.map +1 -0
  527. package/dist/tools/extended-analytics-tools.js +121 -0
  528. package/dist/tools/extended-analytics-tools.js.map +1 -0
  529. package/dist/tools/index.d.ts +7 -0
  530. package/dist/tools/index.d.ts.map +1 -0
  531. package/dist/tools/index.js +4 -0
  532. package/dist/tools/index.js.map +1 -0
  533. package/dist/tools/session-stats.d.ts +162 -0
  534. package/dist/tools/session-stats.d.ts.map +1 -0
  535. package/dist/tools/session-stats.js +1054 -0
  536. package/dist/tools/session-stats.js.map +1 -0
  537. package/dist/tools/workflow-tools.d.ts +126 -0
  538. package/dist/tools/workflow-tools.d.ts.map +1 -0
  539. package/dist/tools/workflow-tools.js +274 -0
  540. package/dist/tools/workflow-tools.js.map +1 -0
  541. package/dist/tracing/mcp-tracer.d.ts +4 -0
  542. package/dist/tracing/mcp-tracer.d.ts.map +1 -0
  543. package/dist/tracing/mcp-tracer.js +14 -0
  544. package/dist/tracing/mcp-tracer.js.map +1 -0
  545. package/dist/tracing/session-span.d.ts +14 -0
  546. package/dist/tracing/session-span.d.ts.map +1 -0
  547. package/dist/tracing/session-span.js +53 -0
  548. package/dist/tracing/session-span.js.map +1 -0
  549. package/dist/tracing/task-span-tracker.d.ts +11 -0
  550. package/dist/tracing/task-span-tracker.d.ts.map +1 -0
  551. package/dist/tracing/task-span-tracker.js +59 -0
  552. package/dist/tracing/task-span-tracker.js.map +1 -0
  553. package/dist/tracing/tool-call-span.d.ts +4 -0
  554. package/dist/tracing/tool-call-span.d.ts.map +1 -0
  555. package/dist/tracing/tool-call-span.js +60 -0
  556. package/dist/tracing/tool-call-span.js.map +1 -0
  557. package/dist/transport/index.d.ts +3 -0
  558. package/dist/transport/index.d.ts.map +1 -0
  559. package/dist/transport/index.js +2 -0
  560. package/dist/transport/index.js.map +1 -0
  561. package/dist/transport/log-ingest.d.ts +42 -0
  562. package/dist/transport/log-ingest.d.ts.map +1 -0
  563. package/dist/transport/log-ingest.js +151 -0
  564. package/dist/transport/log-ingest.js.map +1 -0
  565. package/dist/transport/nr-ingest.d.ts +171 -0
  566. package/dist/transport/nr-ingest.d.ts.map +1 -0
  567. package/dist/transport/nr-ingest.js +659 -0
  568. package/dist/transport/nr-ingest.js.map +1 -0
  569. package/dist/types.d.ts +45 -0
  570. package/dist/types.d.ts.map +1 -0
  571. package/dist/types.js +2 -0
  572. package/dist/types.js.map +1 -0
  573. package/dist/web/assets/index-BrL281N-.css +2 -0
  574. package/dist/web/assets/index-CcaYZzXm.js +42 -0
  575. package/dist/web/favicon.svg +15 -0
  576. package/dist/web/index.html +15 -0
  577. package/examples/local-alert-rules.json +106 -0
  578. package/package.json +129 -1
@@ -0,0 +1,647 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { createLogger } from '../logger.js';
3
+ import { EventBuffer } from './event-buffer.js';
4
+ import { MetricAggregator, snapshotsToNrMetrics } from './metric-aggregator.js';
5
+ const logger = createLogger('harvest');
6
+ /**
7
+ * Generate an 8-hex-char correlation ID for one harvest cycle
8
+ *. Stamped via `logger.child({ harvestId })` so every
9
+ * log line emitted during the cycle — including from `sendEvents*` /
10
+ * `sendMetrics*` helpers — carries the same ID. Operators can pivot on
11
+ * `harvestId` in stderr to trace one cycle through batch send, retry,
12
+ * and overflow logs.
13
+ */
14
+ function newHarvestId() {
15
+ return randomUUID().slice(0, 8);
16
+ }
17
+ const DEFAULT_EVENT_HARVEST_MS = 5_000;
18
+ const DEFAULT_METRIC_HARVEST_MS = 60_000;
19
+ export class HarvestScheduler {
20
+ eventBuffer;
21
+ metricAggregator;
22
+ licenseKey;
23
+ transportOptions;
24
+ sendEventsFn;
25
+ sendMetricsFn;
26
+ eventHarvestIntervalMs;
27
+ metricHarvestIntervalMs;
28
+ otlpEventBridge;
29
+ otlpTransport;
30
+ transport;
31
+ allowProcessExit;
32
+ eventIntervalId = null;
33
+ metricIntervalId = null;
34
+ running = false;
35
+ stopPromise = null;
36
+ // 15 + §4.16 — track in-flight harvests so (a) overlapping
37
+ // interval ticks don't double-fire harvests on the same buffers (a slow
38
+ // network can otherwise produce N concurrent fetches if interval < latency),
39
+ // and (b) stop() can await any harvest already in progress before kicking
40
+ // off the final flush (avoids racing the in-flight one).
41
+ inFlightEventHarvest = null;
42
+ inFlightMetricHarvest = null;
43
+ // Per-transport retry buffers. In 'both' mode, NR and OTLP failures must
44
+ // be tracked independently — otherwise a NR-only failure causes the next
45
+ // harvest to re-send the batch to *both* transports, duplicating on OTLP.
46
+ retryNrEventBatch = [];
47
+ retryOtlpEventBatch = [];
48
+ // retry buffers hold pre-explosion bucket snapshots, not
49
+ // exploded NrMetric[] wire form. On the next harvest the failed snapshots
50
+ // are merged back into the aggregator (via a temporary one, per transport)
51
+ // so that overlapping (name, attributes) keys collapse into a single
52
+ // rolled-up data point with one timestamp instead of two separately
53
+ // exploded sets of count/sum/min/max.
54
+ retryNrMetricSnapshots = [];
55
+ retryOtlpMetricSnapshots = [];
56
+ maxRetryEvents;
57
+ maxRetryMetricSnapshotsCap;
58
+ // Snapshot cap: 500 buckets ≈ 2000 wire metrics post-explosion. Larger
59
+ // effective ceiling than the previous 500-NrMetric cap (~125 buckets) by
60
+ // design — retry pressure is rare and the previous limit was tighter than
61
+ // intended.
62
+ boundBeforeExit;
63
+ constructor(options) {
64
+ this.licenseKey = options.licenseKey;
65
+ this.transportOptions = options.transportOptions;
66
+ this.sendEventsFn = options.sendEventsFn;
67
+ this.sendMetricsFn = options.sendMetricsFn;
68
+ this.eventHarvestIntervalMs = this.validateInterval('eventHarvestIntervalMs', options.eventHarvestIntervalMs ?? DEFAULT_EVENT_HARVEST_MS);
69
+ this.metricHarvestIntervalMs = this.validateInterval('metricHarvestIntervalMs', options.metricHarvestIntervalMs ?? DEFAULT_METRIC_HARVEST_MS);
70
+ this.otlpEventBridge = options.otlpEventBridge;
71
+ this.otlpTransport = options.otlpTransport;
72
+ this.transport = options.transport ?? 'nr-events-api';
73
+ this.allowProcessExit = options.allowProcessExit ?? false;
74
+ // Warn when the transport configuration requires an OTLP component that
75
+ // was not provided — events/metrics would be silently dropped (§HVS4).
76
+ const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
77
+ if (wantOtlp && !this.otlpEventBridge) {
78
+ logger.warn('HarvestScheduler: transport includes otlp but otlpEventBridge is not configured — OTLP events will be silently dropped');
79
+ }
80
+ if (wantOtlp && !this.otlpTransport) {
81
+ logger.warn('HarvestScheduler: transport includes otlp but otlpTransport is not configured — OTLP metrics will be silently dropped');
82
+ }
83
+ this.eventBuffer = new EventBuffer({ maxSize: options.maxEventBufferSize });
84
+ this.metricAggregator = new MetricAggregator();
85
+ // 21: maxRetryEvents defaults to maxEventBufferSize when
86
+ // not specified, preserving prior behavior. Operators that need a
87
+ // deeper retry cap (bursty failures, long downstream outages) can set
88
+ // it independently — peak in-memory event count is then roughly
89
+ // maxEventBufferSize + maxRetryEvents.
90
+ this.maxRetryEvents = options.maxRetryEvents ?? options.maxEventBufferSize ?? 1_000;
91
+ this.maxRetryMetricSnapshotsCap = options.maxRetryMetricSnapshots ?? 500;
92
+ this.boundBeforeExit = () => {
93
+ void this.stop();
94
+ };
95
+ }
96
+ /**
97
+ * Buffer an event for the next harvest tick.
98
+ *
99
+ * Returns `true` when the event was added without evicting another, and
100
+ * `false` when the event buffer was already full and the oldest event
101
+ * was head-dropped. Callers ignoring the return
102
+ * value see the same behavior as before — the per-harvest
103
+ * `nr.ai.dropped_events` self-monitoring metric still counts the drops
104
+ * regardless. The boolean lets producers throttle or surface a custom
105
+ * backpressure metric instead of polling.
106
+ */
107
+ addEvent(event) {
108
+ return this.eventBuffer.add(event);
109
+ }
110
+ /**
111
+ * Record a metric sample.
112
+ *
113
+ * Returns `true` when the sample was accepted into a bucket, and `false`
114
+ * when it was rejected (non-finite value or invalid attribute type) per
115
+ * the §4.8 strict validation contract. Existing
116
+ * callers that ignore the return value see unchanged behavior.
117
+ */
118
+ recordMetric(name, value, attributes = {}) {
119
+ return this.metricAggregator.record(name, value, attributes);
120
+ }
121
+ /**
122
+ * Start periodic harvesting.
123
+ *
124
+ * **Signal handling is the consumer's responsibility.** This library does
125
+ * NOT register `SIGTERM` or `SIGINT` handlers — auto-registering process-level
126
+ * signal handlers from a library is an anti-pattern:
127
+ *
128
+ * 1. The consumer's main process likely has its own `SIGTERM`/`SIGINT`
129
+ * handler. If both fire and the consumer's `process.exit(0)` runs first,
130
+ * our in-flight `stop()` is killed mid-flush and events are lost on
131
+ * graceful K8s rollout.
132
+ * 2. The consumer can't know that *we* attached a handler, so they can't
133
+ * coordinate ordering.
134
+ *
135
+ * To flush cleanly on shutdown, register your own signal handler and
136
+ * `await scheduler.stop()` before exiting:
137
+ *
138
+ * ```ts
139
+ * for (const sig of ['SIGTERM', 'SIGINT'] as const) {
140
+ * process.once(sig, async () => {
141
+ * await scheduler.stop();
142
+ * process.exit(0);
143
+ * });
144
+ * }
145
+ * ```
146
+ *
147
+ * **Process exit semantics.** By default
148
+ * (`allowProcessExit: false`), the harvest intervals keep the Node event
149
+ * loop alive — your process will NOT exit until you call
150
+ * `await scheduler.stop()`. This is intentional: silently dropping
151
+ * buffered events on exit is worse than a process that won't quit.
152
+ * Pass `allowProcessExit: true` to opt into `unref()` + a best-effort
153
+ * `beforeExit` fallback (suitable for short-lived CLI tools where
154
+ * losing the last few buffered events is acceptable).
155
+ */
156
+ start() {
157
+ if (this.running) {
158
+ logger.warn('HarvestScheduler already running');
159
+ return;
160
+ }
161
+ // 19: refuse start() while a previous stop() is still
162
+ // resolving. `running` flips to false at the top of doStop(), so without
163
+ // this guard a fast-cycling consumer could call start() before stop()
164
+ // has finished tearing down intervals / removing the beforeExit
165
+ // listener — the new start would then register a fresh beforeExit
166
+ // listener while the old stop is mid-flight clearing it. Caller must
167
+ // `await scheduler.stop()` before restarting. (`stopPromise` is cleared
168
+ // via `.finally()` in `stop()` once doStop resolves, so a fully-awaited
169
+ // stop unblocks the next start.)
170
+ if (this.stopPromise !== null) {
171
+ logger.warn('HarvestScheduler.start() called while a previous stop() is in flight — refusing. Await stop() before restarting.');
172
+ return;
173
+ }
174
+ this.running = true;
175
+ this.eventIntervalId = setInterval(() => {
176
+ // Re-entrancy guard: if the previous harvest is still running (slow
177
+ // network, big batch), skip this tick rather than fire concurrent
178
+ // fetches. Data isn't lost — the buffer still accumulates and the
179
+ // next clean tick picks it up.
180
+ if (this.inFlightEventHarvest)
181
+ return;
182
+ // .catch prevents unhandled-rejection process crash if the promise
183
+ // rejects unexpectedly before reaching its internal try/catch (§HV3).
184
+ this.harvestEvents().catch((err) => {
185
+ logger.error('Unexpected error in event harvest interval', {
186
+ error: err instanceof Error ? err.message : String(err),
187
+ });
188
+ });
189
+ }, this.eventHarvestIntervalMs);
190
+ this.metricIntervalId = setInterval(() => {
191
+ if (this.inFlightMetricHarvest)
192
+ return;
193
+ this.harvestMetrics().catch((err) => {
194
+ logger.error('Unexpected error in metric harvest interval', {
195
+ error: err instanceof Error ? err.message : String(err),
196
+ });
197
+ });
198
+ }, this.metricHarvestIntervalMs);
199
+ if (this.allowProcessExit) {
200
+ // opt-in path for short-lived CLI tools. unref()'d
201
+ // intervals don't hold the loop open, and beforeExit is registered as
202
+ // a best-effort final-flush attempt. Both are best-effort: Node can
203
+ // exit before the fire-and-forget stop() finishes, so events may be
204
+ // lost. Consumers should still prefer awaiting stop() explicitly.
205
+ this.eventIntervalId.unref();
206
+ this.metricIntervalId.unref();
207
+ process.once('beforeExit', this.boundBeforeExit);
208
+ }
209
+ logger.info('Harvest scheduler started', {
210
+ eventIntervalMs: this.eventHarvestIntervalMs,
211
+ metricIntervalMs: this.metricHarvestIntervalMs,
212
+ allowProcessExit: this.allowProcessExit,
213
+ });
214
+ }
215
+ /**
216
+ * Stop the scheduler and run a final flush.
217
+ *
218
+ * Concurrent calls are coalesced — the first call drives the shutdown,
219
+ * subsequent calls receive the same `Promise`.
220
+ *
221
+ * Order of operations:
222
+ * 1. Clear the harvest intervals.
223
+ * 2. Await any in-flight harvest started by an interval tick.
224
+ * 3. Run a final `harvestEvents()` + `harvestMetrics()`.
225
+ * 4. `flush()` the optional `otlpEventBridge` and `otlpTransport` so
226
+ * pending OTLP batches are drained.
227
+ *
228
+ * **OTel SDK teardown is the consumer's responsibility.**
229
+ * `stop()` deliberately does not call `shutdown()` on `otlpTransport` /
230
+ * `otlpEventBridge` — the scheduler does not own those objects'
231
+ * lifecycles. If you do not call `shutdown()` yourself, you will leak
232
+ * keep-alive HTTP/2 connections, may drop in-flight OTLP batches sitting
233
+ * inside the SDK's batch processors, and SDK timer handles can keep the
234
+ * Node event loop alive past `stop()`. After `await scheduler.stop()`,
235
+ * call:
236
+ *
237
+ * ```ts
238
+ * await otlpTransport.shutdown(); // tracer + meter providers
239
+ * await otlpEventBridge.shutdown(); // logger provider
240
+ * ```
241
+ *
242
+ * Order matters: shut down only *after* `stop()` has resolved, otherwise
243
+ * the final flush above will fail mid-export.
244
+ */
245
+ async stop() {
246
+ if (this.stopPromise)
247
+ return this.stopPromise;
248
+ // Do NOT short-circuit when !this.running — a caller who added events
249
+ // without ever calling start() should still get a final flush (§HVS1).
250
+ // The interval teardown steps in doStop() are all safe no-ops when the
251
+ // intervals were never started.
252
+ // 19: drive doStop to completion through stopPromise so
253
+ // concurrent stop() callers coalesce, then clear stopPromise once it
254
+ // resolves so a subsequent start() can pass the "no in-flight stop"
255
+ // guard. Without the clear, a restart-after-stop sequence would be
256
+ // refused (the §4.19 guard sees a non-null stopPromise from the prior
257
+ // session and treats the next start() as racing an in-flight stop).
258
+ const promise = this.doStop().finally(() => {
259
+ // Only clear if the promise we registered is still the current one —
260
+ // a brand-new stop() may have already overwritten it (unlikely under
261
+ // normal use, but defensive).
262
+ if (this.stopPromise === promise)
263
+ this.stopPromise = null;
264
+ });
265
+ this.stopPromise = promise;
266
+ return promise;
267
+ }
268
+ async doStop() {
269
+ this.running = false;
270
+ if (this.eventIntervalId !== null) {
271
+ clearInterval(this.eventIntervalId);
272
+ this.eventIntervalId = null;
273
+ }
274
+ if (this.metricIntervalId !== null) {
275
+ clearInterval(this.metricIntervalId);
276
+ this.metricIntervalId = null;
277
+ }
278
+ if (this.allowProcessExit) {
279
+ process.removeListener('beforeExit', this.boundBeforeExit);
280
+ }
281
+ // wait for any harvest started by an interval tick
282
+ // to complete before we initiate the final flush. Without this, stop()'s
283
+ // own harvestEvents call could race the in-flight one (the re-entrancy
284
+ // guard would make it a no-op, returning the existing promise — but if
285
+ // we don't await first, we'd queue a second harvest immediately after
286
+ // and lose the chance to flush newly-buffered events from after the
287
+ // in-flight one started).
288
+ if (this.inFlightEventHarvest) {
289
+ await this.inFlightEventHarvest.catch((err) => {
290
+ logger.warn('In-flight event harvest errored during stop()', {
291
+ error: err instanceof Error ? err.message : String(err),
292
+ });
293
+ });
294
+ }
295
+ if (this.inFlightMetricHarvest) {
296
+ await this.inFlightMetricHarvest.catch((err) => {
297
+ logger.warn('In-flight metric harvest errored during stop()', {
298
+ error: err instanceof Error ? err.message : String(err),
299
+ });
300
+ });
301
+ }
302
+ // Final flush of in-memory buffers
303
+ await Promise.all([this.harvestEvents(), this.harvestMetrics()]);
304
+ // Flush OTLP-managed buffers (they live inside the OTel SDK batch
305
+ // processors; otherwise events/metrics already enqueued but not yet
306
+ // exported are dropped on process exit).
307
+ const flushes = [];
308
+ if (this.otlpEventBridge) {
309
+ flushes.push(this.otlpEventBridge.flush().catch((err) => {
310
+ logger.warn('Error flushing OTLP event bridge', {
311
+ error: err instanceof Error ? err.message : String(err),
312
+ });
313
+ }));
314
+ }
315
+ if (this.otlpTransport) {
316
+ flushes.push(this.otlpTransport.flush().catch((err) => {
317
+ logger.warn('Error flushing OTLP transport', {
318
+ error: err instanceof Error ? err.message : String(err),
319
+ });
320
+ }));
321
+ }
322
+ if (flushes.length > 0)
323
+ await Promise.all(flushes);
324
+ logger.info('Harvest scheduler stopped');
325
+ }
326
+ async harvestEvents() {
327
+ // If already in flight, return the same promise — callers (interval
328
+ // ticks, manual invocations, stop's final flush) all wait on the
329
+ // same operation rather than racing.
330
+ if (this.inFlightEventHarvest)
331
+ return this.inFlightEventHarvest;
332
+ this.inFlightEventHarvest = this.doHarvestEvents().finally(() => {
333
+ this.inFlightEventHarvest = null;
334
+ });
335
+ return this.inFlightEventHarvest;
336
+ }
337
+ validateInterval(name, ms) {
338
+ if (!Number.isFinite(ms) || ms < 100) {
339
+ throw new RangeError(`HarvestScheduler: ${name} must be a finite number >= 100ms, got ${String(ms)}`);
340
+ }
341
+ return ms;
342
+ }
343
+ async doHarvestEvents() {
344
+ // 5: scoped child logger stamps `harvestId` on every
345
+ // log line emitted during this cycle (overflow, retry, requeue,
346
+ // OTLP-export failures). Operators searching stderr for one harvest's
347
+ // story have a single pivot.
348
+ const harvestLog = logger.child({ harvestId: newHarvestId(), scope: 'events' });
349
+ const fresh = this.eventBuffer.flush();
350
+ // §4.1: surface event-buffer head-drops as a self-monitoring metric so
351
+ // overflow loss is visible in the consumer's own NR dashboards. Drained
352
+ // once per harvest, paired with a single warn log.
353
+ const dropped = this.eventBuffer.drainDropCount();
354
+ if (dropped > 0) {
355
+ harvestLog.warn('EventBuffer overflow — oldest entries dropped', { dropped });
356
+ this.metricAggregator.record('nr.ai.dropped_events', dropped, {
357
+ source: 'event_buffer',
358
+ });
359
+ }
360
+ // Drain the add counter each cycle so totalAdded represents adds-since-
361
+ // last-flush (§HVS3). Not emitted as a metric today; wired here so a
362
+ // future nr.ai.added_events metric reads the correct per-interval value.
363
+ this.eventBuffer.drainAddCount();
364
+ const wantNr = this.transport === 'nr-events-api' || this.transport === 'both';
365
+ const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
366
+ const sends = [];
367
+ if (wantNr) {
368
+ const nrBatch = this.retryNrEventBatch.length > 0 ? [...this.retryNrEventBatch, ...fresh] : [...fresh];
369
+ this.retryNrEventBatch = [];
370
+ if (nrBatch.length > 0) {
371
+ sends.push(this.sendEventsToNr(nrBatch, harvestLog));
372
+ }
373
+ }
374
+ if (wantOtlp) {
375
+ const otlpBatch = this.retryOtlpEventBatch.length > 0 ? [...this.retryOtlpEventBatch, ...fresh] : [...fresh];
376
+ this.retryOtlpEventBatch = [];
377
+ if (otlpBatch.length > 0) {
378
+ sends.push(this.sendEventsToOtlp(otlpBatch, harvestLog));
379
+ }
380
+ }
381
+ if (sends.length > 0)
382
+ await Promise.all(sends);
383
+ }
384
+ async sendEventsToNr(batch, log = logger) {
385
+ try {
386
+ const result = await this.sendEventsFn(batch, this.licenseKey, this.transportOptions);
387
+ if (!result.success) {
388
+ log.warn('Failed to send events to NR — re-queuing batch for retry', {
389
+ batchSize: batch.length,
390
+ error: result.error,
391
+ });
392
+ this.requeueNrEvents(batch, log);
393
+ }
394
+ else {
395
+ // §4.24 — debug-level success log so operators tailing stderr can
396
+ // distinguish "harvest completed cleanly with N events" from
397
+ // "harvest never ran". Debug (not info) keeps the steady-state
398
+ // happy-path output quiet at default log levels.
399
+ log.debug('Sent events to NR', { batchSize: batch.length });
400
+ }
401
+ }
402
+ catch (err) {
403
+ const message = err instanceof Error ? err.message : String(err);
404
+ log.warn('Unexpected error sending events to NR — re-queuing batch for retry', {
405
+ batchSize: batch.length,
406
+ error: message,
407
+ });
408
+ this.requeueNrEvents(batch, log);
409
+ }
410
+ }
411
+ async sendEventsToOtlp(batch, log = logger) {
412
+ try {
413
+ if (this.otlpEventBridge) {
414
+ this.otlpEventBridge.sendEvents(batch);
415
+ // §4.24 — paired with the NR-side success log. Note: this only
416
+ // confirms enqueue into the OTel BatchLogRecordProcessor, not
417
+ // export — the SDK decides when the wire send actually fires.
418
+ log.debug('Enqueued events to OTLP bridge', { batchSize: batch.length });
419
+ }
420
+ else {
421
+ // Bridge absent: the constructor already warned once (§HVS4). Warn
422
+ // at debug here so repeated harvests are traceable without flooding
423
+ // stderr (§HV2).
424
+ log.debug('OTLP event bridge not configured — batch discarded', {
425
+ batchSize: batch.length,
426
+ });
427
+ }
428
+ }
429
+ catch (err) {
430
+ const message = err instanceof Error ? err.message : String(err);
431
+ log.warn('Error sending events to OTLP — re-queuing batch for retry', {
432
+ batchSize: batch.length,
433
+ error: message,
434
+ });
435
+ this.requeueOtlpEvents(batch, log);
436
+ }
437
+ }
438
+ async harvestMetrics() {
439
+ if (this.inFlightMetricHarvest)
440
+ return this.inFlightMetricHarvest;
441
+ this.inFlightMetricHarvest = this.doHarvestMetrics().finally(() => {
442
+ this.inFlightMetricHarvest = null;
443
+ });
444
+ return this.inFlightMetricHarvest;
445
+ }
446
+ async doHarvestMetrics() {
447
+ // 5: scoped child logger stamps a `harvestId` on every
448
+ // log line emitted during this metric harvest cycle.
449
+ const harvestLog = logger.child({ harvestId: newHarvestId(), scope: 'metrics' });
450
+ // 25 / §4.11 — self-monitoring: drain MetricAggregator's
451
+ // drop counter and emit it as a metric so non-finite-value rejections
452
+ // and invalid-attribute rejections are visible in the consumer's own
453
+ // NR dashboards. Mirrors §4.1's `nr.ai.dropped_events` pattern from
454
+ // doHarvestEvents. Note this is recorded BEFORE harvestSnapshots() so
455
+ // the dropped_metrics gauge itself flows through this same harvest tick
456
+ // (one extra bucket, one extra summary on the wire).
457
+ const droppedMetrics = this.metricAggregator.drainDropCount();
458
+ if (droppedMetrics > 0) {
459
+ harvestLog.warn('MetricAggregator overflow — non-finite or invalid samples dropped', {
460
+ dropped: droppedMetrics,
461
+ });
462
+ this.metricAggregator.record('nr.ai.dropped_metrics', droppedMetrics, {
463
+ source: 'metric_aggregator',
464
+ });
465
+ }
466
+ // drain the aggregator as snapshots, then per
467
+ // transport: merge the previous failed-send snapshots with the fresh
468
+ // ones (so duplicate name+attrs buckets accumulate), then explode to
469
+ // wire form. Each transport gets its own merged set so a NR-only
470
+ // failure doesn't double-send to OTLP (preserves §4.5 semantics).
471
+ const fresh = this.metricAggregator.harvestSnapshots();
472
+ const wantNr = this.transport === 'nr-events-api' || this.transport === 'both';
473
+ const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
474
+ const sends = [];
475
+ if (wantNr) {
476
+ const nrSnapshots = this.mergeSnapshots(this.retryNrMetricSnapshots, fresh);
477
+ this.retryNrMetricSnapshots = [];
478
+ if (nrSnapshots.length > 0) {
479
+ sends.push(this.sendMetricsToNr(snapshotsToNrMetrics(nrSnapshots, this.metricHarvestIntervalMs), nrSnapshots, harvestLog));
480
+ }
481
+ }
482
+ if (wantOtlp) {
483
+ const otlpSnapshots = this.mergeSnapshots(this.retryOtlpMetricSnapshots, fresh);
484
+ this.retryOtlpMetricSnapshots = [];
485
+ if (otlpSnapshots.length > 0) {
486
+ sends.push(this.sendMetricsToOtlp(snapshotsToNrMetrics(otlpSnapshots, this.metricHarvestIntervalMs), otlpSnapshots, harvestLog));
487
+ }
488
+ }
489
+ if (sends.length > 0)
490
+ await Promise.all(sends);
491
+ }
492
+ /**
493
+ * Combine retry snapshots with a fresh harvest's snapshots, accumulating
494
+ * any duplicate (name, attributes) buckets. Returns a new snapshot list
495
+ * with one entry per unique key. Uses a throwaway {@link MetricAggregator}
496
+ * as the merge engine so the bucket-key logic is not duplicated here.
497
+ *
498
+ * without this re-merge, a failed-send retry plus a
499
+ * fresh harvest hitting the same metric+attrs would produce two wire
500
+ * data points with different timestamps, breaking downstream NRQL
501
+ * aggregation that expects one bucket per harvest interval.
502
+ */
503
+ mergeSnapshots(retry, fresh) {
504
+ if (retry.length === 0)
505
+ return [...fresh];
506
+ if (fresh.length === 0)
507
+ return [...retry];
508
+ const merger = new MetricAggregator();
509
+ merger.merge(retry);
510
+ merger.merge(fresh);
511
+ return merger.harvestSnapshots();
512
+ }
513
+ async sendMetricsToNr(batch, snapshots, log = logger) {
514
+ try {
515
+ const result = await this.sendMetricsFn(batch, this.licenseKey, this.transportOptions);
516
+ if (!result.success) {
517
+ log.warn('Failed to send metrics to NR — re-queuing batch for retry', {
518
+ batchSize: batch.length,
519
+ error: result.error,
520
+ });
521
+ this.requeueNrMetrics(snapshots, log);
522
+ }
523
+ else {
524
+ // §4.24 — debug-level success log so operators tailing stderr can
525
+ // distinguish "harvest completed cleanly with N metrics" from
526
+ // "harvest never ran". Includes both the wire batch count and
527
+ // the underlying snapshot count so a future cardinality-explosion
528
+ // signal is visible at debug.
529
+ log.debug('Sent metrics to NR', {
530
+ batchSize: batch.length,
531
+ snapshotCount: snapshots.length,
532
+ });
533
+ }
534
+ }
535
+ catch (err) {
536
+ const message = err instanceof Error ? err.message : String(err);
537
+ log.warn('Unexpected error sending metrics to NR — re-queuing batch for retry', {
538
+ batchSize: batch.length,
539
+ error: message,
540
+ });
541
+ this.requeueNrMetrics(snapshots, log);
542
+ }
543
+ }
544
+ async sendMetricsToOtlp(batch, snapshots, log = logger) {
545
+ try {
546
+ if (this.otlpTransport) {
547
+ await this.otlpTransport.exportMetrics(batch);
548
+ // §4.24 — paired with the NR-side success log.
549
+ log.debug('Sent metrics to OTLP', {
550
+ batchSize: batch.length,
551
+ snapshotCount: snapshots.length,
552
+ });
553
+ }
554
+ else {
555
+ // Transport absent: the constructor already warned once (§HVS4).
556
+ log.debug('OTLP transport not configured — metric batch discarded', {
557
+ batchSize: batch.length,
558
+ });
559
+ }
560
+ }
561
+ catch (err) {
562
+ const message = err instanceof Error ? err.message : String(err);
563
+ // §11.1 — OtlpTransport.exportMetrics throws { code: 'OTLP_BAD_REQUEST' } for
564
+ // HTTP 400 to signal that the payload is permanently malformed and will always
565
+ // fail. Drop the batch instead of requeuing so it does not occupy the retry
566
+ // buffer indefinitely. All other errors are retried normally.
567
+ const isNonRetryable = err instanceof Error && err.code === 'OTLP_BAD_REQUEST';
568
+ if (isNonRetryable) {
569
+ log.error('OTLP metric export rejected (bad request) — dropping batch, will not retry', {
570
+ batchSize: batch.length,
571
+ snapshotCount: snapshots.length,
572
+ error: message,
573
+ });
574
+ }
575
+ else {
576
+ log.warn('Error sending metrics to OTLP — re-queuing batch for retry', {
577
+ batchSize: batch.length,
578
+ error: message,
579
+ });
580
+ this.requeueOtlpMetrics(snapshots, log);
581
+ }
582
+ }
583
+ }
584
+ // NOTE (§HVS5): the self-monitoring metrics recorded below (nr.ai.dropped_events,
585
+ // nr.ai.dropped_metrics) are written to metricAggregator AFTER the current
586
+ // harvest cycle has already drained it via harvestSnapshots(). They will not
587
+ // be sent until the NEXT harvest cycle — operators see overflow counts one
588
+ // cycle late. This is an accepted trade-off; documenting so the lag is not
589
+ // mistaken for a bug.
590
+ requeueNrEvents(batch, log = logger) {
591
+ // for-of push avoids two hazards: (a) the O(n+m) intermediate array from
592
+ // [...old, ...new] (§HVS7), and (b) push(...batch) throwing RangeError when
593
+ // batch.length exceeds the engine's argument-count limit (~65k) (§HV1).
594
+ for (const e of batch)
595
+ this.retryNrEventBatch.push(e);
596
+ if (this.retryNrEventBatch.length > this.maxRetryEvents) {
597
+ const dropped = this.retryNrEventBatch.length - this.maxRetryEvents;
598
+ this.retryNrEventBatch.splice(0, dropped);
599
+ log.warn('NR event retry buffer overflow — oldest entries dropped', { dropped });
600
+ // Surface as self-monitoring metric so NR dashboards show retry-buffer drops (§HS1).
601
+ this.metricAggregator.record('nr.ai.dropped_events', dropped, {
602
+ source: 'retry_buffer',
603
+ transport: 'nr-events-api',
604
+ });
605
+ }
606
+ }
607
+ requeueOtlpEvents(batch, log = logger) {
608
+ for (const e of batch)
609
+ this.retryOtlpEventBatch.push(e);
610
+ if (this.retryOtlpEventBatch.length > this.maxRetryEvents) {
611
+ const dropped = this.retryOtlpEventBatch.length - this.maxRetryEvents;
612
+ this.retryOtlpEventBatch.splice(0, dropped);
613
+ log.warn('OTLP event retry buffer overflow — oldest entries dropped', { dropped });
614
+ this.metricAggregator.record('nr.ai.dropped_events', dropped, {
615
+ source: 'retry_buffer',
616
+ transport: 'otlp',
617
+ });
618
+ }
619
+ }
620
+ requeueNrMetrics(snapshots, log = logger) {
621
+ for (const s of snapshots)
622
+ this.retryNrMetricSnapshots.push(s);
623
+ if (this.retryNrMetricSnapshots.length > this.maxRetryMetricSnapshotsCap) {
624
+ const dropped = this.retryNrMetricSnapshots.length - this.maxRetryMetricSnapshotsCap;
625
+ this.retryNrMetricSnapshots.splice(0, dropped);
626
+ log.warn('NR metric retry buffer overflow — oldest entries dropped', { dropped });
627
+ this.metricAggregator.record('nr.ai.dropped_metrics', dropped, {
628
+ source: 'retry_buffer',
629
+ transport: 'nr-metrics-api',
630
+ });
631
+ }
632
+ }
633
+ requeueOtlpMetrics(snapshots, log = logger) {
634
+ for (const s of snapshots)
635
+ this.retryOtlpMetricSnapshots.push(s);
636
+ if (this.retryOtlpMetricSnapshots.length > this.maxRetryMetricSnapshotsCap) {
637
+ const dropped = this.retryOtlpMetricSnapshots.length - this.maxRetryMetricSnapshotsCap;
638
+ this.retryOtlpMetricSnapshots.splice(0, dropped);
639
+ log.warn('OTLP metric retry buffer overflow — oldest entries dropped', { dropped });
640
+ this.metricAggregator.record('nr.ai.dropped_metrics', dropped, {
641
+ source: 'retry_buffer',
642
+ transport: 'otlp',
643
+ });
644
+ }
645
+ }
646
+ }
647
+ //# sourceMappingURL=harvest-scheduler.js.map