@telora/daemon 0.12.33

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 (473) hide show
  1. package/.env.example +64 -0
  2. package/README.md +229 -0
  3. package/build-info.json +4 -0
  4. package/dist/activity-tracker.d.ts +13 -0
  5. package/dist/activity-tracker.d.ts.map +1 -0
  6. package/dist/activity-tracker.js +19 -0
  7. package/dist/activity-tracker.js.map +1 -0
  8. package/dist/agent-state.d.ts +45 -0
  9. package/dist/agent-state.d.ts.map +1 -0
  10. package/dist/agent-state.js +61 -0
  11. package/dist/agent-state.js.map +1 -0
  12. package/dist/audit-hooks.d.ts +12 -0
  13. package/dist/audit-hooks.d.ts.map +1 -0
  14. package/dist/audit-hooks.js +45 -0
  15. package/dist/audit-hooks.js.map +1 -0
  16. package/dist/auto-update.d.ts +42 -0
  17. package/dist/auto-update.d.ts.map +1 -0
  18. package/dist/auto-update.js +96 -0
  19. package/dist/auto-update.js.map +1 -0
  20. package/dist/branch-status.d.ts +40 -0
  21. package/dist/branch-status.d.ts.map +1 -0
  22. package/dist/branch-status.js +107 -0
  23. package/dist/branch-status.js.map +1 -0
  24. package/dist/completion-detector.d.ts +87 -0
  25. package/dist/completion-detector.d.ts.map +1 -0
  26. package/dist/completion-detector.js +160 -0
  27. package/dist/completion-detector.js.map +1 -0
  28. package/dist/completion-handler.d.ts +48 -0
  29. package/dist/completion-handler.d.ts.map +1 -0
  30. package/dist/completion-handler.js +200 -0
  31. package/dist/completion-handler.js.map +1 -0
  32. package/dist/condition-evaluators.d.ts +31 -0
  33. package/dist/condition-evaluators.d.ts.map +1 -0
  34. package/dist/condition-evaluators.js +416 -0
  35. package/dist/condition-evaluators.js.map +1 -0
  36. package/dist/config.d.ts +55 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +311 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/control-state.d.ts +41 -0
  41. package/dist/control-state.d.ts.map +1 -0
  42. package/dist/control-state.js +204 -0
  43. package/dist/control-state.js.map +1 -0
  44. package/dist/crash-recovery-cleanup.d.ts +21 -0
  45. package/dist/crash-recovery-cleanup.d.ts.map +1 -0
  46. package/dist/crash-recovery-cleanup.js +198 -0
  47. package/dist/crash-recovery-cleanup.js.map +1 -0
  48. package/dist/crash-recovery-scan.d.ts +19 -0
  49. package/dist/crash-recovery-scan.d.ts.map +1 -0
  50. package/dist/crash-recovery-scan.js +145 -0
  51. package/dist/crash-recovery-scan.js.map +1 -0
  52. package/dist/crash-recovery-types.d.ts +54 -0
  53. package/dist/crash-recovery-types.d.ts.map +1 -0
  54. package/dist/crash-recovery-types.js +13 -0
  55. package/dist/crash-recovery-types.js.map +1 -0
  56. package/dist/crash-recovery.d.ts +88 -0
  57. package/dist/crash-recovery.d.ts.map +1 -0
  58. package/dist/crash-recovery.js +448 -0
  59. package/dist/crash-recovery.js.map +1 -0
  60. package/dist/daemon-logs.d.ts +19 -0
  61. package/dist/daemon-logs.d.ts.map +1 -0
  62. package/dist/daemon-logs.js +81 -0
  63. package/dist/daemon-logs.js.map +1 -0
  64. package/dist/daemon-process.d.ts +154 -0
  65. package/dist/daemon-process.d.ts.map +1 -0
  66. package/dist/daemon-process.js +427 -0
  67. package/dist/daemon-process.js.map +1 -0
  68. package/dist/dag-validator.d.ts +52 -0
  69. package/dist/dag-validator.d.ts.map +1 -0
  70. package/dist/dag-validator.js +199 -0
  71. package/dist/dag-validator.js.map +1 -0
  72. package/dist/delivery-guards.d.ts +41 -0
  73. package/dist/delivery-guards.d.ts.map +1 -0
  74. package/dist/delivery-guards.js +195 -0
  75. package/dist/delivery-guards.js.map +1 -0
  76. package/dist/delivery-lifecycle.d.ts +110 -0
  77. package/dist/delivery-lifecycle.d.ts.map +1 -0
  78. package/dist/delivery-lifecycle.js +353 -0
  79. package/dist/delivery-lifecycle.js.map +1 -0
  80. package/dist/delivery-merge.d.ts +17 -0
  81. package/dist/delivery-merge.d.ts.map +1 -0
  82. package/dist/delivery-merge.js +89 -0
  83. package/dist/delivery-merge.js.map +1 -0
  84. package/dist/dependency-resolver.d.ts +77 -0
  85. package/dist/dependency-resolver.d.ts.map +1 -0
  86. package/dist/dependency-resolver.js +337 -0
  87. package/dist/dependency-resolver.js.map +1 -0
  88. package/dist/evaluation-context.d.ts +49 -0
  89. package/dist/evaluation-context.d.ts.map +1 -0
  90. package/dist/evaluation-context.js +98 -0
  91. package/dist/evaluation-context.js.map +1 -0
  92. package/dist/git-activity.d.ts +24 -0
  93. package/dist/git-activity.d.ts.map +1 -0
  94. package/dist/git-activity.js +97 -0
  95. package/dist/git-activity.js.map +1 -0
  96. package/dist/git-branch.d.ts +33 -0
  97. package/dist/git-branch.d.ts.map +1 -0
  98. package/dist/git-branch.js +88 -0
  99. package/dist/git-branch.js.map +1 -0
  100. package/dist/git-integration.d.ts +27 -0
  101. package/dist/git-integration.d.ts.map +1 -0
  102. package/dist/git-integration.js +82 -0
  103. package/dist/git-integration.js.map +1 -0
  104. package/dist/git-merge-helpers.d.ts +48 -0
  105. package/dist/git-merge-helpers.d.ts.map +1 -0
  106. package/dist/git-merge-helpers.js +105 -0
  107. package/dist/git-merge-helpers.js.map +1 -0
  108. package/dist/git-merge-lock.d.ts +67 -0
  109. package/dist/git-merge-lock.d.ts.map +1 -0
  110. package/dist/git-merge-lock.js +157 -0
  111. package/dist/git-merge-lock.js.map +1 -0
  112. package/dist/git-merge-strategies.d.ts +39 -0
  113. package/dist/git-merge-strategies.d.ts.map +1 -0
  114. package/dist/git-merge-strategies.js +127 -0
  115. package/dist/git-merge-strategies.js.map +1 -0
  116. package/dist/git-merge.d.ts +80 -0
  117. package/dist/git-merge.d.ts.map +1 -0
  118. package/dist/git-merge.js +373 -0
  119. package/dist/git-merge.js.map +1 -0
  120. package/dist/git-state-detector.d.ts +24 -0
  121. package/dist/git-state-detector.d.ts.map +1 -0
  122. package/dist/git-state-detector.js +122 -0
  123. package/dist/git-state-detector.js.map +1 -0
  124. package/dist/git-types.d.ts +40 -0
  125. package/dist/git-types.d.ts.map +1 -0
  126. package/dist/git-types.js +23 -0
  127. package/dist/git-types.js.map +1 -0
  128. package/dist/git-utils.d.ts +28 -0
  129. package/dist/git-utils.d.ts.map +1 -0
  130. package/dist/git-utils.js +57 -0
  131. package/dist/git-utils.js.map +1 -0
  132. package/dist/git.d.ts +24 -0
  133. package/dist/git.d.ts.map +1 -0
  134. package/dist/git.js +64 -0
  135. package/dist/git.js.map +1 -0
  136. package/dist/guard-engine.d.ts +19 -0
  137. package/dist/guard-engine.d.ts.map +1 -0
  138. package/dist/guard-engine.js +21 -0
  139. package/dist/guard-engine.js.map +1 -0
  140. package/dist/guard-evaluator.d.ts +47 -0
  141. package/dist/guard-evaluator.d.ts.map +1 -0
  142. package/dist/guard-evaluator.js +193 -0
  143. package/dist/guard-evaluator.js.map +1 -0
  144. package/dist/heartbeat.d.ts +73 -0
  145. package/dist/heartbeat.d.ts.map +1 -0
  146. package/dist/heartbeat.js +306 -0
  147. package/dist/heartbeat.js.map +1 -0
  148. package/dist/index.d.ts +32 -0
  149. package/dist/index.d.ts.map +1 -0
  150. package/dist/index.js +493 -0
  151. package/dist/index.js.map +1 -0
  152. package/dist/listener-auto-advance.d.ts +29 -0
  153. package/dist/listener-auto-advance.d.ts.map +1 -0
  154. package/dist/listener-auto-advance.js +172 -0
  155. package/dist/listener-auto-advance.js.map +1 -0
  156. package/dist/listener-review.d.ts +37 -0
  157. package/dist/listener-review.d.ts.map +1 -0
  158. package/dist/listener-review.js +217 -0
  159. package/dist/listener-review.js.map +1 -0
  160. package/dist/listener.d.ts +57 -0
  161. package/dist/listener.d.ts.map +1 -0
  162. package/dist/listener.js +361 -0
  163. package/dist/listener.js.map +1 -0
  164. package/dist/log-manager.d.ts +18 -0
  165. package/dist/log-manager.d.ts.map +1 -0
  166. package/dist/log-manager.js +18 -0
  167. package/dist/log-manager.js.map +1 -0
  168. package/dist/otlp-log-parser.d.ts +21 -0
  169. package/dist/otlp-log-parser.d.ts.map +1 -0
  170. package/dist/otlp-log-parser.js +143 -0
  171. package/dist/otlp-log-parser.js.map +1 -0
  172. package/dist/otlp-metric-parser.d.ts +20 -0
  173. package/dist/otlp-metric-parser.d.ts.map +1 -0
  174. package/dist/otlp-metric-parser.js +113 -0
  175. package/dist/otlp-metric-parser.js.map +1 -0
  176. package/dist/otlp-port-manager.d.ts +26 -0
  177. package/dist/otlp-port-manager.d.ts.map +1 -0
  178. package/dist/otlp-port-manager.js +130 -0
  179. package/dist/otlp-port-manager.js.map +1 -0
  180. package/dist/otlp-receiver.d.ts +51 -0
  181. package/dist/otlp-receiver.d.ts.map +1 -0
  182. package/dist/otlp-receiver.js +663 -0
  183. package/dist/otlp-receiver.js.map +1 -0
  184. package/dist/otlp-types.d.ts +92 -0
  185. package/dist/otlp-types.d.ts.map +1 -0
  186. package/dist/otlp-types.js +133 -0
  187. package/dist/otlp-types.js.map +1 -0
  188. package/dist/output-monitor.d.ts +33 -0
  189. package/dist/output-monitor.d.ts.map +1 -0
  190. package/dist/output-monitor.js +67 -0
  191. package/dist/output-monitor.js.map +1 -0
  192. package/dist/planning-prompt-builder.d.ts +67 -0
  193. package/dist/planning-prompt-builder.d.ts.map +1 -0
  194. package/dist/planning-prompt-builder.js +515 -0
  195. package/dist/planning-prompt-builder.js.map +1 -0
  196. package/dist/prompt-builder.d.ts +14 -0
  197. package/dist/prompt-builder.d.ts.map +1 -0
  198. package/dist/prompt-builder.js +174 -0
  199. package/dist/prompt-builder.js.map +1 -0
  200. package/dist/qa-crash-recovery.d.ts +77 -0
  201. package/dist/qa-crash-recovery.d.ts.map +1 -0
  202. package/dist/qa-crash-recovery.js +243 -0
  203. package/dist/qa-crash-recovery.js.map +1 -0
  204. package/dist/qa-dev-server.d.ts +73 -0
  205. package/dist/qa-dev-server.d.ts.map +1 -0
  206. package/dist/qa-dev-server.js +279 -0
  207. package/dist/qa-dev-server.js.map +1 -0
  208. package/dist/qa-orchestrator.d.ts +79 -0
  209. package/dist/qa-orchestrator.d.ts.map +1 -0
  210. package/dist/qa-orchestrator.js +349 -0
  211. package/dist/qa-orchestrator.js.map +1 -0
  212. package/dist/qa-port-allocator.d.ts +34 -0
  213. package/dist/qa-port-allocator.d.ts.map +1 -0
  214. package/dist/qa-port-allocator.js +75 -0
  215. package/dist/qa-port-allocator.js.map +1 -0
  216. package/dist/qa-provisioner.d.ts +33 -0
  217. package/dist/qa-provisioner.d.ts.map +1 -0
  218. package/dist/qa-provisioner.js +141 -0
  219. package/dist/qa-provisioner.js.map +1 -0
  220. package/dist/qa-state.d.ts +93 -0
  221. package/dist/qa-state.d.ts.map +1 -0
  222. package/dist/qa-state.js +74 -0
  223. package/dist/qa-state.js.map +1 -0
  224. package/dist/queries/control-state.d.ts +25 -0
  225. package/dist/queries/control-state.d.ts.map +1 -0
  226. package/dist/queries/control-state.js +34 -0
  227. package/dist/queries/control-state.js.map +1 -0
  228. package/dist/queries/daemon-connection.d.ts +25 -0
  229. package/dist/queries/daemon-connection.d.ts.map +1 -0
  230. package/dist/queries/daemon-connection.js +28 -0
  231. package/dist/queries/daemon-connection.js.map +1 -0
  232. package/dist/queries/deliveries.d.ts +100 -0
  233. package/dist/queries/deliveries.d.ts.map +1 -0
  234. package/dist/queries/deliveries.js +184 -0
  235. package/dist/queries/deliveries.js.map +1 -0
  236. package/dist/queries/git-activity.d.ts +20 -0
  237. package/dist/queries/git-activity.d.ts.map +1 -0
  238. package/dist/queries/git-activity.js +22 -0
  239. package/dist/queries/git-activity.js.map +1 -0
  240. package/dist/queries/guards.d.ts +47 -0
  241. package/dist/queries/guards.d.ts.map +1 -0
  242. package/dist/queries/guards.js +138 -0
  243. package/dist/queries/guards.js.map +1 -0
  244. package/dist/queries/index.d.ts +19 -0
  245. package/dist/queries/index.d.ts.map +1 -0
  246. package/dist/queries/index.js +17 -0
  247. package/dist/queries/index.js.map +1 -0
  248. package/dist/queries/issues.d.ts +41 -0
  249. package/dist/queries/issues.d.ts.map +1 -0
  250. package/dist/queries/issues.js +67 -0
  251. package/dist/queries/issues.js.map +1 -0
  252. package/dist/queries/qa.d.ts +79 -0
  253. package/dist/queries/qa.d.ts.map +1 -0
  254. package/dist/queries/qa.js +85 -0
  255. package/dist/queries/qa.js.map +1 -0
  256. package/dist/queries/roles.d.ts +13 -0
  257. package/dist/queries/roles.d.ts.map +1 -0
  258. package/dist/queries/roles.js +39 -0
  259. package/dist/queries/roles.js.map +1 -0
  260. package/dist/queries/schemas.d.ts +777 -0
  261. package/dist/queries/schemas.d.ts.map +1 -0
  262. package/dist/queries/schemas.js +391 -0
  263. package/dist/queries/schemas.js.map +1 -0
  264. package/dist/queries/sessions.d.ts +64 -0
  265. package/dist/queries/sessions.d.ts.map +1 -0
  266. package/dist/queries/sessions.js +100 -0
  267. package/dist/queries/sessions.js.map +1 -0
  268. package/dist/queries/shared.d.ts +61 -0
  269. package/dist/queries/shared.d.ts.map +1 -0
  270. package/dist/queries/shared.js +187 -0
  271. package/dist/queries/shared.js.map +1 -0
  272. package/dist/queries/strategies.d.ts +69 -0
  273. package/dist/queries/strategies.d.ts.map +1 -0
  274. package/dist/queries/strategies.js +80 -0
  275. package/dist/queries/strategies.js.map +1 -0
  276. package/dist/queries/workflows.d.ts +17 -0
  277. package/dist/queries/workflows.d.ts.map +1 -0
  278. package/dist/queries/workflows.js +49 -0
  279. package/dist/queries/workflows.js.map +1 -0
  280. package/dist/queries/worktrees.d.ts +38 -0
  281. package/dist/queries/worktrees.d.ts.map +1 -0
  282. package/dist/queries/worktrees.js +37 -0
  283. package/dist/queries/worktrees.js.map +1 -0
  284. package/dist/self-update.d.ts +94 -0
  285. package/dist/self-update.d.ts.map +1 -0
  286. package/dist/self-update.js +438 -0
  287. package/dist/self-update.js.map +1 -0
  288. package/dist/session-lifecycle.d.ts +77 -0
  289. package/dist/session-lifecycle.d.ts.map +1 -0
  290. package/dist/session-lifecycle.js +379 -0
  291. package/dist/session-lifecycle.js.map +1 -0
  292. package/dist/shutdown-state.d.ts +17 -0
  293. package/dist/shutdown-state.d.ts.map +1 -0
  294. package/dist/shutdown-state.js +22 -0
  295. package/dist/shutdown-state.js.map +1 -0
  296. package/dist/spawn-cooldown.d.ts +14 -0
  297. package/dist/spawn-cooldown.d.ts.map +1 -0
  298. package/dist/spawn-cooldown.js +34 -0
  299. package/dist/spawn-cooldown.js.map +1 -0
  300. package/dist/spawn-environment.d.ts +35 -0
  301. package/dist/spawn-environment.d.ts.map +1 -0
  302. package/dist/spawn-environment.js +48 -0
  303. package/dist/spawn-environment.js.map +1 -0
  304. package/dist/spawner-liveness.d.ts +23 -0
  305. package/dist/spawner-liveness.d.ts.map +1 -0
  306. package/dist/spawner-liveness.js +99 -0
  307. package/dist/spawner-liveness.js.map +1 -0
  308. package/dist/spawner-resolution.d.ts +27 -0
  309. package/dist/spawner-resolution.d.ts.map +1 -0
  310. package/dist/spawner-resolution.js +99 -0
  311. package/dist/spawner-resolution.js.map +1 -0
  312. package/dist/spawner-timeout.d.ts +32 -0
  313. package/dist/spawner-timeout.d.ts.map +1 -0
  314. package/dist/spawner-timeout.js +124 -0
  315. package/dist/spawner-timeout.js.map +1 -0
  316. package/dist/spawner.d.ts +77 -0
  317. package/dist/spawner.d.ts.map +1 -0
  318. package/dist/spawner.js +734 -0
  319. package/dist/spawner.js.map +1 -0
  320. package/dist/strategy-completion.d.ts +110 -0
  321. package/dist/strategy-completion.d.ts.map +1 -0
  322. package/dist/strategy-completion.js +434 -0
  323. package/dist/strategy-completion.js.map +1 -0
  324. package/dist/strategy-engine.d.ts +47 -0
  325. package/dist/strategy-engine.d.ts.map +1 -0
  326. package/dist/strategy-engine.js +419 -0
  327. package/dist/strategy-engine.js.map +1 -0
  328. package/dist/strategy-executor.d.ts +93 -0
  329. package/dist/strategy-executor.d.ts.map +1 -0
  330. package/dist/strategy-executor.js +775 -0
  331. package/dist/strategy-executor.js.map +1 -0
  332. package/dist/strategy-lifecycle.d.ts +61 -0
  333. package/dist/strategy-lifecycle.d.ts.map +1 -0
  334. package/dist/strategy-lifecycle.js +516 -0
  335. package/dist/strategy-lifecycle.js.map +1 -0
  336. package/dist/strategy-merge.d.ts +72 -0
  337. package/dist/strategy-merge.d.ts.map +1 -0
  338. package/dist/strategy-merge.js +371 -0
  339. package/dist/strategy-merge.js.map +1 -0
  340. package/dist/strategy-prompt-builder.d.ts +62 -0
  341. package/dist/strategy-prompt-builder.d.ts.map +1 -0
  342. package/dist/strategy-prompt-builder.js +538 -0
  343. package/dist/strategy-prompt-builder.js.map +1 -0
  344. package/dist/strategy-provisioning.d.ts +16 -0
  345. package/dist/strategy-provisioning.d.ts.map +1 -0
  346. package/dist/strategy-provisioning.js +119 -0
  347. package/dist/strategy-provisioning.js.map +1 -0
  348. package/dist/strategy-team-state.d.ts +24 -0
  349. package/dist/strategy-team-state.d.ts.map +1 -0
  350. package/dist/strategy-team-state.js +43 -0
  351. package/dist/strategy-team-state.js.map +1 -0
  352. package/dist/strategy-teardown.d.ts +24 -0
  353. package/dist/strategy-teardown.d.ts.map +1 -0
  354. package/dist/strategy-teardown.js +158 -0
  355. package/dist/strategy-teardown.js.map +1 -0
  356. package/dist/strategy-worktree-state.d.ts +47 -0
  357. package/dist/strategy-worktree-state.d.ts.map +1 -0
  358. package/dist/strategy-worktree-state.js +104 -0
  359. package/dist/strategy-worktree-state.js.map +1 -0
  360. package/dist/supabase.d.ts +36 -0
  361. package/dist/supabase.d.ts.map +1 -0
  362. package/dist/supabase.js +50 -0
  363. package/dist/supabase.js.map +1 -0
  364. package/dist/task-converter.d.ts +61 -0
  365. package/dist/task-converter.d.ts.map +1 -0
  366. package/dist/task-converter.js +286 -0
  367. package/dist/task-converter.js.map +1 -0
  368. package/dist/task-dag-builder.d.ts +14 -0
  369. package/dist/task-dag-builder.d.ts.map +1 -0
  370. package/dist/task-dag-builder.js +17 -0
  371. package/dist/task-dag-builder.js.map +1 -0
  372. package/dist/team-prompt-base.d.ts +114 -0
  373. package/dist/team-prompt-base.d.ts.map +1 -0
  374. package/dist/team-prompt-base.js +531 -0
  375. package/dist/team-prompt-base.js.map +1 -0
  376. package/dist/team-prompt-variants.d.ts +27 -0
  377. package/dist/team-prompt-variants.d.ts.map +1 -0
  378. package/dist/team-prompt-variants.js +134 -0
  379. package/dist/team-prompt-variants.js.map +1 -0
  380. package/dist/team-spawner.d.ts +50 -0
  381. package/dist/team-spawner.d.ts.map +1 -0
  382. package/dist/team-spawner.js +410 -0
  383. package/dist/team-spawner.js.map +1 -0
  384. package/dist/telemetry-writer.d.ts +66 -0
  385. package/dist/telemetry-writer.d.ts.map +1 -0
  386. package/dist/telemetry-writer.js +96 -0
  387. package/dist/telemetry-writer.js.map +1 -0
  388. package/dist/trigger-executor.d.ts +56 -0
  389. package/dist/trigger-executor.d.ts.map +1 -0
  390. package/dist/trigger-executor.js +313 -0
  391. package/dist/trigger-executor.js.map +1 -0
  392. package/dist/types/config.d.ts +60 -0
  393. package/dist/types/config.d.ts.map +1 -0
  394. package/dist/types/config.js +5 -0
  395. package/dist/types/config.js.map +1 -0
  396. package/dist/types/dag.d.ts +53 -0
  397. package/dist/types/dag.d.ts.map +1 -0
  398. package/dist/types/dag.js +5 -0
  399. package/dist/types/dag.js.map +1 -0
  400. package/dist/types/delivery.d.ts +71 -0
  401. package/dist/types/delivery.d.ts.map +1 -0
  402. package/dist/types/delivery.js +5 -0
  403. package/dist/types/delivery.js.map +1 -0
  404. package/dist/types/index.d.ts +15 -0
  405. package/dist/types/index.d.ts.map +1 -0
  406. package/dist/types/index.js +15 -0
  407. package/dist/types/index.js.map +1 -0
  408. package/dist/types/issue.d.ts +22 -0
  409. package/dist/types/issue.d.ts.map +1 -0
  410. package/dist/types/issue.js +5 -0
  411. package/dist/types/issue.js.map +1 -0
  412. package/dist/types/merge.d.ts +28 -0
  413. package/dist/types/merge.d.ts.map +1 -0
  414. package/dist/types/merge.js +5 -0
  415. package/dist/types/merge.js.map +1 -0
  416. package/dist/types/session.d.ts +98 -0
  417. package/dist/types/session.d.ts.map +1 -0
  418. package/dist/types/session.js +5 -0
  419. package/dist/types/session.js.map +1 -0
  420. package/dist/types/strategy.d.ts +175 -0
  421. package/dist/types/strategy.d.ts.map +1 -0
  422. package/dist/types/strategy.js +5 -0
  423. package/dist/types/strategy.js.map +1 -0
  424. package/dist/types/workflow.d.ts +34 -0
  425. package/dist/types/workflow.d.ts.map +1 -0
  426. package/dist/types/workflow.js +9 -0
  427. package/dist/types/workflow.js.map +1 -0
  428. package/dist/types.d.ts +9 -0
  429. package/dist/types.d.ts.map +1 -0
  430. package/dist/types.js +9 -0
  431. package/dist/types.js.map +1 -0
  432. package/dist/unified-init.d.ts +16 -0
  433. package/dist/unified-init.d.ts.map +1 -0
  434. package/dist/unified-init.js +183 -0
  435. package/dist/unified-init.js.map +1 -0
  436. package/dist/unified-shell-config.d.ts +34 -0
  437. package/dist/unified-shell-config.d.ts.map +1 -0
  438. package/dist/unified-shell-config.js +238 -0
  439. package/dist/unified-shell-config.js.map +1 -0
  440. package/dist/unified-shell-status.d.ts +15 -0
  441. package/dist/unified-shell-status.d.ts.map +1 -0
  442. package/dist/unified-shell-status.js +100 -0
  443. package/dist/unified-shell-status.js.map +1 -0
  444. package/dist/unified-shell.d.ts +50 -0
  445. package/dist/unified-shell.d.ts.map +1 -0
  446. package/dist/unified-shell.js +682 -0
  447. package/dist/unified-shell.js.map +1 -0
  448. package/dist/version-check.d.ts +19 -0
  449. package/dist/version-check.d.ts.map +1 -0
  450. package/dist/version-check.js +67 -0
  451. package/dist/version-check.js.map +1 -0
  452. package/dist/workflow-engine.d.ts +95 -0
  453. package/dist/workflow-engine.d.ts.map +1 -0
  454. package/dist/workflow-engine.js +165 -0
  455. package/dist/workflow-engine.js.map +1 -0
  456. package/dist/worktree-merge.d.ts +23 -0
  457. package/dist/worktree-merge.d.ts.map +1 -0
  458. package/dist/worktree-merge.js +57 -0
  459. package/dist/worktree-merge.js.map +1 -0
  460. package/dist/worktree-safety.d.ts +48 -0
  461. package/dist/worktree-safety.d.ts.map +1 -0
  462. package/dist/worktree-safety.js +113 -0
  463. package/dist/worktree-safety.js.map +1 -0
  464. package/dist/worktree-strategy.d.ts +69 -0
  465. package/dist/worktree-strategy.d.ts.map +1 -0
  466. package/dist/worktree-strategy.js +214 -0
  467. package/dist/worktree-strategy.js.map +1 -0
  468. package/dist/worktree.d.ts +159 -0
  469. package/dist/worktree.d.ts.map +1 -0
  470. package/dist/worktree.js +512 -0
  471. package/dist/worktree.js.map +1 -0
  472. package/package.json +76 -0
  473. package/scripts/telora-daemon-wrapper.sh +31 -0
