@interactive-inc/claude-funnel 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/README.md +155 -133
  2. package/dist/bin.js +1417 -0
  3. package/dist/gateway/daemon.js +513 -0
  4. package/dist/highlights-eq9cgrbb.scm +604 -0
  5. package/dist/highlights-ghv9g403.scm +205 -0
  6. package/dist/highlights-hk7bwhj4.scm +284 -0
  7. package/dist/highlights-r812a2qc.scm +150 -0
  8. package/dist/highlights-x6tmsnaa.scm +115 -0
  9. package/dist/injections-73j83es3.scm +27 -0
  10. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  11. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  12. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  13. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  14. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  15. package/lib/bin.ts +78 -0
  16. package/lib/{modules → cli}/router/to-request.ts +13 -20
  17. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +27 -0
  18. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +40 -0
  19. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +41 -0
  20. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +22 -0
  21. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +23 -0
  22. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +26 -0
  23. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +92 -0
  24. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +22 -0
  25. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +63 -0
  26. package/lib/cli/routes/channels.$channel.connectors.ts +26 -0
  27. package/lib/cli/routes/channels.$channel.rename.$newName.ts +22 -0
  28. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +34 -0
  29. package/lib/cli/routes/channels.$channel.ts +34 -0
  30. package/lib/cli/routes/channels.add.$channel.ts +33 -0
  31. package/lib/cli/routes/channels.remove.$channel.ts +20 -0
  32. package/lib/cli/routes/channels.ts +39 -0
  33. package/lib/cli/routes/claude.ts +69 -0
  34. package/lib/cli/routes/gateway.listeners.ts +41 -0
  35. package/lib/cli/routes/gateway.logs.ts +123 -0
  36. package/lib/{routes/gateway/restart.ts → cli/routes/gateway.restart.ts} +20 -5
  37. package/lib/cli/routes/gateway.run.ts +41 -0
  38. package/lib/cli/routes/gateway.start.ts +50 -0
  39. package/lib/cli/routes/gateway.status.ts +19 -0
  40. package/lib/cli/routes/gateway.stop.ts +32 -0
  41. package/lib/cli/routes/gateway.ts +55 -0
  42. package/lib/cli/routes/index.ts +202 -0
  43. package/lib/cli/routes/profiles.$profile.as-default.ts +22 -0
  44. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +22 -0
  45. package/lib/cli/routes/profiles.$profile.run.ts +36 -0
  46. package/lib/cli/routes/profiles.add.$profile.ts +46 -0
  47. package/lib/cli/routes/profiles.remove.$profile.ts +20 -0
  48. package/lib/cli/routes/profiles.set.$profile.ts +46 -0
  49. package/lib/cli/routes/profiles.ts +40 -0
  50. package/lib/cli/routes/status.ts +93 -0
  51. package/lib/cli/routes/update.ts +27 -0
  52. package/lib/connectors/connector-config-schema.ts +16 -0
  53. package/lib/connectors/connector-factory.ts +94 -0
  54. package/lib/connectors/connector-listener.ts +20 -0
  55. package/lib/{modules/connectors/funnel-discord-adapter.ts → connectors/discord-adapter.ts} +6 -11
  56. package/lib/{modules/connectors → connectors}/discord-connector-schema.ts +4 -1
  57. package/lib/connectors/discord-listener.ts +111 -0
  58. package/lib/{modules/connectors/funnel-gh-adapter.ts → connectors/gh-adapter.ts} +3 -6
  59. package/lib/{modules/connectors → connectors}/gh-connector-schema.ts +4 -1
  60. package/lib/{modules/connectors/funnel-gh-listener.ts → connectors/gh-listener.ts} +45 -19
  61. package/lib/{modules/connectors → connectors}/match-cron.ts +10 -4
  62. package/lib/connectors/schedule-connector-schema.ts +33 -0
  63. package/lib/connectors/schedule-listener.ts +207 -0
  64. package/lib/connectors/schedule-state-store.ts +54 -0
  65. package/lib/connectors/slack-adapter.ts +36 -0
  66. package/lib/{modules/connectors → connectors}/slack-connector-schema.ts +4 -1
  67. package/lib/{modules/connectors/funnel-slack-event-processor.ts → connectors/slack-event-processor.ts} +15 -9
  68. package/lib/{modules/connectors/funnel-slack-listener.ts → connectors/slack-listener.ts} +33 -14
  69. package/lib/engine/channels/channels.ts +520 -0
  70. package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +28 -55
  71. package/lib/engine/claude/gateway-controller.ts +4 -0
  72. package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +4 -0
  73. package/lib/{modules/fs/memory-funnel-file-system.ts → engine/fs/memory-file-system.ts} +20 -3
  74. package/lib/{modules/fs/node-funnel-file-system.ts → engine/fs/node-file-system.ts} +14 -2
  75. package/lib/{modules/http/memory-funnel-http-client.ts → engine/http/memory-http-client.ts} +1 -5
  76. package/lib/{modules/http/node-funnel-http-client.ts → engine/http/node-http-client.ts} +1 -5
  77. package/lib/{modules/id/memory-funnel-id-generator.ts → engine/id/memory-id-generator.ts} +1 -1
  78. package/lib/{modules/id/node-funnel-id-generator.ts → engine/id/node-id-generator.ts} +1 -1
  79. package/lib/{modules/logger/memory-funnel-logger.ts → engine/logger/memory-logger.ts} +1 -1
  80. package/lib/{modules/logger/node-funnel-logger.ts → engine/logger/node-logger.ts} +1 -1
  81. package/lib/{modules/logger/noop-funnel-logger.ts → engine/logger/noop-logger.ts} +1 -1
  82. package/lib/engine/mcp/channel-server.ts +204 -0
  83. package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +24 -10
  84. package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
  85. package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
  86. package/lib/engine/profiles/profile-channel-checker.ts +7 -0
  87. package/lib/{modules/profiles/funnel-profiles.ts → engine/profiles/profiles.ts} +41 -43
  88. package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
  89. package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
  90. package/lib/engine/settings/settings-schema.ts +46 -0
  91. package/lib/engine/settings/settings-store.ts +110 -0
  92. package/lib/{modules/time/memory-funnel-clock.ts → engine/time/memory-clock.ts} +1 -1
  93. package/lib/{modules/time/node-funnel-clock.ts → engine/time/node-clock.ts} +1 -1
  94. package/lib/funnel.ts +83 -78
  95. package/lib/gateway/auth-middleware.ts +44 -0
  96. package/lib/gateway/broadcaster.ts +319 -0
  97. package/lib/gateway/daemon.ts +47 -0
  98. package/lib/gateway/factory.ts +10 -0
  99. package/lib/gateway/funnel-event-store.ts +155 -0
  100. package/lib/gateway/gateway-server.ts +414 -0
  101. package/lib/gateway/gateway-token.ts +79 -0
  102. package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +27 -13
  103. package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +4 -4
  104. package/lib/gateway/listener-supervisor.ts +339 -0
  105. package/lib/gateway/listeners-client.ts +128 -0
  106. package/lib/gateway/resolve-daemon-script.ts +26 -0
  107. package/lib/gateway/routes/channels.connectors.call.ts +39 -0
  108. package/lib/gateway/routes/health.ts +13 -0
  109. package/lib/gateway/routes/index.ts +24 -0
  110. package/lib/gateway/routes/listeners.list.ts +6 -0
  111. package/lib/gateway/routes/listeners.restart.ts +15 -0
  112. package/lib/gateway/routes/listeners.start.ts +15 -0
  113. package/lib/gateway/routes/listeners.stop.ts +15 -0
  114. package/lib/gateway/routes/route-deps.ts +11 -0
  115. package/lib/gateway/routes/status.ts +15 -0
  116. package/lib/gateway/routes/validator.ts +17 -0
  117. package/lib/index.ts +50 -92
  118. package/lib/logger/leuco-human-file-writer.ts +65 -0
  119. package/lib/logger/leuco-human-logger.ts +98 -0
  120. package/lib/logger/leuco-human-record.ts +16 -0
  121. package/lib/logger/leuco-human-stdout-writer.ts +26 -0
  122. package/lib/logger/leuco-human-writer.ts +14 -0
  123. package/lib/logger/leuco-logger-memory-sink.ts +67 -0
  124. package/lib/logger/leuco-logger-record.ts +13 -0
  125. package/lib/logger/leuco-logger-sink.ts +33 -0
  126. package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
  127. package/lib/logger/leuco-logger.ts +135 -0
  128. package/lib/tui/app.tsx +357 -0
  129. package/lib/tui/components/add-row.tsx +18 -0
  130. package/lib/tui/components/brand.tsx +27 -0
  131. package/lib/tui/components/card.tsx +44 -0
  132. package/lib/tui/components/detail-bar.tsx +46 -0
  133. package/lib/tui/components/editable-field.tsx +33 -0
  134. package/lib/tui/components/empty-state.tsx +11 -0
  135. package/lib/tui/components/gateway-status.tsx +66 -0
  136. package/lib/tui/components/keymap.tsx +29 -0
  137. package/lib/tui/components/menu-item.tsx +73 -0
  138. package/lib/tui/components/menu.tsx +26 -0
  139. package/lib/tui/components/panel-header.tsx +22 -0
  140. package/lib/tui/components/readonly-field.tsx +18 -0
  141. package/lib/tui/components/section-header.tsx +25 -0
  142. package/lib/tui/components/selection-accent.tsx +32 -0
  143. package/lib/tui/components/session-item.tsx +33 -0
  144. package/lib/tui/components/session-list.tsx +33 -0
  145. package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
  146. package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
  147. package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
  148. package/lib/tui/components/ui/hascii/badge.tsx +51 -0
  149. package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
  150. package/lib/tui/components/ui/hascii/button.tsx +194 -0
  151. package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
  152. package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
  153. package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
  154. package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
  155. package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
  156. package/lib/tui/components/ui/hascii/card.tsx +27 -0
  157. package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
  158. package/lib/tui/components/ui/hascii/command.tsx +159 -0
  159. package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
  160. package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
  161. package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
  162. package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
  163. package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
  164. package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
  165. package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
  166. package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
  167. package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
  168. package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
  169. package/lib/tui/components/ui/hascii/input.tsx +130 -0
  170. package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
  171. package/lib/tui/components/ui/hascii/progress.tsx +28 -0
  172. package/lib/tui/components/ui/hascii/select.tsx +131 -0
  173. package/lib/tui/components/ui/hascii/separator.tsx +35 -0
  174. package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
  175. package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
  176. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
  177. package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
  178. package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
  179. package/lib/tui/components/ui/hascii/slider.tsx +91 -0
  180. package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
  181. package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
  182. package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
  183. package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
  184. package/lib/tui/components/ui/hascii/switch.tsx +66 -0
  185. package/lib/tui/components/ui/hascii/table.tsx +95 -0
  186. package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
  187. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
  188. package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
  189. package/lib/tui/components/ui/hascii/tree.tsx +104 -0
  190. package/lib/tui/components/view-shell.tsx +44 -0
  191. package/lib/tui/filter-input.tsx +33 -0
  192. package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
  193. package/lib/tui/parse-comma-list.ts +14 -0
  194. package/lib/tui/profile-launcher.tsx +61 -0
  195. package/lib/tui/scrollbar-options.ts +19 -0
  196. package/lib/tui/sidebar.tsx +50 -0
  197. package/lib/tui/theme.ts +40 -0
  198. package/lib/tui/tui.tsx +20 -0
  199. package/lib/tui/types.ts +38 -0
  200. package/lib/tui/unique-name.ts +18 -0
  201. package/lib/tui/use-event-stream.ts +133 -0
  202. package/lib/tui/use-snapshot.ts +99 -0
  203. package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
  204. package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
  205. package/lib/tui/utils/hascii/theme-context.tsx +26 -0
  206. package/lib/tui/utils/hascii/theme.ts +176 -0
  207. package/lib/tui/views/channels-view.tsx +108 -0
  208. package/lib/tui/views/connectors-view.tsx +164 -0
  209. package/lib/tui/views/events-view.tsx +160 -0
  210. package/lib/tui/views/listeners-view.tsx +80 -0
  211. package/lib/tui/views/profiles-view.tsx +152 -0
  212. package/package.json +50 -44
  213. package/lib/api.ts +0 -54
  214. package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
  215. package/lib/modules/channels/funnel-channels.ts +0 -160
  216. package/lib/modules/connectors/connector-config-schema.ts +0 -16
  217. package/lib/modules/connectors/connector-existence-checker.ts +0 -3
  218. package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
  219. package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
  220. package/lib/modules/connectors/funnel-connector-stores.ts +0 -52
  221. package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
  222. package/lib/modules/connectors/funnel-connectors.ts +0 -151
  223. package/lib/modules/connectors/funnel-discord-listener.ts +0 -71
  224. package/lib/modules/connectors/funnel-discord-store.ts +0 -88
  225. package/lib/modules/connectors/funnel-gh-store.ts +0 -101
  226. package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
  227. package/lib/modules/connectors/funnel-schedule-listener.ts +0 -130
  228. package/lib/modules/connectors/funnel-schedule-store.ts +0 -195
  229. package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
  230. package/lib/modules/connectors/funnel-slack-store.ts +0 -90
  231. package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -81
  232. package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
  233. package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
  234. package/lib/modules/gateway/daemon.ts +0 -74
  235. package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
  236. package/lib/modules/gateway/funnel-event-logger.ts +0 -59
  237. package/lib/modules/gateway/funnel-gateway-server.ts +0 -241
  238. package/lib/modules/mcp/channel-server.ts +0 -76
  239. package/lib/modules/profiles/profile-channel-checker.ts +0 -3
  240. package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
  241. package/lib/modules/repos/funnel-repositories.ts +0 -112
  242. package/lib/modules/schedule/funnel-schedule.ts +0 -39
  243. package/lib/modules/settings/funnel-settings-store.ts +0 -56
  244. package/lib/modules/settings/settings-schema.ts +0 -33
  245. package/lib/modules/tui/app.tsx +0 -44
  246. package/lib/modules/tui/tui.tsx +0 -13
  247. package/lib/routes/channels/add.help.ts +0 -3
  248. package/lib/routes/channels/add.ts +0 -21
  249. package/lib/routes/channels/connectors-attach.help.ts +0 -3
  250. package/lib/routes/channels/connectors-attach.ts +0 -17
  251. package/lib/routes/channels/connectors-detach.help.ts +0 -3
  252. package/lib/routes/channels/connectors-detach.ts +0 -17
  253. package/lib/routes/channels/group.help.ts +0 -16
  254. package/lib/routes/channels/group.ts +0 -22
  255. package/lib/routes/channels/remove.help.ts +0 -3
  256. package/lib/routes/channels/remove.ts +0 -17
  257. package/lib/routes/channels/rename.help.ts +0 -5
  258. package/lib/routes/channels/rename.ts +0 -17
  259. package/lib/routes/channels/routes.ts +0 -19
  260. package/lib/routes/channels/show.help.ts +0 -1
  261. package/lib/routes/channels/show.ts +0 -26
  262. package/lib/routes/claude/claude.help.ts +0 -16
  263. package/lib/routes/claude/claude.ts +0 -76
  264. package/lib/routes/claude/routes.ts +0 -4
  265. package/lib/routes/connectors/add.help.ts +0 -28
  266. package/lib/routes/connectors/add.ts +0 -64
  267. package/lib/routes/connectors/group.help.ts +0 -14
  268. package/lib/routes/connectors/group.ts +0 -18
  269. package/lib/routes/connectors/remove.help.ts +0 -3
  270. package/lib/routes/connectors/remove.ts +0 -17
  271. package/lib/routes/connectors/rename.help.ts +0 -5
  272. package/lib/routes/connectors/rename.ts +0 -17
  273. package/lib/routes/connectors/routes.ts +0 -23
  274. package/lib/routes/connectors/schedules-add.help.ts +0 -11
  275. package/lib/routes/connectors/schedules-add.ts +0 -33
  276. package/lib/routes/connectors/schedules-group.help.ts +0 -1
  277. package/lib/routes/connectors/schedules-group.ts +0 -38
  278. package/lib/routes/connectors/schedules-remove.help.ts +0 -3
  279. package/lib/routes/connectors/schedules-remove.ts +0 -17
  280. package/lib/routes/connectors/set.help.ts +0 -8
  281. package/lib/routes/connectors/set.ts +0 -72
  282. package/lib/routes/connectors/show.help.ts +0 -1
  283. package/lib/routes/connectors/show.ts +0 -41
  284. package/lib/routes/gateway/group.help.ts +0 -15
  285. package/lib/routes/gateway/group.ts +0 -28
  286. package/lib/routes/gateway/logs.help.ts +0 -13
  287. package/lib/routes/gateway/logs.ts +0 -102
  288. package/lib/routes/gateway/restart.help.ts +0 -10
  289. package/lib/routes/gateway/routes.ts +0 -18
  290. package/lib/routes/gateway/run.help.ts +0 -12
  291. package/lib/routes/gateway/run.ts +0 -35
  292. package/lib/routes/gateway/start.help.ts +0 -15
  293. package/lib/routes/gateway/start.ts +0 -32
  294. package/lib/routes/gateway/status.help.ts +0 -9
  295. package/lib/routes/gateway/status.ts +0 -28
  296. package/lib/routes/gateway/stop.help.ts +0 -8
  297. package/lib/routes/gateway/stop.ts +0 -21
  298. package/lib/routes/profiles/add.help.ts +0 -3
  299. package/lib/routes/profiles/add.ts +0 -33
  300. package/lib/routes/profiles/group.help.ts +0 -16
  301. package/lib/routes/profiles/group.ts +0 -25
  302. package/lib/routes/profiles/launch.help.ts +0 -4
  303. package/lib/routes/profiles/launch.ts +0 -36
  304. package/lib/routes/profiles/remove.help.ts +0 -3
  305. package/lib/routes/profiles/remove.ts +0 -17
  306. package/lib/routes/profiles/rename.help.ts +0 -5
  307. package/lib/routes/profiles/rename.ts +0 -17
  308. package/lib/routes/profiles/routes.ts +0 -18
  309. package/lib/routes/profiles/set.help.ts +0 -5
  310. package/lib/routes/profiles/set.ts +0 -32
  311. package/lib/routes/repos/add.help.ts +0 -6
  312. package/lib/routes/repos/add.ts +0 -20
  313. package/lib/routes/repos/group.help.ts +0 -11
  314. package/lib/routes/repos/group.ts +0 -18
  315. package/lib/routes/repos/remove.help.ts +0 -3
  316. package/lib/routes/repos/remove.ts +0 -17
  317. package/lib/routes/repos/rename.help.ts +0 -5
  318. package/lib/routes/repos/rename.ts +0 -17
  319. package/lib/routes/repos/routes.ts +0 -17
  320. package/lib/routes/repos/set.help.ts +0 -5
  321. package/lib/routes/repos/set.ts +0 -21
  322. package/lib/routes/repos/show.help.ts +0 -1
  323. package/lib/routes/repos/show.ts +0 -19
  324. package/lib/routes/request/discord-help.ts +0 -9
  325. package/lib/routes/request/discord.help.ts +0 -19
  326. package/lib/routes/request/discord.ts +0 -65
  327. package/lib/routes/request/group.help.ts +0 -15
  328. package/lib/routes/request/group.ts +0 -9
  329. package/lib/routes/request/routes.ts +0 -14
  330. package/lib/routes/request/slack-help.ts +0 -9
  331. package/lib/routes/request/slack.help.ts +0 -19
  332. package/lib/routes/request/slack.ts +0 -61
  333. package/lib/routes/status/routes.ts +0 -4
  334. package/lib/routes/status/status.help.ts +0 -6
  335. package/lib/routes/status/status.ts +0 -77
  336. package/lib/routes/update/routes.ts +0 -4
  337. package/lib/routes/update/update.help.ts +0 -5
  338. package/lib/routes/update/update.ts +0 -21
  339. package/lib/routes.ts +0 -40
  340. /package/lib/{factory.ts → cli/factory.ts} +0 -0
  341. /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
  342. /package/lib/{modules → cli}/router/validator.ts +0 -0
  343. /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
  344. /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
  345. /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
  346. /package/lib/{modules/id/funnel-id-generator.ts → engine/id/id-generator.ts} +0 -0
  347. /package/lib/{modules/logger/funnel-logger.ts → engine/logger/logger.ts} +0 -0
  348. /package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +0 -0
  349. /package/lib/{modules/time/funnel-clock.ts → engine/time/clock.ts} +0 -0
