@interactive-inc/claude-funnel 0.4.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 +233 -111
  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} +57 -22
  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} +39 -14
  69. package/lib/engine/channels/channels.ts +520 -0
  70. package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +47 -62
  71. package/lib/engine/claude/gateway-controller.ts +4 -0
  72. package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +9 -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/engine/id/id-generator.ts +7 -0
  78. package/lib/engine/id/memory-id-generator.ts +20 -0
  79. package/lib/engine/id/node-id-generator.ts +7 -0
  80. package/lib/engine/logger/logger.ts +11 -0
  81. package/lib/engine/logger/memory-logger.ts +28 -0
  82. package/lib/engine/logger/node-logger.ts +49 -0
  83. package/lib/engine/logger/noop-logger.ts +9 -0
  84. package/lib/engine/mcp/channel-server.ts +204 -0
  85. package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +29 -10
  86. package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
  87. package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
  88. package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +5 -0
  89. package/lib/engine/profiles/profile-channel-checker.ts +7 -0
  90. package/lib/engine/profiles/profiles.ts +126 -0
  91. package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
  92. package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
  93. package/lib/engine/settings/settings-schema.ts +46 -0
  94. package/lib/engine/settings/settings-store.ts +110 -0
  95. package/lib/engine/time/clock.ts +15 -0
  96. package/lib/engine/time/memory-clock.ts +26 -0
  97. package/lib/engine/time/node-clock.ts +7 -0
  98. package/lib/funnel.ts +148 -56
  99. package/lib/gateway/auth-middleware.ts +44 -0
  100. package/lib/gateway/broadcaster.ts +319 -0
  101. package/lib/gateway/daemon.ts +47 -0
  102. package/lib/gateway/factory.ts +10 -0
  103. package/lib/gateway/funnel-event-store.ts +155 -0
  104. package/lib/gateway/gateway-server.ts +414 -0
  105. package/lib/gateway/gateway-token.ts +79 -0
  106. package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +70 -27
  107. package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +7 -3
  108. package/lib/gateway/listener-supervisor.ts +339 -0
  109. package/lib/gateway/listeners-client.ts +128 -0
  110. package/lib/gateway/resolve-daemon-script.ts +26 -0
  111. package/lib/gateway/routes/channels.connectors.call.ts +39 -0
  112. package/lib/gateway/routes/health.ts +13 -0
  113. package/lib/gateway/routes/index.ts +24 -0
  114. package/lib/gateway/routes/listeners.list.ts +6 -0
  115. package/lib/gateway/routes/listeners.restart.ts +15 -0
  116. package/lib/gateway/routes/listeners.start.ts +15 -0
  117. package/lib/gateway/routes/listeners.stop.ts +15 -0
  118. package/lib/gateway/routes/route-deps.ts +11 -0
  119. package/lib/gateway/routes/status.ts +15 -0
  120. package/lib/gateway/routes/validator.ts +17 -0
  121. package/lib/index.ts +50 -92
  122. package/lib/logger/leuco-human-file-writer.ts +65 -0
  123. package/lib/logger/leuco-human-logger.ts +98 -0
  124. package/lib/logger/leuco-human-record.ts +16 -0
  125. package/lib/logger/leuco-human-stdout-writer.ts +26 -0
  126. package/lib/logger/leuco-human-writer.ts +14 -0
  127. package/lib/logger/leuco-logger-memory-sink.ts +67 -0
  128. package/lib/logger/leuco-logger-record.ts +13 -0
  129. package/lib/logger/leuco-logger-sink.ts +33 -0
  130. package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
  131. package/lib/logger/leuco-logger.ts +135 -0
  132. package/lib/tui/app.tsx +357 -0
  133. package/lib/tui/components/add-row.tsx +18 -0
  134. package/lib/tui/components/brand.tsx +27 -0
  135. package/lib/tui/components/card.tsx +44 -0
  136. package/lib/tui/components/detail-bar.tsx +46 -0
  137. package/lib/tui/components/editable-field.tsx +33 -0
  138. package/lib/tui/components/empty-state.tsx +11 -0
  139. package/lib/tui/components/gateway-status.tsx +66 -0
  140. package/lib/tui/components/keymap.tsx +29 -0
  141. package/lib/tui/components/menu-item.tsx +73 -0
  142. package/lib/tui/components/menu.tsx +26 -0
  143. package/lib/tui/components/panel-header.tsx +22 -0
  144. package/lib/tui/components/readonly-field.tsx +18 -0
  145. package/lib/tui/components/section-header.tsx +25 -0
  146. package/lib/tui/components/selection-accent.tsx +32 -0
  147. package/lib/tui/components/session-item.tsx +33 -0
  148. package/lib/tui/components/session-list.tsx +33 -0
  149. package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
  150. package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
  151. package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
  152. package/lib/tui/components/ui/hascii/badge.tsx +51 -0
  153. package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
  154. package/lib/tui/components/ui/hascii/button.tsx +194 -0
  155. package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
  156. package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
  157. package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
  158. package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
  159. package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
  160. package/lib/tui/components/ui/hascii/card.tsx +27 -0
  161. package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
  162. package/lib/tui/components/ui/hascii/command.tsx +159 -0
  163. package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
  164. package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
  165. package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
  166. package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
  167. package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
  168. package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
  169. package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
  170. package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
  171. package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
  172. package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
  173. package/lib/tui/components/ui/hascii/input.tsx +130 -0
  174. package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
  175. package/lib/tui/components/ui/hascii/progress.tsx +28 -0
  176. package/lib/tui/components/ui/hascii/select.tsx +131 -0
  177. package/lib/tui/components/ui/hascii/separator.tsx +35 -0
  178. package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
  179. package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
  180. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
  181. package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
  182. package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
  183. package/lib/tui/components/ui/hascii/slider.tsx +91 -0
  184. package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
  185. package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
  186. package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
  187. package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
  188. package/lib/tui/components/ui/hascii/switch.tsx +66 -0
  189. package/lib/tui/components/ui/hascii/table.tsx +95 -0
  190. package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
  191. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
  192. package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
  193. package/lib/tui/components/ui/hascii/tree.tsx +104 -0
  194. package/lib/tui/components/view-shell.tsx +44 -0
  195. package/lib/tui/filter-input.tsx +33 -0
  196. package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
  197. package/lib/tui/parse-comma-list.ts +14 -0
  198. package/lib/tui/profile-launcher.tsx +61 -0
  199. package/lib/tui/scrollbar-options.ts +19 -0
  200. package/lib/tui/sidebar.tsx +50 -0
  201. package/lib/tui/theme.ts +40 -0
  202. package/lib/tui/tui.tsx +20 -0
  203. package/lib/tui/types.ts +38 -0
  204. package/lib/tui/unique-name.ts +18 -0
  205. package/lib/tui/use-event-stream.ts +133 -0
  206. package/lib/tui/use-snapshot.ts +99 -0
  207. package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
  208. package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
  209. package/lib/tui/utils/hascii/theme-context.tsx +26 -0
  210. package/lib/tui/utils/hascii/theme.ts +176 -0
  211. package/lib/tui/views/channels-view.tsx +108 -0
  212. package/lib/tui/views/connectors-view.tsx +164 -0
  213. package/lib/tui/views/events-view.tsx +160 -0
  214. package/lib/tui/views/listeners-view.tsx +80 -0
  215. package/lib/tui/views/profiles-view.tsx +152 -0
  216. package/package.json +51 -34
  217. package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
  218. package/lib/modules/channels/funnel-channels.ts +0 -155
  219. package/lib/modules/connectors/connector-config-schema.ts +0 -16
  220. package/lib/modules/connectors/connector-existence-checker.ts +0 -3
  221. package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
  222. package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
  223. package/lib/modules/connectors/funnel-connector-stores.ts +0 -24
  224. package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
  225. package/lib/modules/connectors/funnel-connectors.ts +0 -145
  226. package/lib/modules/connectors/funnel-discord-listener.ts +0 -65
  227. package/lib/modules/connectors/funnel-discord-store.ts +0 -84
  228. package/lib/modules/connectors/funnel-gh-store.ts +0 -84
  229. package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
  230. package/lib/modules/connectors/funnel-schedule-listener.ts +0 -124
  231. package/lib/modules/connectors/funnel-schedule-store.ts +0 -178
  232. package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
  233. package/lib/modules/connectors/funnel-slack-store.ts +0 -86
  234. package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -77
  235. package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
  236. package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
  237. package/lib/modules/gateway/daemon.ts +0 -207
  238. package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
  239. package/lib/modules/gateway/funnel-event-logger.ts +0 -59
  240. package/lib/modules/logger.ts +0 -26
  241. package/lib/modules/mcp/channel-server.ts +0 -76
  242. package/lib/modules/profiles/funnel-profiles.ts +0 -123
  243. package/lib/modules/profiles/profile-channel-checker.ts +0 -3
  244. package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
  245. package/lib/modules/repos/funnel-repositories.ts +0 -107
  246. package/lib/modules/schedule/funnel-schedule.ts +0 -34
  247. package/lib/modules/settings/funnel-settings-store.ts +0 -56
  248. package/lib/modules/settings/settings-schema.ts +0 -33
  249. package/lib/modules/tui/app.tsx +0 -44
  250. package/lib/modules/tui/tui.tsx +0 -13
  251. package/lib/routes/channels/add.help.ts +0 -3
  252. package/lib/routes/channels/add.ts +0 -21
  253. package/lib/routes/channels/connectors-attach.help.ts +0 -3
  254. package/lib/routes/channels/connectors-attach.ts +0 -17
  255. package/lib/routes/channels/connectors-detach.help.ts +0 -3
  256. package/lib/routes/channels/connectors-detach.ts +0 -17
  257. package/lib/routes/channels/group.help.ts +0 -16
  258. package/lib/routes/channels/group.ts +0 -22
  259. package/lib/routes/channels/remove.help.ts +0 -3
  260. package/lib/routes/channels/remove.ts +0 -17
  261. package/lib/routes/channels/rename.help.ts +0 -5
  262. package/lib/routes/channels/rename.ts +0 -17
  263. package/lib/routes/channels/routes.ts +0 -19
  264. package/lib/routes/channels/show.help.ts +0 -1
  265. package/lib/routes/channels/show.ts +0 -26
  266. package/lib/routes/claude/claude.help.ts +0 -16
  267. package/lib/routes/claude/claude.ts +0 -76
  268. package/lib/routes/claude/routes.ts +0 -4
  269. package/lib/routes/connectors/add.help.ts +0 -28
  270. package/lib/routes/connectors/add.ts +0 -64
  271. package/lib/routes/connectors/group.help.ts +0 -14
  272. package/lib/routes/connectors/group.ts +0 -18
  273. package/lib/routes/connectors/remove.help.ts +0 -3
  274. package/lib/routes/connectors/remove.ts +0 -17
  275. package/lib/routes/connectors/rename.help.ts +0 -5
  276. package/lib/routes/connectors/rename.ts +0 -17
  277. package/lib/routes/connectors/routes.ts +0 -23
  278. package/lib/routes/connectors/schedules-add.help.ts +0 -11
  279. package/lib/routes/connectors/schedules-add.ts +0 -33
  280. package/lib/routes/connectors/schedules-group.help.ts +0 -1
  281. package/lib/routes/connectors/schedules-group.ts +0 -38
  282. package/lib/routes/connectors/schedules-remove.help.ts +0 -3
  283. package/lib/routes/connectors/schedules-remove.ts +0 -17
  284. package/lib/routes/connectors/set.help.ts +0 -8
  285. package/lib/routes/connectors/set.ts +0 -72
  286. package/lib/routes/connectors/show.help.ts +0 -1
  287. package/lib/routes/connectors/show.ts +0 -41
  288. package/lib/routes/gateway/group.help.ts +0 -15
  289. package/lib/routes/gateway/group.ts +0 -28
  290. package/lib/routes/gateway/logs.help.ts +0 -13
  291. package/lib/routes/gateway/logs.ts +0 -100
  292. package/lib/routes/gateway/restart.help.ts +0 -10
  293. package/lib/routes/gateway/routes.ts +0 -18
  294. package/lib/routes/gateway/run.help.ts +0 -12
  295. package/lib/routes/gateway/run.ts +0 -35
  296. package/lib/routes/gateway/start.help.ts +0 -15
  297. package/lib/routes/gateway/start.ts +0 -32
  298. package/lib/routes/gateway/status.help.ts +0 -9
  299. package/lib/routes/gateway/status.ts +0 -28
  300. package/lib/routes/gateway/stop.help.ts +0 -8
  301. package/lib/routes/gateway/stop.ts +0 -21
  302. package/lib/routes/profiles/add.help.ts +0 -3
  303. package/lib/routes/profiles/add.ts +0 -33
  304. package/lib/routes/profiles/group.help.ts +0 -16
  305. package/lib/routes/profiles/group.ts +0 -25
  306. package/lib/routes/profiles/launch.help.ts +0 -4
  307. package/lib/routes/profiles/launch.ts +0 -36
  308. package/lib/routes/profiles/remove.help.ts +0 -3
  309. package/lib/routes/profiles/remove.ts +0 -17
  310. package/lib/routes/profiles/rename.help.ts +0 -5
  311. package/lib/routes/profiles/rename.ts +0 -17
  312. package/lib/routes/profiles/routes.ts +0 -18
  313. package/lib/routes/profiles/set.help.ts +0 -5
  314. package/lib/routes/profiles/set.ts +0 -32
  315. package/lib/routes/repos/add.help.ts +0 -6
  316. package/lib/routes/repos/add.ts +0 -20
  317. package/lib/routes/repos/group.help.ts +0 -11
  318. package/lib/routes/repos/group.ts +0 -18
  319. package/lib/routes/repos/remove.help.ts +0 -3
  320. package/lib/routes/repos/remove.ts +0 -17
  321. package/lib/routes/repos/rename.help.ts +0 -5
  322. package/lib/routes/repos/rename.ts +0 -17
  323. package/lib/routes/repos/routes.ts +0 -17
  324. package/lib/routes/repos/set.help.ts +0 -5
  325. package/lib/routes/repos/set.ts +0 -21
  326. package/lib/routes/repos/show.help.ts +0 -1
  327. package/lib/routes/repos/show.ts +0 -19
  328. package/lib/routes/request/discord-help.ts +0 -9
  329. package/lib/routes/request/discord.help.ts +0 -19
  330. package/lib/routes/request/discord.ts +0 -65
  331. package/lib/routes/request/group.help.ts +0 -15
  332. package/lib/routes/request/group.ts +0 -9
  333. package/lib/routes/request/routes.ts +0 -14
  334. package/lib/routes/request/slack-help.ts +0 -9
  335. package/lib/routes/request/slack.help.ts +0 -19
  336. package/lib/routes/request/slack.ts +0 -61
  337. package/lib/routes/status/routes.ts +0 -4
  338. package/lib/routes/status/status.help.ts +0 -6
  339. package/lib/routes/status/status.ts +0 -77
  340. package/lib/routes/update/routes.ts +0 -4
  341. package/lib/routes/update/update.help.ts +0 -5
  342. package/lib/routes/update/update.ts +0 -21
  343. package/lib/routes.ts +0 -40
  344. /package/lib/{factory.ts → cli/factory.ts} +0 -0
  345. /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
  346. /package/lib/{modules → cli}/router/validator.ts +0 -0
  347. /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
  348. /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
  349. /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