@@ -0,0 +1,663 @@
1
+ /**
2
+ * OTLP HTTP Receiver for agent telemetry.
3
+ *
4
+ * Lightweight HTTP server accepting OpenTelemetry JSON payloads:
5
+ * - POST /v1/metrics -- OTLP metrics (token counts, cost, tool calls)
6
+ * - POST /v1/logs -- OTLP logs (tool call events, errors)
7
+ *
8
+ * Extracts Telora resource attributes (telora.org_id, telora.strategy_id,
9
+ * telora.delivery_id) for entity correlation and forwards parsed data
10
+ * to the telemetry writer for batched Postgres inserts.
11
+ *
12
+ * Runs on a configurable port (default 4318) alongside the daemon process.
13
+ */
14
+ import { createServer } from 'node:http';
15
+ import { connect as netConnect } from 'node:net';
16
+ import { execFileSync } from 'node:child_process';
17
+ import { readFileSync, existsSync } from 'node:fs';
18
+ import { bufferMetric, bufferEvent, } from './telemetry-writer.js';
19
+ function extractResourceAttributes(resource) {
20
+ const attrs = {
21
+ orgId: null,
22
+ strategyId: null,
23
+ deliveryId: null,
24
+ sessionId: null,
25
+ instanceId: null,
26
+ workUnitId: null,
27
+ specId: null,
28
+ };
29
+ if (!resource?.attributes)
30
+ return attrs;
31
+ for (const kv of resource.attributes) {
32
+ const val = kv.value.stringValue;
33
+ if (!val)
34
+ continue;
35
+ switch (kv.key) {
36
+ case 'telora.org_id':
37
+ attrs.orgId = val;
38
+ break;
39
+ case 'telora.strategy_id':
40
+ attrs.strategyId = val;
41
+ break;
42
+ case 'telora.delivery_id':
43
+ attrs.deliveryId = val;
44
+ break;
45
+ case 'telora.session_id':
46
+ attrs.sessionId = val;
47
+ break;
48
+ case 'telora.instance_id':
49
+ attrs.instanceId = val;
50
+ break;
51
+ case 'telora.work_unit_id':
52
+ attrs.workUnitId = val;
53
+ break;
54
+ case 'telora.spec_id':
55
+ attrs.specId = val;
56
+ break;
57
+ }
58
+ }
59
+ return attrs;
60
+ }
61
+ /**
62
+ * Build factory-specific metadata from resource attributes.
63
+ * Returns null when no factory attributes are present.
64
+ */
65
+ function buildFactoryMetadata(attrs) {
66
+ const meta = {};
67
+ if (attrs.instanceId)
68
+ meta.factory_instance_id = attrs.instanceId;
69
+ if (attrs.workUnitId)
70
+ meta.factory_work_unit_id = attrs.workUnitId;
71
+ if (attrs.specId)
72
+ meta.factory_spec_id = attrs.specId;
73
+ return Object.keys(meta).length > 0 ? meta : null;
74
+ }
75
+ // ── Metric parsing ───────────────────────────────────────────────────
76
+ function getDataPointValue(dp) {
77
+ if (dp.asDouble !== undefined)
78
+ return dp.asDouble;
79
+ if (dp.asInt !== undefined)
80
+ return typeof dp.asInt === 'string' ? parseInt(dp.asInt, 10) : dp.asInt;
81
+ return 0;
82
+ }
83
+ function nanoToIso(nanos) {
84
+ if (!nanos)
85
+ return new Date().toISOString();
86
+ const ms = Math.floor(parseInt(nanos, 10) / 1_000_000);
87
+ return new Date(ms).toISOString();
88
+ }
89
+ /**
90
+ * Get a string attribute from an OTLP data point's attributes array.
91
+ */
92
+ function getDataPointAttribute(dp, key) {
93
+ if (!dp.attributes)
94
+ return null;
95
+ const kv = dp.attributes.find(a => a.key === key);
96
+ if (!kv)
97
+ return null;
98
+ return kv.value.stringValue ?? null;
99
+ }
100
+ /**
101
+ * Parse OTLP metrics payload and buffer metric rows.
102
+ *
103
+ * Claude Code emits these metrics (per official OTel schema):
104
+ * - claude_code.token.usage (Counter, attrs: type=input|output|cacheRead|cacheCreation, model)
105
+ * - claude_code.cost.usage (Counter, attr: model, unit: USD)
106
+ * - claude_code.active_time.total (Counter, unit: seconds)
107
+ * - claude_code.session.count, claude_code.lines_of_code.count, etc.
108
+ *
109
+ * We aggregate per-resource into a single TelemetryMetricRow.
110
+ */
111
+ function processMetrics(payload, fallbackOrgId) {
112
+ if (!payload.resourceMetrics)
113
+ return;
114
+ for (const rm of payload.resourceMetrics) {
115
+ const attrs = extractResourceAttributes(rm.resource);
116
+ const orgId = attrs.orgId ?? fallbackOrgId;
117
+ // Collect all metric values into a single row per resource
118
+ const factoryMeta = buildFactoryMetadata(attrs);
119
+ const row = {
120
+ organization_id: orgId,
121
+ strategy_id: attrs.strategyId,
122
+ delivery_id: attrs.deliveryId,
123
+ session_id: attrs.sessionId,
124
+ input_tokens: 0,
125
+ output_tokens: 0,
126
+ cache_read_tokens: 0,
127
+ cache_creation_tokens: 0,
128
+ cost_usd: 0,
129
+ active_time_ms: 0,
130
+ tool_call_count: 0,
131
+ tool_error_count: 0,
132
+ model: null,
133
+ metadata: factoryMeta,
134
+ period_start: new Date().toISOString(),
135
+ period_end: new Date().toISOString(),
136
+ };
137
+ let hasData = false;
138
+ let earliestNano;
139
+ let latestNano;
140
+ for (const sm of rm.scopeMetrics ?? []) {
141
+ for (const metric of sm.metrics ?? []) {
142
+ const dataPoints = metric.sum?.dataPoints ?? metric.gauge?.dataPoints ?? [];
143
+ for (const dp of dataPoints) {
144
+ const value = getDataPointValue(dp);
145
+ // Track time range
146
+ if (dp.startTimeUnixNano && (!earliestNano || dp.startTimeUnixNano < earliestNano)) {
147
+ earliestNano = dp.startTimeUnixNano;
148
+ }
149
+ if (dp.timeUnixNano && (!latestNano || dp.timeUnixNano > latestNano)) {
150
+ latestNano = dp.timeUnixNano;
151
+ }
152
+ switch (metric.name) {
153
+ case 'claude_code.token.usage': {
154
+ // Token usage has a `type` attribute: input, output, cacheRead, cacheCreation
155
+ const tokenType = getDataPointAttribute(dp, 'type');
156
+ // Extract model from first data point that has it
157
+ if (!row.model) {
158
+ row.model = getDataPointAttribute(dp, 'model');
159
+ }
160
+ switch (tokenType) {
161
+ case 'input':
162
+ row.input_tokens = value;
163
+ hasData = true;
164
+ break;
165
+ case 'output':
166
+ row.output_tokens = value;
167
+ hasData = true;
168
+ break;
169
+ case 'cacheRead':
170
+ row.cache_read_tokens = value;
171
+ hasData = true;
172
+ break;
173
+ case 'cacheCreation':
174
+ row.cache_creation_tokens = value;
175
+ hasData = true;
176
+ break;
177
+ }
178
+ break;
179
+ }
180
+ case 'claude_code.cost.usage':
181
+ row.cost_usd = value;
182
+ hasData = true;
183
+ break;
184
+ case 'claude_code.active_time.total':
185
+ // Claude emits seconds, DB stores milliseconds
186
+ row.active_time_ms = Math.round(value * 1000);
187
+ hasData = true;
188
+ break;
189
+ // code_edit_tool.decision tracks Edit/Write/NotebookEdit permission
190
+ // decisions (accept/reject), not general tool calls. Tool call counts
191
+ // are derived from tool_result log events in processLogs() instead.
192
+ // Unknown metrics (session.count, lines_of_code.count, etc.) are silently ignored
193
+ }
194
+ }
195
+ }
196
+ }
197
+ if (hasData) {
198
+ row.period_start = nanoToIso(earliestNano);
199
+ row.period_end = nanoToIso(latestNano);
200
+ bufferMetric(row);
201
+ }
202
+ }
203
+ }
204
+ // ── Log/event parsing ────────────────────────────────────────────────
205
+ function getLogAttribute(attrs, key) {
206
+ if (!attrs)
207
+ return null;
208
+ const kv = attrs.find(a => a.key === key);
209
+ if (!kv)
210
+ return null;
211
+ return kv.value.stringValue ?? (kv.value.intValue !== undefined ? String(kv.value.intValue) : null);
212
+ }
213
+ function getLogAttributeBool(attrs, key) {
214
+ if (!attrs)
215
+ return null;
216
+ const kv = attrs.find(a => a.key === key);
217
+ if (!kv)
218
+ return null;
219
+ if (kv.value.boolValue !== undefined)
220
+ return kv.value.boolValue;
221
+ if (kv.value.stringValue !== undefined)
222
+ return kv.value.stringValue === 'true';
223
+ return null;
224
+ }
225
+ function getLogAttributeInt(attrs, key) {
226
+ if (!attrs)
227
+ return null;
228
+ const kv = attrs.find(a => a.key === key);
229
+ if (!kv)
230
+ return null;
231
+ if (kv.value.intValue !== undefined)
232
+ return typeof kv.value.intValue === 'string' ? parseInt(kv.value.intValue, 10) : kv.value.intValue;
233
+ if (kv.value.doubleValue !== undefined)
234
+ return Math.round(kv.value.doubleValue);
235
+ return null;
236
+ }
237
+ function getLogAttributeDouble(attrs, key) {
238
+ if (!attrs)
239
+ return null;
240
+ const kv = attrs.find(a => a.key === key);
241
+ if (!kv)
242
+ return null;
243
+ if (kv.value.doubleValue !== undefined)
244
+ return kv.value.doubleValue;
245
+ if (kv.value.intValue !== undefined)
246
+ return typeof kv.value.intValue === 'string' ? parseFloat(kv.value.intValue) : kv.value.intValue;
247
+ return null;
248
+ }
249
+ /**
250
+ * Parse OTLP logs payload and buffer event rows.
251
+ *
252
+ * Claude Code emits events via the OTLP logs protocol with an `event.name`
253
+ * attribute identifying the event type. Names may be prefixed with `claude_code.`
254
+ * or bare -- we normalize to bare names before matching:
255
+ * - tool_result: tool_name, success, duration_ms, error
256
+ * - api_request: model, cost_usd, duration_ms, token counts
257
+ * - api_error: model, error, status_code, duration_ms
258
+ * - user_prompt: prompt_length
259
+ * - tool_decision: tool_name, decision, source
260
+ */
261
+ function processLogs(payload, fallbackOrgId) {
262
+ if (!payload.resourceLogs)
263
+ return;
264
+ for (const rl of payload.resourceLogs) {
265
+ const attrs = extractResourceAttributes(rl.resource);
266
+ const orgId = attrs.orgId ?? fallbackOrgId;
267
+ const factoryMeta = buildFactoryMetadata(attrs);
268
+ // Track tool call/error counts per resource for metric aggregation
269
+ let toolCallCount = 0;
270
+ let toolErrorCount = 0;
271
+ let earliestNano;
272
+ let latestNano;
273
+ for (const sl of rl.scopeLogs ?? []) {
274
+ for (const lr of sl.logRecords ?? []) {
275
+ // Claude Code uses `event.name` attribute for event identification.
276
+ // Fall back to body.stringValue (some OTel exporters put event name there).
277
+ const rawEventName = getLogAttribute(lr.attributes, 'event.name')
278
+ ?? lr.body?.stringValue
279
+ ?? lr.severityText
280
+ ?? 'unknown';
281
+ // Normalize: strip `claude_code.` prefix if present so the switch
282
+ // matches regardless of whether Claude Code emits prefixed or bare names.
283
+ const eventName = rawEventName.startsWith('claude_code.')
284
+ ? rawEventName.slice('claude_code.'.length)
285
+ : rawEventName;
286
+ // Track time range across all events
287
+ if (lr.timeUnixNano) {
288
+ if (!earliestNano || lr.timeUnixNano < earliestNano)
289
+ earliestNano = lr.timeUnixNano;
290
+ if (!latestNano || lr.timeUnixNano > latestNano)
291
+ latestNano = lr.timeUnixNano;
292
+ }
293
+ const row = {
294
+ organization_id: orgId,
295
+ strategy_id: attrs.strategyId,
296
+ delivery_id: attrs.deliveryId,
297
+ session_id: attrs.sessionId,
298
+ event_type: eventName,
299
+ tool_name: null,
300
+ success: null,
301
+ duration_ms: null,
302
+ error_message: null,
303
+ metadata: factoryMeta ? { ...factoryMeta } : null,
304
+ event_timestamp: nanoToIso(lr.timeUnixNano),
305
+ };
306
+ // Extract event-specific fields based on normalized event name
307
+ switch (eventName) {
308
+ case 'tool_result': {
309
+ row.tool_name = getLogAttribute(lr.attributes, 'tool_name');
310
+ row.success = getLogAttributeBool(lr.attributes, 'success');
311
+ row.duration_ms = getLogAttributeInt(lr.attributes, 'duration_ms');
312
+ row.error_message = getLogAttribute(lr.attributes, 'error');
313
+ // Aggregate tool counts for metric row
314
+ toolCallCount++;
315
+ if (row.success === false)
316
+ toolErrorCount++;
317
+ break;
318
+ }
319
+ case 'api_request':
320
+ row.duration_ms = getLogAttributeInt(lr.attributes, 'duration_ms');
321
+ row.metadata = {
322
+ ...row.metadata,
323
+ model: getLogAttribute(lr.attributes, 'model'),
324
+ cost_usd: getLogAttributeDouble(lr.attributes, 'cost_usd'),
325
+ input_tokens: getLogAttributeInt(lr.attributes, 'input_tokens'),
326
+ output_tokens: getLogAttributeInt(lr.attributes, 'output_tokens'),
327
+ cache_read_tokens: getLogAttributeInt(lr.attributes, 'cache_read_tokens'),
328
+ cache_creation_tokens: getLogAttributeInt(lr.attributes, 'cache_creation_tokens'),
329
+ };
330
+ break;
331
+ case 'api_error':
332
+ row.error_message = getLogAttribute(lr.attributes, 'error');
333
+ row.duration_ms = getLogAttributeInt(lr.attributes, 'duration_ms');
334
+ row.metadata = {
335
+ ...row.metadata,
336
+ model: getLogAttribute(lr.attributes, 'model'),
337
+ status_code: getLogAttributeInt(lr.attributes, 'status_code'),
338
+ attempt: getLogAttributeInt(lr.attributes, 'attempt'),
339
+ };
340
+ break;
341
+ case 'user_prompt':
342
+ row.metadata = {
343
+ ...row.metadata,
344
+ prompt_length: getLogAttributeInt(lr.attributes, 'prompt_length'),
345
+ };
346
+ break;
347
+ case 'tool_decision':
348
+ row.tool_name = getLogAttribute(lr.attributes, 'tool_name');
349
+ row.metadata = {
350
+ ...row.metadata,
351
+ decision: getLogAttribute(lr.attributes, 'decision'),
352
+ source: getLogAttribute(lr.attributes, 'source'),
353
+ };
354
+ break;
355
+ }
356
+ bufferEvent(row);
357
+ }
358
+ }
359
+ // Emit a metric row with aggregated tool counts from tool_result events
360
+ if (toolCallCount > 0) {
361
+ const now = new Date().toISOString();
362
+ bufferMetric({
363
+ organization_id: orgId,
364
+ strategy_id: attrs.strategyId,
365
+ delivery_id: attrs.deliveryId,
366
+ session_id: attrs.sessionId,
367
+ input_tokens: 0,
368
+ output_tokens: 0,
369
+ cache_read_tokens: 0,
370
+ cache_creation_tokens: 0,
371
+ cost_usd: 0,
372
+ active_time_ms: 0,
373
+ tool_call_count: toolCallCount,
374
+ tool_error_count: toolErrorCount,
375
+ model: null,
376
+ metadata: factoryMeta,
377
+ period_start: earliestNano ? nanoToIso(earliestNano) : now,
378
+ period_end: latestNano ? nanoToIso(latestNano) : now,
379
+ });
380
+ }
381
+ }
382
+ }
383
+ // ── HTTP server ──────────────────────────────────────────────────────
384
+ let server = null;
385
+ /**
386
+ * Read the full request body as a string.
387
+ */
388
+ function readBody(req) {
389
+ return new Promise((resolve, reject) => {
390
+ const chunks = [];
391
+ let totalBytes = 0;
392
+ const MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB
393
+ req.on('data', (chunk) => {
394
+ totalBytes += chunk.length;
395
+ if (totalBytes > MAX_BODY_BYTES) {
396
+ req.destroy();
397
+ reject(new Error('Request body too large'));
398
+ return;
399
+ }
400
+ chunks.push(chunk);
401
+ });
402
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
403
+ req.on('error', reject);
404
+ });
405
+ }
406
+ function sendJson(res, status, body) {
407
+ res.writeHead(status, { 'Content-Type': 'application/json' });
408
+ res.end(JSON.stringify(body));
409
+ }
410
+ // ── Stale port detection and reclaim ─────────────────────────────────
411
+ /**
412
+ * Check if a port is currently in use by attempting a TCP connection.
413
+ * Returns true if something is listening on the port.
414
+ */
415
+ export function isPortInUse(port, host = '127.0.0.1') {
416
+ return new Promise((resolve) => {
417
+ const socket = netConnect({ port, host });
418
+ socket.once('connect', () => {
419
+ socket.destroy();
420
+ resolve(true);
421
+ });
422
+ socket.once('error', () => {
423
+ socket.destroy();
424
+ resolve(false);
425
+ });
426
+ // Short timeout -- we only need to know if something answers
427
+ socket.setTimeout(1000, () => {
428
+ socket.destroy();
429
+ resolve(false);
430
+ });
431
+ });
432
+ }
433
+ /**
434
+ * Find the PID(s) holding a given TCP port using `lsof`.
435
+ * Returns an array of PIDs (may be empty if lsof is unavailable or no match).
436
+ */
437
+ export function findPidsOnPort(port) {
438
+ try {
439
+ // -i :port -t returns just PIDs, one per line
440
+ const output = execFileSync('lsof', ['-i', `:${port}`, '-t', '-sTCP:LISTEN'], {
441
+ encoding: 'utf-8',
442
+ timeout: 5000,
443
+ });
444
+ return output
445
+ .trim()
446
+ .split('\n')
447
+ .map(line => parseInt(line.trim(), 10))
448
+ .filter(pid => !isNaN(pid) && pid > 0);
449
+ }
450
+ catch {
451
+ // lsof not available or no matches -- not fatal
452
+ return [];
453
+ }
454
+ }
455
+ /**
456
+ * Check if a process with the given PID is alive.
457
+ */
458
+ function isProcessAlive(pid) {
459
+ try {
460
+ process.kill(pid, 0);
461
+ return true;
462
+ }
463
+ catch {
464
+ return false;
465
+ }
466
+ }
467
+ /**
468
+ * Read the daemon PID file and return the PID, or null if unreadable/missing.
469
+ */
470
+ function readDaemonPid(pidFilePath) {
471
+ if (!pidFilePath || !existsSync(pidFilePath))
472
+ return null;
473
+ try {
474
+ const contents = readFileSync(pidFilePath, 'utf-8').trim();
475
+ const pid = parseInt(contents, 10);
476
+ return isNaN(pid) ? null : pid;
477
+ }
478
+ catch {
479
+ return null;
480
+ }
481
+ }
482
+ /**
483
+ * Attempt to reclaim a stale OTLP port.
484
+ *
485
+ * Checks if the port is in use, cross-references with the PID file,
486
+ * and kills orphaned processes if the recorded daemon PID is stale.
487
+ *
488
+ * @returns true if reclaim was attempted (port was in use), false if port was free
489
+ */
490
+ export async function reclaimStalePort(port, pidFilePath) {
491
+ const inUse = await isPortInUse(port);
492
+ if (!inUse)
493
+ return false;
494
+ console.warn(`[otlp-receiver] Port ${port} is already in use, attempting reclaim`);
495
+ // Read the daemon PID file to check if the previous daemon is still alive
496
+ const daemonPid = readDaemonPid(pidFilePath);
497
+ if (daemonPid !== null && isProcessAlive(daemonPid)) {
498
+ // The previous daemon is still running -- do not kill it.
499
+ // The caller (acquirePidFile in index.ts) should have caught this.
500
+ console.warn(`[otlp-receiver] Previous daemon (PID ${daemonPid}) is still alive -- cannot reclaim`);
501
+ return true;
502
+ }
503
+ // Previous daemon is dead (or no PID file). Find what holds the port.
504
+ const pids = findPidsOnPort(port);
505
+ if (pids.length === 0) {
506
+ // Port appears in use but lsof found nothing -- possibly TIME_WAIT.
507
+ // SO_REUSEADDR + retry should handle this.
508
+ console.log('[otlp-receiver] No process found holding port (likely TIME_WAIT)');
509
+ return true;
510
+ }
511
+ // Kill orphaned processes that are holding the port.
512
+ // Only kill processes that are NOT the current daemon.
513
+ for (const pid of pids) {
514
+ if (pid === process.pid)
515
+ continue;
516
+ // If we have a PID file, only kill processes that match the stale daemon PID
517
+ // or are children of it. Without ppid checks, be conservative: kill if
518
+ // the daemon PID is stale (already verified above).
519
+ console.log(`[otlp-receiver] Killing orphaned process ${pid} holding port ${port}`);
520
+ try {
521
+ process.kill(pid, 'SIGTERM');
522
+ }
523
+ catch (err) {
524
+ console.warn(`[otlp-receiver] Failed to kill PID ${pid}: ${err.message}`);
525
+ }
526
+ }
527
+ // Give killed processes a moment to release the port
528
+ await new Promise(resolve => setTimeout(resolve, 500));
529
+ return true;
530
+ }
531
+ // ── Retry configuration ──────────────────────────────────────────────
532
+ /** Maximum number of listen attempts before failing fatally. */
533
+ const MAX_LISTEN_ATTEMPTS = 5;
534
+ /** Base delay between retries in ms (exponential backoff: 500, 1000, 2000, 4000). */
535
+ const BASE_RETRY_DELAY_MS = 500;
536
+ /**
537
+ * Attempt to bind the HTTP server to the configured port.
538
+ * Returns a promise that resolves when listening or rejects on error.
539
+ */
540
+ function attemptListen(srv, port, host) {
541
+ return new Promise((resolve, reject) => {
542
+ const onError = (err) => {
543
+ srv.removeListener('listening', onListening);
544
+ reject(err);
545
+ };
546
+ const onListening = () => {
547
+ srv.removeListener('error', onError);
548
+ resolve();
549
+ };
550
+ srv.once('error', onError);
551
+ srv.once('listening', onListening);
552
+ // exclusive: false allows SO_REUSEADDR, preventing TIME_WAIT sockets
553
+ // from a previous daemon instance from blocking rebind.
554
+ srv.listen({ port, host, exclusive: false });
555
+ });
556
+ }
557
+ /**
558
+ * Start the OTLP HTTP receiver server.
559
+ *
560
+ * Performs stale port detection and reclaim before binding, then retries
561
+ * with exponential backoff if EADDRINUSE persists (e.g. TIME_WAIT).
562
+ *
563
+ * @param pidFilePath - Path to the daemon PID file for stale process detection
564
+ */
565
+ export async function startOtlpReceiver(config, organizationId, pidFilePath) {
566
+ // Pre-bind: attempt to reclaim stale port from a previous daemon
567
+ await reclaimStalePort(config.port, pidFilePath);
568
+ const srv = createServer(async (req, res) => {
569
+ // CORS preflight
570
+ if (req.method === 'OPTIONS') {
571
+ res.writeHead(204, {
572
+ 'Access-Control-Allow-Origin': '*',
573
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
574
+ 'Access-Control-Allow-Headers': 'Content-Type',
575
+ });
576
+ res.end();
577
+ return;
578
+ }
579
+ if (req.method !== 'POST') {
580
+ sendJson(res, 405, { error: 'Method not allowed' });
581
+ return;
582
+ }
583
+ try {
584
+ const body = await readBody(req);
585
+ const parsed = JSON.parse(body);
586
+ if (req.url === '/v1/metrics') {
587
+ processMetrics(parsed, organizationId);
588
+ sendJson(res, 200, { partialSuccess: {} });
589
+ }
590
+ else if (req.url === '/v1/logs') {
591
+ processLogs(parsed, organizationId);
592
+ sendJson(res, 200, { partialSuccess: {} });
593
+ }
594
+ else {
595
+ sendJson(res, 404, { error: `Unknown endpoint: ${req.url}` });
596
+ }
597
+ }
598
+ catch (err) {
599
+ const message = err instanceof Error ? err.message : String(err);
600
+ console.warn(`[otlp-receiver] Parse error on ${req.url}:`, message);
601
+ sendJson(res, 400, { error: `Parse error: ${message}` });
602
+ }
603
+ });
604
+ // Retry listen with exponential backoff on EADDRINUSE
605
+ for (let attempt = 1; attempt <= MAX_LISTEN_ATTEMPTS; attempt++) {
606
+ try {
607
+ await attemptListen(srv, config.port, '127.0.0.1');
608
+ server = srv;
609
+ // Attach a persistent error handler for post-startup errors
610
+ srv.on('error', (err) => {
611
+ console.error('[otlp-receiver] Server error:', err.message);
612
+ });
613
+ console.log(`[otlp-receiver] Listening on http://127.0.0.1:${config.port}`);
614
+ return;
615
+ }
616
+ catch (err) {
617
+ const isAddrInUse = err.code === 'EADDRINUSE';
618
+ if (!isAddrInUse || attempt === MAX_LISTEN_ATTEMPTS) {
619
+ // Non-retryable error or exhausted retries -- fail fatally
620
+ srv.close();
621
+ const suffix = isAddrInUse
622
+ ? ` (EADDRINUSE after ${MAX_LISTEN_ATTEMPTS} attempts -- port ${config.port} may be held by another process)`
623
+ : '';
624
+ throw new Error(`[otlp-receiver] Failed to bind port ${config.port}: ${err.message}${suffix}`);
625
+ }
626
+ const delayMs = BASE_RETRY_DELAY_MS * Math.pow(2, attempt - 1);
627
+ console.warn(`[otlp-receiver] EADDRINUSE on port ${config.port}, ` +
628
+ `retrying in ${delayMs}ms (attempt ${attempt}/${MAX_LISTEN_ATTEMPTS})`);
629
+ await new Promise(resolve => setTimeout(resolve, delayMs));
630
+ }
631
+ }
632
+ }
633
+ /**
634
+ * Stop the OTLP HTTP receiver server.
635
+ *
636
+ * Closes all open connections and waits for the server to shut down.
637
+ * If the server doesn't close within SHUTDOWN_TIMEOUT_MS, it is
638
+ * force-destroyed to prevent hanging on stale keep-alive connections.
639
+ */
640
+ export function stopOtlpReceiver() {
641
+ const SHUTDOWN_TIMEOUT_MS = 5000;
642
+ return new Promise((resolve) => {
643
+ if (!server) {
644
+ resolve();
645
+ return;
646
+ }
647
+ const srv = server;
648
+ server = null;
649
+ // Force-close after timeout to avoid hanging on stale connections
650
+ const forceTimer = setTimeout(() => {
651
+ console.warn('[otlp-receiver] Shutdown timeout reached, force-closing server');
652
+ srv.closeAllConnections();
653
+ }, SHUTDOWN_TIMEOUT_MS);
654
+ srv.close(() => {
655
+ clearTimeout(forceTimer);
656
+ console.log('[otlp-receiver] Server stopped');
657
+ resolve();
658
+ });
659
+ // Immediately close idle connections so close() completes faster
660
+ srv.closeAllConnections();
661
+ });
662
+ }
663
+ //# sourceMappingURL=otlp-receiver.js.map