@@ -0,0 +1,414 @@
1
+ import { existsSync, mkdirSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import type { Server, ServerWebSocket } from "bun"
4
+ import type { Hono } from "hono"
5
+ import type { FunnelChannels } from "@/engine/channels/channels"
6
+ import { constantTimeEqual, requireBearerToken } from "@/gateway/auth-middleware"
7
+ import { type Env, factory } from "@/gateway/factory"
8
+ import { FunnelBroadcaster } from "@/gateway/broadcaster"
9
+ import { FunnelEventStore } from "@/gateway/funnel-event-store"
10
+ import { FunnelListenerSupervisor } from "@/gateway/listener-supervisor"
11
+ import { killCompetingSlackGateways } from "@/gateway/kill-competing-slack-gateways"
12
+ import { gatewayRoutes } from "@/gateway/routes"
13
+ import { FunnelLogger } from "@/engine/logger/logger"
14
+ import { NodeFunnelLogger } from "@/engine/logger/node-logger"
15
+ import type { FunnelProcessRunner } from "@/engine/process/process-runner"
16
+ import type { FunnelSettingsReader } from "@/engine/settings/settings-reader"
17
+ import type { FunnelClock } from "@/engine/time/clock"
18
+
19
+ const DEFAULT_PORT = 9742
20
+ const DEFAULT_LOG_DIR = "/tmp/funnel/events"
21
+ const DB_FILENAME = "events.db"
22
+
23
+ type Deps = {
24
+ channels: FunnelChannels
25
+ settings: FunnelSettingsReader
26
+ port?: number
27
+ /** Directory holding the SQLite event store. The DB file lives at `<logDir>/events.db`. */
28
+ logDir?: string
29
+ process?: FunnelProcessRunner
30
+ clock?: FunnelClock
31
+ logger?: FunnelLogger
32
+ selfPid?: number
33
+ killCompetingSlack?: boolean
34
+ /** Bearer token required for `/listeners*`, `/status`, and `/ws`. Empty string disables auth (tests only). */
35
+ token?: string
36
+ }
37
+
38
+ type WsData = {
39
+ /** Stable channel id (uuid) the client subscribed to. "" for tap-all clients. */
40
+ channel: string
41
+ /** Resolved channel name (for log readability). null for tap-all or unknown. */
42
+ channelName: string | null
43
+ /** Connector names belonging to that channel; used by tap-all replay filtering. */
44
+ connectors: string[]
45
+ tapAll?: boolean
46
+ /** Routing mode for this channel; resolved at upgrade time from settings. */
47
+ delivery: "fanout" | "exclusive"
48
+ /** Replay any events with offset strictly greater than this on open, then resume the live stream. */
49
+ since?: number
50
+ }
51
+
52
+ const defaultLogger = new NodeFunnelLogger()
53
+
54
+ /**
55
+ * In-process gateway: runs `Bun.serve` (HTTP + WebSocket /ws), boots connector
56
+ * listeners through `FunnelListenerSupervisor`, fans events out via
57
+ * `FunnelBroadcaster`, and persists them via `FunnelEventStore` (SQLite).
58
+ * System events (gateway lifecycle, connect/disconnect) flow to `FunnelLogger`
59
+ * instead — keeping the SQLite seq space exclusive to broadcaster traffic so
60
+ * the broadcaster's offset counter and `getMaxSeq()` stay aligned without
61
+ * per-event coordination. Exposes `/listeners` HTTP for runtime
62
+ * start/stop/restart of individual connectors.
63
+ */
64
+ export class FunnelGatewayServer {
65
+ private readonly channels: FunnelChannels
66
+ private readonly settings: FunnelSettingsReader
67
+ private readonly port: number
68
+ private readonly logDir: string
69
+ private readonly process?: FunnelProcessRunner
70
+ private readonly logger: FunnelLogger
71
+ private readonly selfPid: number
72
+ private readonly killCompetingSlack: boolean
73
+ private readonly token: string
74
+ private readonly broadcaster: FunnelBroadcaster
75
+ private readonly eventStore: FunnelEventStore
76
+ private readonly supervisor: FunnelListenerSupervisor
77
+ private readonly nowMs: () => number
78
+ private startedAt: number | null = null
79
+ private server: Server<WsData> | null = null
80
+
81
+ constructor(deps: Deps) {
82
+ this.channels = deps.channels
83
+ this.settings = deps.settings
84
+ this.port = deps.port ?? DEFAULT_PORT
85
+ this.logDir = deps.logDir ?? DEFAULT_LOG_DIR
86
+ this.process = deps.process
87
+ this.logger = deps.logger ?? defaultLogger
88
+ this.selfPid = deps.selfPid ?? globalThis.process.pid
89
+ this.killCompetingSlack = deps.killCompetingSlack ?? true
90
+ this.token = deps.token ?? ""
91
+ const clock = deps.clock
92
+ this.nowMs = clock ? () => clock.millis() : () => Date.now()
93
+ if (!existsSync(this.logDir)) mkdirSync(this.logDir, { recursive: true })
94
+ this.eventStore = new FunnelEventStore({
95
+ path: join(this.logDir, DB_FILENAME),
96
+ now: this.nowMs,
97
+ })
98
+ this.broadcaster = new FunnelBroadcaster({
99
+ logger: this.logger,
100
+ now: this.nowMs,
101
+ persistentReplay: this.eventStore,
102
+ })
103
+ this.broadcaster.seedLatestOffset(this.eventStore.findMaxOffset())
104
+ this.supervisor = new FunnelListenerSupervisor({
105
+ channels: this.channels,
106
+ logger: this.logger,
107
+ notify: (channelName, connectorName, content, meta) =>
108
+ this.notify(channelName, connectorName, content, meta),
109
+ now: this.nowMs,
110
+ })
111
+ }
112
+
113
+ async start(): Promise<Server<WsData>> {
114
+ if (this.server) return this.server
115
+
116
+ const app = this.buildApp()
117
+
118
+ this.startedAt = this.nowMs()
119
+ this.server = Bun.serve<WsData>({
120
+ port: this.port,
121
+ development: false,
122
+ fetch: (request, server) => this.handleFetch(request, server, app),
123
+ websocket: {
124
+ open: (ws) => this.handleWsOpen(ws),
125
+ close: (ws) => this.handleWsClose(ws),
126
+ message() {
127
+ // required by Bun's websocket interface; no client → gateway messages today
128
+ },
129
+ },
130
+ })
131
+
132
+ this.logServerStarted()
133
+ await this.bootListeners()
134
+
135
+ return this.server
136
+ }
137
+
138
+ async stop(): Promise<void> {
139
+ await this.supervisor.stopAll()
140
+
141
+ if (this.server) {
142
+ this.server.stop()
143
+ this.server = null
144
+ }
145
+ }
146
+
147
+ getStatus(): { clients: number; channels: { channel: string; connectors: string[] }[] } {
148
+ return {
149
+ clients: this.broadcaster.getClientCount(),
150
+ channels: this.broadcaster.listChannels(),
151
+ }
152
+ }
153
+
154
+ getBroadcaster(): FunnelBroadcaster {
155
+ return this.broadcaster
156
+ }
157
+
158
+ getSupervisor(): FunnelListenerSupervisor {
159
+ return this.supervisor
160
+ }
161
+
162
+ getEventStore(): FunnelEventStore {
163
+ return this.eventStore
164
+ }
165
+
166
+ private handleFetch(
167
+ request: Request,
168
+ server: Server<WsData>,
169
+ app: Hono<Env>,
170
+ ): Response | Promise<Response> | undefined {
171
+ const url = new URL(request.url)
172
+
173
+ if (url.pathname === "/ws" && request.headers.get("upgrade") === "websocket") {
174
+ if (this.token && !this.tokenMatchesUpgrade(request)) {
175
+ return new Response("unauthorized", { status: 401 })
176
+ }
177
+
178
+ const tapAll = url.searchParams.get("tap") === "all"
179
+ const requestedChannel = tapAll ? "" : (url.searchParams.get("channel") ?? "")
180
+ const channel = !tapAll && requestedChannel ? this.resolveChannel(requestedChannel) : null
181
+ const channelId = tapAll ? "" : (channel?.id ?? requestedChannel)
182
+ const channelName = tapAll ? null : (channel?.name ?? null)
183
+ const connectors = channel?.connectors ?? []
184
+ const delivery = channel?.delivery ?? "fanout"
185
+ const sinceRaw = url.searchParams.get("since")
186
+ const sinceParsed = sinceRaw === null ? Number.NaN : Number.parseInt(sinceRaw, 10)
187
+ const since = Number.isFinite(sinceParsed) && sinceParsed >= 0 ? sinceParsed : undefined
188
+ const upgraded = server.upgrade(request, {
189
+ data: {
190
+ channel: channelId,
191
+ channelName,
192
+ connectors,
193
+ tapAll,
194
+ delivery,
195
+ since,
196
+ },
197
+ })
198
+
199
+ if (upgraded) return undefined
200
+
201
+ return new Response("WebSocket upgrade failed", { status: 400 })
202
+ }
203
+
204
+ return app.fetch(request)
205
+ }
206
+
207
+ private handleWsOpen(ws: ServerWebSocket<WsData>): void {
208
+ if (typeof ws.data.since === "number") {
209
+ const replay = this.broadcaster.replaySince(ws.data.since, ws.data)
210
+
211
+ for (const event of replay) ws.send(JSON.stringify(event))
212
+ }
213
+
214
+ this.broadcaster.addClient(ws, ws.data)
215
+
216
+ if (ws.data.channelName) {
217
+ const meta: Record<string, string> = {
218
+ event_type: "system",
219
+ action: "channel_connect",
220
+ channel: ws.data.channelName,
221
+ channelId: ws.data.channel,
222
+ connectors: ws.data.connectors.join(","),
223
+ total: String(this.broadcaster.getClientCount()),
224
+ }
225
+
226
+ this.logger.info("channel connected", meta)
227
+ } else {
228
+ this.logger.info("tap-all client connected", {
229
+ event_type: "system",
230
+ action: "tap_connect",
231
+ total: String(this.broadcaster.getClientCount()),
232
+ })
233
+ }
234
+ }
235
+
236
+ private handleWsClose(ws: ServerWebSocket<WsData>): void {
237
+ this.broadcaster.removeClient(ws)
238
+
239
+ if (ws.data.channelName) {
240
+ this.logger.info("channel disconnected", {
241
+ event_type: "system",
242
+ action: "channel_disconnect",
243
+ channel: ws.data.channelName,
244
+ channelId: ws.data.channel,
245
+ total: String(this.broadcaster.getClientCount()),
246
+ })
247
+ } else {
248
+ this.logger.info("tap-all client disconnected", {
249
+ event_type: "system",
250
+ action: "tap_disconnect",
251
+ total: String(this.broadcaster.getClientCount()),
252
+ })
253
+ }
254
+ }
255
+
256
+ private logServerStarted(): void {
257
+ this.logger.info("gateway started", {
258
+ event_type: "system",
259
+ action: "gateway_start",
260
+ port: String(this.port),
261
+ pid: String(this.selfPid),
262
+ })
263
+
264
+ this.logger.info("funnel gateway listening", {
265
+ url: `http://localhost:${this.port}`,
266
+ websocket: `ws://localhost:${this.port}/ws`,
267
+ health: `http://localhost:${this.port}/health`,
268
+ })
269
+ }
270
+
271
+ private buildApp(): Hono<Env> {
272
+ const base = factory.createApp()
273
+
274
+ base.use((c, next) => {
275
+ c.set("deps", {
276
+ selfPid: this.selfPid,
277
+ broadcaster: this.broadcaster,
278
+ supervisor: this.supervisor,
279
+ channels: this.channels,
280
+ uptimeMs: () => (this.startedAt ? this.nowMs() - this.startedAt : 0),
281
+ })
282
+
283
+ return next()
284
+ })
285
+
286
+ if (this.token) {
287
+ base.use("/listeners/*", requireBearerToken({ expected: this.token }))
288
+ base.use("/status", requireBearerToken({ expected: this.token }))
289
+ base.use("/channels/*", requireBearerToken({ expected: this.token }))
290
+ }
291
+
292
+ return base.route("/", gatewayRoutes)
293
+ }
294
+
295
+ /**
296
+ * Reads the bearer token from the WebSocket upgrade request. Accepts:
297
+ * - `Sec-WebSocket-Protocol: funnel.token.<value>` (preferred — header, never logged in URLs)
298
+ * - `Authorization: Bearer <value>` (also header-based)
299
+ * Returns true on a constant-time match against the daemon token.
300
+ */
301
+ private tokenMatchesUpgrade(request: Request): boolean {
302
+ const protocols = (request.headers.get("sec-websocket-protocol") ?? "")
303
+ .split(",")
304
+ .map((p) => p.trim())
305
+ .filter((p) => p.length > 0)
306
+
307
+ for (const proto of protocols) {
308
+ if (
309
+ proto.startsWith("funnel.token.") &&
310
+ constantTimeEqual(proto.slice("funnel.token.".length), this.token)
311
+ ) {
312
+ return true
313
+ }
314
+ }
315
+
316
+ const auth = request.headers.get("authorization") ?? ""
317
+ const match = auth.match(/^Bearer\s+(.+)$/i)
318
+
319
+ if (match && constantTimeEqual(match[1] ?? "", this.token)) return true
320
+
321
+ return false
322
+ }
323
+
324
+ private resolveChannel(
325
+ requested: string,
326
+ ): { id: string; name: string; connectors: string[]; delivery: "fanout" | "exclusive" } | null {
327
+ const settings = this.settings.read()
328
+ const channel = settings?.channels.find((c) => c.id === requested || c.name === requested)
329
+
330
+ if (!channel) return null
331
+
332
+ return {
333
+ id: channel.id,
334
+ name: channel.name,
335
+ connectors: channel.connectors.map((c) => c.name),
336
+ delivery: channel.delivery,
337
+ }
338
+ }
339
+
340
+ private async bootListeners(): Promise<void> {
341
+ const allConnectors = this.channels.listAllConnectors()
342
+
343
+ if (this.killCompetingSlack && allConnectors.some((c) => c.type === "slack")) {
344
+ const killed = await killCompetingSlackGateways({
345
+ selfPid: this.selfPid,
346
+ process: this.process,
347
+ logger: this.logger,
348
+ })
349
+
350
+ if (killed.length > 0) {
351
+ this.logger.info("killed competing Slack gateway processes", {
352
+ event_type: "system",
353
+ action: "kill_competing",
354
+ pids: killed.join(","),
355
+ })
356
+ }
357
+ }
358
+
359
+ await this.supervisor.startAll()
360
+
361
+ for (const entry of this.supervisor.list()) {
362
+ this.logger.info(`${entry.type} listener started: ${entry.name}`, {
363
+ event_type: "system",
364
+ action: `${entry.type}_connect`,
365
+ channel: entry.channelName,
366
+ connector: entry.name,
367
+ })
368
+ }
369
+
370
+ this.logger.info(`event store: ${join(this.logDir, DB_FILENAME)}`)
371
+ this.logger.info("funnel gateway running")
372
+ }
373
+
374
+ private async notify(
375
+ channelName: string,
376
+ connectorName: string,
377
+ content: string,
378
+ meta?: Record<string, string>,
379
+ ): Promise<void> {
380
+ const channelId = this.lookupChannelId(channelName)
381
+ const connectorId = channelId ? this.lookupConnectorId(channelId, connectorName) : null
382
+ const enriched: Record<string, string> = {
383
+ ...meta,
384
+ channel: channelName,
385
+ connector: connectorName,
386
+ }
387
+
388
+ if (channelId) enriched.channelId = channelId
389
+ if (connectorId) enriched.connectorId = connectorId
390
+
391
+ const event = this.broadcaster.broadcast(content, enriched)
392
+
393
+ this.eventStore.record({
394
+ content,
395
+ channelId: channelId ?? null,
396
+ connectorId: connectorId ?? null,
397
+ meta: enriched,
398
+ offset: event.offset,
399
+ })
400
+ }
401
+
402
+ private lookupChannelId(channelName: string): string | null {
403
+ const channel = this.settings.read().channels.find((c) => c.name === channelName)
404
+
405
+ return channel?.id ?? null
406
+ }
407
+
408
+ private lookupConnectorId(channelId: string, connectorName: string): string | null {
409
+ const channel = this.settings.read().channels.find((c) => c.id === channelId)
410
+ const connector = channel?.connectors.find((c) => c.name === connectorName)
411
+
412
+ return connector?.id ?? null
413
+ }
414
+ }
@@ -0,0 +1,79 @@
1
+ import { homedir } from "node:os"
2
+ import { dirname, join } from "node:path"
3
+ import { FunnelFileSystem } from "@/engine/fs/file-system"
4
+ import { NodeFunnelFileSystem } from "@/engine/fs/node-file-system"
5
+ import { FUNNEL_DIR } from "@/engine/settings/settings-store"
6
+
7
+ const TOKEN_FILE_NAME = "gateway.token"
8
+ const TOKEN_BYTES = 32
9
+
10
+ type Deps = {
11
+ fs?: FunnelFileSystem
12
+ dir?: string
13
+ generate?: () => string
14
+ }
15
+
16
+ const defaultFs = new NodeFunnelFileSystem()
17
+
18
+ const defaultGenerate = (): string => {
19
+ const buf = new Uint8Array(TOKEN_BYTES)
20
+ crypto.getRandomValues(buf)
21
+
22
+ return [...buf].map((b) => b.toString(16).padStart(2, "0")).join("")
23
+ }
24
+
25
+ /**
26
+ * Reads / generates the gateway daemon token used to authenticate
27
+ * `/listeners*`, `/status`, and `/ws` connections.
28
+ *
29
+ * Token file: `<dir>/gateway.token` (default `~/.funnel/gateway.token`),
30
+ * written with mode 0600. Clients on the same machine as the daemon read
31
+ * the file directly; the token never leaves the user's home directory.
32
+ */
33
+ export class FunnelGatewayToken {
34
+ private readonly fs: FunnelFileSystem
35
+ private readonly path: string
36
+ private readonly generate: () => string
37
+
38
+ constructor(deps: Deps = {}) {
39
+ this.fs = deps.fs ?? defaultFs
40
+ this.path = join(deps.dir ?? FUNNEL_DIR, TOKEN_FILE_NAME)
41
+ this.generate = deps.generate ?? defaultGenerate
42
+ Object.freeze(this)
43
+ }
44
+
45
+ read(): string | null {
46
+ if (!this.fs.existsSync(this.path)) return null
47
+
48
+ const value = this.fs.readFileSync(this.path).trim()
49
+
50
+ return value.length > 0 ? value : null
51
+ }
52
+
53
+ /**
54
+ * Returns the existing token or, if missing, generates one and writes it with mode 0600.
55
+ *
56
+ * NOTE: not atomic — two concurrent `ensure()` calls (e.g., `fnl gateway start` racing
57
+ * itself before the PID lock is acquired) could each generate independent tokens. The
58
+ * gateway PID file makes this practically a non-issue; if you need stronger guarantees,
59
+ * take a file lock around this call externally.
60
+ */
61
+ ensure(): string {
62
+ const existing = this.read()
63
+
64
+ if (existing) return existing
65
+
66
+ const token = this.generate()
67
+
68
+ this.fs.mkdirSync(dirname(this.path), { recursive: true })
69
+ this.fs.writeSecretFileSync(this.path, `${token}\n`)
70
+
71
+ return token
72
+ }
73
+
74
+ getPath(): string {
75
+ return this.path
76
+ }
77
+ }
78
+
79
+ export const DEFAULT_GATEWAY_TOKEN_PATH = join(homedir(), ".funnel", TOKEN_FILE_NAME)
@@ -1,14 +1,19 @@
1
- import { join, resolve } from "node:path"
2
- import { FunnelFileSystem } from "@/modules/fs/funnel-file-system"
3
- import { NodeFunnelFileSystem } from "@/modules/fs/node-funnel-file-system"
4
- import { FunnelProcessRunner } from "@/modules/process/funnel-process-runner"
5
- import { NodeFunnelProcessRunner } from "@/modules/process/node-funnel-process-runner"
6
- import { FUNNEL_DIR } from "@/modules/settings/funnel-settings-store"
7
- import { FunnelClock } from "@/modules/time/funnel-clock"
8
- import { NodeFunnelClock } from "@/modules/time/node-funnel-clock"
1
+ import { join } from "node:path"
2
+ import { FunnelFileSystem } from "@/engine/fs/file-system"
3
+ import { resolveDaemonScript } from "@/gateway/resolve-daemon-script"
4
+ import { NodeFunnelFileSystem } from "@/engine/fs/node-file-system"
5
+ import { FunnelProcessRunner } from "@/engine/process/process-runner"
6
+ import { NodeFunnelProcessRunner } from "@/engine/process/node-process-runner"
7
+ import { FUNNEL_DIR } from "@/engine/settings/settings-store"
8
+ import { FunnelClock } from "@/engine/time/clock"
9
+ import { NodeFunnelClock } from "@/engine/time/node-clock"
9
10
 
10
11
  const DEFAULT_PORT = 9742
11
12
  const DEFAULT_TMP_DIR = "/tmp/funnel"
13
+ const STARTUP_TIMEOUT_MS = 5000
14
+ const SIGTERM_TIMEOUT_MS = 2000
15
+ const POLL_INTERVAL_MS = 100
16
+ const SIGKILL_GRACE_MS = 200
12
17
 
13
18
  type Deps = {
14
19
  process?: FunnelProcessRunner
@@ -78,12 +83,17 @@ export class FunnelGateway {
78
83
 
79
84
  this.fs.mkdirSync(this.tmpDir, { recursive: true })
80
85
 
81
- const gatewayScript = resolve(import.meta.dir, "./daemon.ts")
86
+ const gatewayScript = resolveDaemonScript()
82
87
  const command = this.buildStartCommand(gatewayScript, options)
83
88
 
84
89
  this.process.detach(["bash", "-c", command])
85
90
 
86
- await this.sleep(800)
91
+ const deadline = Date.now() + STARTUP_TIMEOUT_MS
92
+
93
+ while (Date.now() < deadline) {
94
+ if (this.isRunning()) return true
95
+ await this.sleep(POLL_INTERVAL_MS)
96
+ }
87
97
 
88
98
  return this.isRunning()
89
99
  }
@@ -111,7 +121,7 @@ export class FunnelGateway {
111
121
  return false
112
122
  }
113
123
 
114
- const deadline = this.clock.millis() + 2000
124
+ const deadline = this.clock.millis() + SIGTERM_TIMEOUT_MS
115
125
 
116
126
  while (this.clock.millis() < deadline) {
117
127
  if (!this.isProcessAlive(pid)) {
@@ -119,7 +129,7 @@ export class FunnelGateway {
119
129
  return true
120
130
  }
121
131
 
122
- await this.sleep(100)
132
+ await this.sleep(POLL_INTERVAL_MS)
123
133
  }
124
134
 
125
135
  try {
@@ -128,7 +138,7 @@ export class FunnelGateway {
128
138
  // ignore
129
139
  }
130
140
 
131
- await this.sleep(200)
141
+ await this.sleep(SIGKILL_GRACE_MS)
132
142
  this.removePid()
133
143
 
134
144
  return !this.isProcessAlive(pid)
@@ -162,6 +172,10 @@ export class FunnelGateway {
162
172
  return this.gatewayLog
163
173
  }
164
174
 
175
+ getPort(): number {
176
+ return this.port
177
+ }
178
+
165
179
  private readPid(): number | null {
166
180
  if (!this.fs.existsSync(this.pidFile)) return null
167
181
 
@@ -1,7 +1,7 @@
1
- import { FunnelLogger } from "@/modules/logger/funnel-logger"
2
- import { NodeFunnelLogger } from "@/modules/logger/node-funnel-logger"
3
- import { FunnelProcessRunner } from "@/modules/process/funnel-process-runner"
4
- import { NodeFunnelProcessRunner } from "@/modules/process/node-funnel-process-runner"
1
+ import { FunnelLogger } from "@/engine/logger/logger"
2
+ import { NodeFunnelLogger } from "@/engine/logger/node-logger"
3
+ import { FunnelProcessRunner } from "@/engine/process/process-runner"
4
+ import { NodeFunnelProcessRunner } from "@/engine/process/node-process-runner"
5
5
 
6
6
  type Props = {
7
7
  selfPid: number