@@ -1,58 +1,65 @@
1
1
  import { join } from "node:path"
2
- import type { FunnelChannels } from "@/modules/channels/funnel-channels"
3
- import { FunnelFileSystem } from "@/modules/fs/funnel-file-system"
4
- import { NodeFunnelFileSystem } from "@/modules/fs/node-funnel-file-system"
5
- import type { FunnelGateway } from "@/modules/gateway/funnel-gateway"
6
- import { logger } from "@/modules/logger"
7
- import type { FunnelMcp } from "@/modules/mcp/funnel-mcp"
8
- import { FunnelProcessRunner } from "@/modules/process/funnel-process-runner"
9
- import { NodeFunnelProcessRunner } from "@/modules/process/node-funnel-process-runner"
10
- import type { FunnelRepositories } from "@/modules/repos/funnel-repositories"
11
- import { FUNNEL_DIR } from "@/modules/settings/funnel-settings-store"
12
-
13
- const CLAUDE_PID_DIR = join(FUNNEL_DIR, "claude")
2
+ import type { FunnelChannels } from "@/engine/channels/channels"
3
+ import type { GatewayController } from "@/engine/claude/gateway-controller"
4
+ import { FunnelFileSystem } from "@/engine/fs/file-system"
5
+ import { NodeFunnelFileSystem } from "@/engine/fs/node-file-system"
6
+ import { FunnelLogger } from "@/engine/logger/logger"
7
+ import { NodeFunnelLogger } from "@/engine/logger/node-logger"
8
+ import type { FunnelMcp } from "@/engine/mcp/mcp"
9
+ import { FunnelProcessRunner } from "@/engine/process/process-runner"
10
+ import { NodeFunnelProcessRunner } from "@/engine/process/node-process-runner"
11
+ import { FUNNEL_DIR } from "@/engine/settings/settings-store"
14
12
 
15
13
  export type LaunchOptions = {
16
14
  channel: string
17
- repo?: string
15
+ cwd?: string
18
16
  subAgent?: string
19
- envFiles?: string[]
20
17
  userArgs?: string[]
21
18
  profileName?: string
22
19
  }
23
20
 
24
21
  type Deps = {
25
22
  channels: FunnelChannels
26
- repositories: FunnelRepositories
27
23
  mcp: FunnelMcp
28
- gateway: FunnelGateway
24
+ gateway: GatewayController
29
25
  process?: FunnelProcessRunner
30
26
  fs?: FunnelFileSystem
27
+ logger?: FunnelLogger
28
+ dir?: string
31
29
  }
32
30
 
33
31
  const defaultProcess = new NodeFunnelProcessRunner()
34
32
  const defaultFs = new NodeFunnelFileSystem()
35
-
33
+ const defaultLogger = new NodeFunnelLogger()
34
+
35
+ /**
36
+ * Launches Claude Code with funnel pre-wired: ensures the gateway is running,
37
+ * installs the funnel MCP into the target repo's `.mcp.json` if missing,
38
+ * injects `FUNNEL_CHANNEL_ID` into the child env, and writes a per-profile
39
+ * PID file to enforce singleton launches.
40
+ */
36
41
  export class FunnelClaude {
37
42
  private readonly channels: FunnelChannels
38
- private readonly repositories: FunnelRepositories
39
43
  private readonly mcp: FunnelMcp
40
- private readonly gateway: FunnelGateway
44
+ private readonly gateway: GatewayController
41
45
  private readonly process: FunnelProcessRunner
42
46
  private readonly fs: FunnelFileSystem
47
+ private readonly logger: FunnelLogger
48
+ private readonly pidDir: string
43
49
 
44
50
  constructor(deps: Deps) {
45
51
  this.channels = deps.channels
46
- this.repositories = deps.repositories
47
52
  this.mcp = deps.mcp
48
53
  this.gateway = deps.gateway
49
54
  this.process = deps.process ?? defaultProcess
50
55
  this.fs = deps.fs ?? defaultFs
56
+ this.logger = deps.logger ?? defaultLogger
57
+ this.pidDir = join(deps.dir ?? FUNNEL_DIR, "claude")
51
58
  Object.freeze(this)
52
59
  }
53
60
 
54
61
  async launch(options: LaunchOptions): Promise<number> {
55
- const channel = this.channels.get(options.channel)
62
+ const channel = this.channels.get(options.channel) ?? this.channels.getById(options.channel)
56
63
 
57
64
  if (!channel) {
58
65
  throw new Error(`channel "${options.channel}" not found`)
@@ -62,18 +69,16 @@ export class FunnelClaude {
62
69
  throw new Error(`profile "${options.profileName}" is already running`)
63
70
  }
64
71
 
65
- const cwd = options.repo
66
- ? this.repositories.resolvePath(options.repo)
67
- : globalThis.process.cwd()
72
+ const cwd = options.cwd ?? globalThis.process.cwd()
68
73
 
69
74
  if (!this.mcp.findInstalledName(cwd)) {
70
75
  this.mcp.install(cwd)
71
76
 
72
- logger.info(`added funnel MCP to .mcp.json`, { cwd })
77
+ this.logger.info(`added funnel MCP to .mcp.json`, { cwd })
73
78
  }
74
79
 
75
80
  if (!this.gateway.isRunning()) {
76
- logger.info(`starting gateway automatically`)
81
+ this.logger.info(`starting gateway automatically`)
77
82
  await this.gateway.start()
78
83
  }
79
84
 
@@ -83,11 +88,11 @@ export class FunnelClaude {
83
88
  }
84
89
 
85
90
  const claudeArgs = this.buildArgs(options, cwd)
86
- const env = this.buildEnv(options, cwd)
91
+ const env = this.buildEnv(channel.id)
87
92
 
88
- logger.info(`claude launch`, {
93
+ this.logger.info(`claude launch`, {
89
94
  channel: options.channel,
90
- repo: options.repo,
95
+ channelId: channel.id,
91
96
  subAgent: options.subAgent,
92
97
  cwd,
93
98
  })
@@ -108,7 +113,7 @@ export class FunnelClaude {
108
113
  }
109
114
 
110
115
  private pidPath(profileName: string): string {
111
- return join(CLAUDE_PID_DIR, `${profileName}.pid`)
116
+ return join(this.pidDir, `${profileName}.pid`)
112
117
  }
113
118
 
114
119
  private readPid(profileName: string): number | null {
@@ -129,7 +134,7 @@ export class FunnelClaude {
129
134
  }
130
135
 
131
136
  private writePidFile(profileName: string): void {
132
- this.fs.mkdirSync(CLAUDE_PID_DIR, { recursive: true })
137
+ this.fs.mkdirSync(this.pidDir, { recursive: true })
133
138
  this.fs.writeFileSync(this.pidPath(profileName), String(globalThis.process.pid))
134
139
  }
135
140
 
@@ -140,11 +145,12 @@ export class FunnelClaude {
140
145
  }
141
146
 
142
147
  private installCleanup(profileName: string): void {
143
- const cleanup = () => this.removePidFile(profileName)
144
-
145
- globalThis.process.once("exit", cleanup)
146
- globalThis.process.once("SIGINT", cleanup)
147
- globalThis.process.once("SIGTERM", cleanup)
148
+ // Default Bun behavior on SIGINT/SIGTERM is process.exit(130/143), which
149
+ // fires the "exit" event. Hooking only "exit" keeps the PID file cleanup
150
+ // running while letting the signal terminate the process normally —
151
+ // adding our own SIGINT handler would suppress the default exit and leave
152
+ // funnel hanging until claude responds.
153
+ globalThis.process.once("exit", () => this.removePidFile(profileName))
148
154
  }
149
155
 
150
156
  private isProcessAlive(pid: number): boolean {
@@ -179,35 +185,14 @@ export class FunnelClaude {
179
185
  return result
180
186
  }
181
187
 
182
- private buildEnv(options: LaunchOptions, cwd: string): Record<string, string> {
183
- const env: Record<string, string> = { ...globalThis.process.env } as Record<string, string>
184
-
185
- if (options.envFiles) {
186
- for (const file of options.envFiles) {
187
- const filePath = `${cwd}/${file}`
188
-
189
- if (!this.fs.existsSync(filePath)) continue
190
-
191
- const content = this.fs.readFileSync(filePath)
192
-
193
- for (const line of content.split("\n")) {
194
- const trimmed = line.trim()
195
-
196
- if (!trimmed || trimmed.startsWith("#")) continue
197
-
198
- const eqIndex = trimmed.indexOf("=")
199
-
200
- if (eqIndex < 0) continue
201
-
202
- const key = trimmed.slice(0, eqIndex)
203
- const value = trimmed.slice(eqIndex + 1).replace(/^["']|["']$/g, "")
188
+ private buildEnv(channelId: string): Record<string, string> {
189
+ const env: Record<string, string> = {}
204
190
 
205
- env[key] = value
206
- }
207
- }
191
+ for (const [key, value] of Object.entries(globalThis.process.env)) {
192
+ if (typeof value === "string") env[key] = value
208
193
  }
209
194
 
210
- env.FUNNEL_CHANNEL_ID = options.channel
195
+ env.FUNNEL_CHANNEL_ID = channelId
211
196
 
212
197
  return env
213
198
  }
@@ -0,0 +1,4 @@
1
+ export type GatewayController = {
2
+ isRunning(): boolean
3
+ start(options?: { caffeinate?: boolean }): Promise<boolean>
4
+ }
@@ -1,11 +1,20 @@
1
1
  export type FileStat = {
2
2
  mtimeMs: number
3
+ /** POSIX mode bits (e.g. 0o600). `null` when the underlying FS does not expose mode. */
4
+ mode: number | null
3
5
  }
4
6
 
7
+ /**
8
+ * Filesystem boundary used everywhere funnel reads or writes.
9
+ * Default is NodeFunnelFileSystem (real `node:fs`); MemoryFunnelFileSystem
10
+ * provides a sandbox for tests and embedded use.
11
+ */
5
12
  export abstract class FunnelFileSystem {
6
13
  abstract existsSync(path: string): boolean
7
14
  abstract readFileSync(path: string): string
8
15
  abstract writeFileSync(path: string, data: string): void
16
+ /** Write `data` and ensure the resulting file is owner-only (0600). Use for tokens and any file that may contain secrets. */
17
+ abstract writeSecretFileSync(path: string, data: string): void
9
18
  abstract appendFileSync(path: string, data: string): void
10
19
  abstract unlink(path: string): void
11
20
  abstract mkdirSync(path: string, options?: { recursive?: boolean }): void
@@ -1,16 +1,20 @@
1
- import { type FileStat, FunnelFileSystem } from "@/modules/fs/funnel-file-system"
1
+ import { type FileStat, FunnelFileSystem } from "@/engine/fs/file-system"
2
2
 
3
3
  type Props = {
4
4
  dirs?: string[]
5
5
  files?: Record<string, string>
6
6
  mtimes?: Record<string, number>
7
+ modes?: Record<string, number>
7
8
  now?: () => number
8
9
  }
9
10
 
11
+ const SECRET_MODE = 0o600
12
+
10
13
  export class MemoryFunnelFileSystem extends FunnelFileSystem {
11
14
  private readonly dirs: Set<string>
12
15
  private readonly files: Map<string, string>
13
16
  private readonly mtimes: Map<string, number>
17
+ private readonly modes: Map<string, number>
14
18
  private readonly now: () => number
15
19
 
16
20
  constructor(props: Props = {}) {
@@ -18,6 +22,7 @@ export class MemoryFunnelFileSystem extends FunnelFileSystem {
18
22
  this.dirs = new Set(props.dirs ?? [])
19
23
  this.files = new Map(Object.entries(props.files ?? {}))
20
24
  this.mtimes = new Map(Object.entries(props.mtimes ?? {}))
25
+ this.modes = new Map(Object.entries(props.modes ?? {}))
21
26
  this.now = props.now ?? (() => Date.now())
22
27
  }
23
28
 
@@ -34,6 +39,12 @@ export class MemoryFunnelFileSystem extends FunnelFileSystem {
34
39
  this.touch(path)
35
40
  }
36
41
 
42
+ writeSecretFileSync(path: string, data: string): void {
43
+ this.files.set(path, data)
44
+ this.modes.set(path, SECRET_MODE)
45
+ this.touch(path)
46
+ }
47
+
37
48
  appendFileSync(path: string, data: string): void {
38
49
  const prev = this.files.get(path) ?? ""
39
50
  this.files.set(path, prev + data)
@@ -43,9 +54,11 @@ export class MemoryFunnelFileSystem extends FunnelFileSystem {
43
54
  unlink(path: string): void {
44
55
  this.files.delete(path)
45
56
  this.mtimes.delete(path)
57
+ this.modes.delete(path)
46
58
  }
47
59
 
48
- mkdirSync(path: string): void {
60
+ mkdirSync(path: string, options?: { recursive?: boolean }): void {
61
+ void options
49
62
  this.dirs.add(path)
50
63
  }
51
64
 
@@ -71,13 +84,17 @@ export class MemoryFunnelFileSystem extends FunnelFileSystem {
71
84
  throw new Error(`not found: ${path}`)
72
85
  }
73
86
 
74
- return { mtimeMs }
87
+ return { mtimeMs, mode: this.modes.get(path) ?? null }
75
88
  }
76
89
 
77
90
  setMtime(path: string, mtimeMs: number): void {
78
91
  this.mtimes.set(path, mtimeMs)
79
92
  }
80
93
 
94
+ setMode(path: string, mode: number): void {
95
+ this.modes.set(path, mode)
96
+ }
97
+
81
98
  private touch(path: string): void {
82
99
  if (!this.mtimes.has(path)) this.mtimes.set(path, this.now())
83
100
  else this.mtimes.set(path, this.now())
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  appendFileSync,
3
+ chmodSync,
3
4
  existsSync,
4
5
  mkdirSync,
5
6
  readdirSync,
@@ -8,7 +9,9 @@ import {
8
9
  unlinkSync,
9
10
  writeFileSync,
10
11
  } from "node:fs"
11
- import { type FileStat, FunnelFileSystem } from "@/modules/fs/funnel-file-system"
12
+ import { type FileStat, FunnelFileSystem } from "@/engine/fs/file-system"
13
+
14
+ const SECRET_MODE = 0o600
12
15
 
13
16
  export class NodeFunnelFileSystem extends FunnelFileSystem {
14
17
  constructor() {
@@ -28,6 +31,15 @@ export class NodeFunnelFileSystem extends FunnelFileSystem {
28
31
  writeFileSync(path, data)
29
32
  }
30
33
 
34
+ writeSecretFileSync(path: string, data: string): void {
35
+ writeFileSync(path, data, { mode: SECRET_MODE })
36
+ try {
37
+ chmodSync(path, SECRET_MODE)
38
+ } catch {
39
+ // ignore — best-effort tightening for files that already existed with looser perms
40
+ }
41
+ }
42
+
31
43
  appendFileSync(path: string, data: string): void {
32
44
  appendFileSync(path, data)
33
45
  }
@@ -51,6 +63,6 @@ export class NodeFunnelFileSystem extends FunnelFileSystem {
51
63
  statSync(path: string): FileStat {
52
64
  const stat = statSync(path)
53
65
 
54
- return { mtimeMs: stat.mtimeMs }
66
+ return { mtimeMs: stat.mtimeMs, mode: stat.mode & 0o777 }
55
67
  }
56
68
  }
@@ -1,8 +1,4 @@
1
- import {
2
- FunnelHttpClient,
3
- type HttpRequest,
4
- type HttpResponse,
5
- } from "@/modules/http/funnel-http-client"
1
+ import { FunnelHttpClient, type HttpRequest, type HttpResponse } from "@/engine/http/http-client"
6
2
 
7
3
  export type MemoryHttpResponse = {
8
4
  status?: number
@@ -1,8 +1,4 @@
1
- import {
2
- FunnelHttpClient,
3
- type HttpRequest,
4
- type HttpResponse,
5
- } from "@/modules/http/funnel-http-client"
1
+ import { FunnelHttpClient, type HttpRequest, type HttpResponse } from "@/engine/http/http-client"
6
2
 
7
3
  export class NodeFunnelHttpClient extends FunnelHttpClient {
8
4
  constructor() {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ID generator boundary. Default NodeFunnelIdGenerator wraps `crypto.randomUUID()`;
3
+ * MemoryFunnelIdGenerator emits `<prefix>-1, <prefix>-2, ...` for deterministic tests.
4
+ */
5
+ export abstract class FunnelIdGenerator {
6
+ abstract generate(): string
7
+ }
@@ -0,0 +1,20 @@
1
+ import { FunnelIdGenerator } from "@/engine/id/id-generator"
2
+
3
+ type Props = {
4
+ prefix?: string
5
+ }
6
+
7
+ export class MemoryFunnelIdGenerator extends FunnelIdGenerator {
8
+ private counter = 0
9
+ private readonly prefix: string
10
+
11
+ constructor(props: Props = {}) {
12
+ super()
13
+ this.prefix = props.prefix ?? "id"
14
+ }
15
+
16
+ generate(): string {
17
+ this.counter++
18
+ return `${this.prefix}-${this.counter}`
19
+ }
20
+ }
@@ -0,0 +1,7 @@
1
+ import { FunnelIdGenerator } from "@/engine/id/id-generator"
2
+
3
+ export class NodeFunnelIdGenerator extends FunnelIdGenerator {
4
+ generate(): string {
5
+ return crypto.randomUUID()
6
+ }
7
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Structured logger with three levels and an optional log-file path.
3
+ * Defaults to NodeFunnelLogger (appends to /tmp/funnel/funnel.log);
4
+ * MemoryFunnelLogger captures entries in memory and NoopFunnelLogger silences output.
5
+ */
6
+ export abstract class FunnelLogger {
7
+ abstract info(message: string, meta?: Record<string, unknown>): void
8
+ abstract warn(message: string, meta?: Record<string, unknown>): void
9
+ abstract error(message: string, meta?: Record<string, unknown>): void
10
+ abstract readonly file: string | null
11
+ }
@@ -0,0 +1,28 @@
1
+ import { FunnelLogger } from "@/engine/logger/logger"
2
+
3
+ export type LogEntry = {
4
+ level: "info" | "warn" | "error"
5
+ message: string
6
+ meta?: Record<string, unknown>
7
+ }
8
+
9
+ export class MemoryFunnelLogger extends FunnelLogger {
10
+ readonly file = null
11
+ readonly entries: LogEntry[] = []
12
+
13
+ info(message: string, meta?: Record<string, unknown>): void {
14
+ this.entries.push({ level: "info", message, meta })
15
+ }
16
+
17
+ warn(message: string, meta?: Record<string, unknown>): void {
18
+ this.entries.push({ level: "warn", message, meta })
19
+ }
20
+
21
+ error(message: string, meta?: Record<string, unknown>): void {
22
+ this.entries.push({ level: "error", message, meta })
23
+ }
24
+
25
+ clear(): void {
26
+ this.entries.length = 0
27
+ }
28
+ }
@@ -0,0 +1,49 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs"
2
+ import { dirname, join } from "node:path"
3
+ import { FunnelLogger } from "@/engine/logger/logger"
4
+
5
+ const DEFAULT_LOG_FILE = join("/tmp/funnel", "funnel.log")
6
+
7
+ type Level = "info" | "warn" | "error"
8
+
9
+ type Props = {
10
+ file?: string
11
+ now?: () => Date
12
+ }
13
+
14
+ export class NodeFunnelLogger extends FunnelLogger {
15
+ readonly file: string
16
+ private readonly now: () => Date
17
+
18
+ constructor(props: Props = {}) {
19
+ super()
20
+ this.file = props.file ?? DEFAULT_LOG_FILE
21
+ this.now = props.now ?? (() => new Date())
22
+ Object.freeze(this)
23
+ }
24
+
25
+ info(message: string, meta?: Record<string, unknown>): void {
26
+ this.write("info", message, meta)
27
+ }
28
+
29
+ warn(message: string, meta?: Record<string, unknown>): void {
30
+ this.write("warn", message, meta)
31
+ }
32
+
33
+ error(message: string, meta?: Record<string, unknown>): void {
34
+ this.write("error", message, meta)
35
+ }
36
+
37
+ private write(level: Level, message: string, meta?: Record<string, unknown>): void {
38
+ mkdirSync(dirname(this.file), { recursive: true })
39
+
40
+ const entry = {
41
+ time: this.now().toISOString(),
42
+ level,
43
+ message,
44
+ ...(meta ? { meta } : {}),
45
+ }
46
+
47
+ appendFileSync(this.file, `${JSON.stringify(entry)}\n`)
48
+ }
49
+ }
@@ -0,0 +1,9 @@
1
+ import { FunnelLogger } from "@/engine/logger/logger"
2
+
3
+ export class NoopFunnelLogger extends FunnelLogger {
4
+ readonly file = null
5
+
6
+ info(): void {}
7
+ warn(): void {}
8
+ error(): void {}
9
+ }