@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,34 +0,0 @@
1
- import type { FunnelScheduleStore } from "@/modules/connectors/funnel-schedule-store"
2
- import type { ScheduleEntry } from "@/modules/connectors/schedule-connector-schema"
3
-
4
- type Deps = {
5
- store: FunnelScheduleStore
6
- }
7
-
8
- export class FunnelSchedule {
9
- private readonly store: FunnelScheduleStore
10
-
11
- constructor(deps: Deps) {
12
- this.store = deps.store
13
- Object.freeze(this)
14
- }
15
-
16
- listEntries(connector: string): ScheduleEntry[] {
17
- const config = this.store.get(connector)
18
-
19
- if (!config) throw new Error(`connector "${connector}" not found`)
20
-
21
- return config.entries
22
- }
23
-
24
- addEntry(
25
- connector: string,
26
- entry: Omit<ScheduleEntry, "id"> & { id?: string },
27
- ): ScheduleEntry {
28
- return this.store.addEntry(connector, entry)
29
- }
30
-
31
- removeEntry(connector: string, id: string): void {
32
- this.store.removeEntry(connector, id)
33
- }
34
- }
@@ -1,56 +0,0 @@
1
- import { homedir } from "node:os"
2
- import { dirname, join } from "node:path"
3
- import { FunnelFileSystem } from "@/modules/fs/funnel-file-system"
4
- import { NodeFunnelFileSystem } from "@/modules/fs/node-funnel-file-system"
5
- import { FunnelSettingsReader } from "@/modules/settings/funnel-settings-reader"
6
- import { settingsSchema } from "@/modules/settings/settings-schema"
7
- import type { Settings } from "@/modules/settings/settings-schema"
8
-
9
- export const FUNNEL_DIR = join(homedir(), ".funnel")
10
- export const SETTINGS_PATH = join(FUNNEL_DIR, "settings.json")
11
-
12
- type Deps = {
13
- path?: string
14
- fs?: FunnelFileSystem
15
- }
16
-
17
- const defaultFs = new NodeFunnelFileSystem()
18
-
19
- export class FunnelSettingsStore extends FunnelSettingsReader {
20
- private readonly path: string
21
- private readonly fs: FunnelFileSystem
22
-
23
- constructor(deps: Deps = {}) {
24
- super()
25
- this.path = deps.path ?? SETTINGS_PATH
26
- this.fs = deps.fs ?? defaultFs
27
- Object.freeze(this)
28
- }
29
-
30
- read(): Settings {
31
- if (!this.fs.existsSync(this.path)) {
32
- return {
33
- channels: [],
34
- repositories: [],
35
- profiles: [],
36
- }
37
- }
38
-
39
- const content = this.fs.readFileSync(this.path)
40
- const parsed = JSON.parse(content)
41
- const result = settingsSchema.safeParse(parsed)
42
-
43
- if (!result.success) {
44
- throw new Error(
45
- `invalid settings.json (${this.path}): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`,
46
- )
47
- }
48
-
49
- return result.data
50
- }
51
-
52
- write(settings: Settings): void {
53
- this.fs.mkdirSync(dirname(this.path), { recursive: true })
54
- this.fs.writeFileSync(this.path, `${JSON.stringify(settings, null, 2)}\n`)
55
- }
56
- }
@@ -1,33 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const channelConfigSchema = z.object({
4
- name: z.string(),
5
- connectors: z.array(z.string()).default([]),
6
- })
7
-
8
- export type ChannelConfig = z.infer<typeof channelConfigSchema>
9
-
10
- export const repositoryConfigSchema = z.object({
11
- name: z.string(),
12
- path: z.string(),
13
- })
14
-
15
- export type RepositoryConfig = z.infer<typeof repositoryConfigSchema>
16
-
17
- export const profileConfigSchema = z.object({
18
- name: z.string(),
19
- channel: z.string(),
20
- repo: z.string().optional(),
21
- subAgent: z.string().optional(),
22
- envFiles: z.array(z.string()).optional(),
23
- })
24
-
25
- export type ProfileConfig = z.infer<typeof profileConfigSchema>
26
-
27
- export const settingsSchema = z.object({
28
- channels: z.array(channelConfigSchema).default([]),
29
- repositories: z.array(repositoryConfigSchema).default([]),
30
- profiles: z.array(profileConfigSchema).default([]),
31
- })
32
-
33
- export type Settings = z.infer<typeof settingsSchema>
@@ -1,44 +0,0 @@
1
- import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/react"
2
-
3
- const zinc = {
4
- 50: "#fafafa",
5
- 600: "#52525b",
6
- 950: "#09090b",
7
- } as const
8
-
9
- const LABEL = "funnel"
10
-
11
- export function App() {
12
- const renderer = useRenderer()
13
-
14
- const dimensions = useTerminalDimensions()
15
-
16
- useKeyboard((key) => {
17
- if (key.name === "q" || key.name === "escape" || (key.ctrl && key.name === "c")) {
18
- renderer.destroy()
19
- }
20
- })
21
-
22
- const marginLeft = Math.max(0, Math.floor((dimensions.width - LABEL.length) / 2))
23
-
24
- const marginTop = Math.max(0, Math.floor(dimensions.height / 2) - 1)
25
-
26
- return (
27
- <box
28
- style={{
29
- width: "100%",
30
- height: "100%",
31
- flexDirection: "column",
32
- backgroundColor: zinc[950],
33
- }}
34
- >
35
- <box style={{ height: marginTop, backgroundColor: zinc[950] }} />
36
- <text fg={zinc[50]} bg={zinc[950]} style={{ marginLeft }}>
37
- {LABEL}
38
- </text>
39
- <text fg={zinc[600]} bg={zinc[950]} position="absolute" bottom={0}>
40
- press q to quit
41
- </text>
42
- </box>
43
- )
44
- }
@@ -1,13 +0,0 @@
1
- import { createCliRenderer } from "@opentui/core"
2
- import { createRoot } from "@opentui/react"
3
- import { App } from "@/modules/tui/app"
4
-
5
- export async function launchTui(): Promise<void> {
6
- const renderer = await createCliRenderer()
7
-
8
- createRoot(renderer).render(<App />)
9
-
10
- await new Promise<void>((resolve) => {
11
- renderer.once("destroy", () => resolve())
12
- })
13
- }
@@ -1,3 +0,0 @@
1
- export const help = `funnel channels add — add a channel
2
-
3
- usage: funnel channels add <name> [--connector <c>]...`
@@ -1,21 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/add.help"
5
-
6
- export const channelsAddHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator("query", z.object({ connector: z.string().optional() }), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const query = c.req.valid("query")
12
- const funnel = c.var.funnel
13
-
14
- funnel.channels.add({
15
- name: param.name,
16
- connectors: query.connector ? [query.connector] : [],
17
- })
18
-
19
- return c.text(`added channel "${param.name}"`)
20
- },
21
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel channels <name> connectors attach — subscribe to a connector
2
-
3
- usage: funnel channels <name> connectors attach <connector>`
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/connectors-attach.help"
5
-
6
- export const channelsConnectorsAttachHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string(), connector: z.string() })),
8
- zValidator("query", z.object({}), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const funnel = c.var.funnel
12
-
13
- funnel.channels.attachConnector(param.name, param.connector)
14
-
15
- return c.text(`attached connector "${param.connector}" to channel "${param.name}"`)
16
- },
17
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel channels <name> connectors detach — unsubscribe from a connector
2
-
3
- usage: funnel channels <name> connectors detach <connector>`
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/connectors-detach.help"
5
-
6
- export const channelsConnectorsDetachHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string(), connector: z.string() })),
8
- zValidator("query", z.object({}), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const funnel = c.var.funnel
12
-
13
- funnel.channels.detachConnector(param.name, param.connector)
14
-
15
- return c.text(`detached connector "${param.connector}" from channel "${param.name}"`)
16
- },
17
- )
@@ -1,16 +0,0 @@
1
- export const help = `funnel channels — manage subscription boxes
2
-
3
- usage: funnel channels [subcommand]
4
-
5
- subcommands:
6
- (none) list
7
- add <name> add
8
- remove <name> remove
9
- <name> show details
10
- <name> connectors attach <c> subscribe to a connector
11
- <name> connectors detach <c> unsubscribe from a connector
12
-
13
- examples:
14
- funnel channels add prod-inbox
15
- funnel channels prod-inbox connectors attach prod-slack
16
- funnel channels prod-inbox`
@@ -1,22 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/group.help"
5
-
6
- export const channelsGroupHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- (c) => {
9
- const funnel = c.var.funnel
10
- const channels = funnel.channels.list()
11
-
12
- if (channels.length === 0) return c.text("no channels")
13
-
14
- const lines = channels.map((ch) => {
15
- const connectors = ch.connectors.length > 0 ? ch.connectors.join(", ") : "(none)"
16
-
17
- return `${ch.name} [${connectors}]`
18
- })
19
-
20
- return c.text(lines.join("\n"))
21
- },
22
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel channels remove — remove a channel
2
-
3
- usage: funnel channels remove <name>`
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/remove.help"
5
-
6
- export const channelsRemoveHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator("query", z.object({}), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const funnel = c.var.funnel
12
-
13
- funnel.channels.remove(param.name)
14
-
15
- return c.text(`removed channel "${param.name}"`)
16
- },
17
- )
@@ -1,5 +0,0 @@
1
- export const help = `funnel channels rename — rename a channel
2
-
3
- usage:
4
- funnel channels rename <old> <new>
5
- funnel channels <old> rename <new>`
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/channels/rename.help"
5
-
6
- export const channelsRenameHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string(), newName: z.string() })),
8
- zValidator("query", z.object({}), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const funnel = c.var.funnel
12
-
13
- funnel.channels.rename(param.name, param["newName"])
14
-
15
- return c.text(`renamed channel "${param.name}" to "${param["newName"]}"`)
16
- },
17
- )
@@ -1,19 +0,0 @@
1
- import { factory } from "@/factory"
2
- import { channelsAddHandler } from "@/routes/channels/add"
3
- import { channelsConnectorsAttachHandler } from "@/routes/channels/connectors-attach"
4
- import { channelsConnectorsDetachHandler } from "@/routes/channels/connectors-detach"
5
- import { channelsGroupHandler } from "@/routes/channels/group"
6
- import { channelsRemoveHandler } from "@/routes/channels/remove"
7
- import { channelsRenameHandler } from "@/routes/channels/rename"
8
- import { channelsShowHandler } from "@/routes/channels/show"
9
-
10
- export const channelsRoutes = factory
11
- .createApp()
12
- .get("/", ...channelsGroupHandler)
13
- .put("/:name/rename/:newName", ...channelsRenameHandler)
14
- .put("/rename/:name/:newName", ...channelsRenameHandler)
15
- .put("/:name/connectors/attach/:connector", ...channelsConnectorsAttachHandler)
16
- .delete("/:name/connectors/detach/:connector", ...channelsConnectorsDetachHandler)
17
- .post("/:name", ...channelsAddHandler)
18
- .delete("/:name", ...channelsRemoveHandler)
19
- .get("/:name", ...channelsShowHandler)
@@ -1 +0,0 @@
1
- export const help = `funnel channels <name> — show channel details`
@@ -1,26 +0,0 @@
1
- import { HTTPException } from "hono/http-exception"
2
- import { z } from "zod"
3
- import { factory } from "@/factory"
4
- import { zValidator } from "@/modules/router/validator"
5
- import { help } from "@/routes/channels/show.help"
6
-
7
- export const channelsShowHandler = factory.createHandlers(
8
- zValidator("param", z.object({ name: z.string() })),
9
- zValidator("query", z.object({}), help),
10
- (c) => {
11
- const param = c.req.valid("param")
12
- const funnel = c.var.funnel
13
- const channel = funnel.channels.get(param.name)
14
-
15
- if (!channel) {
16
- throw new HTTPException(404, { message: `channel "${param.name}" not found` })
17
- }
18
-
19
- const lines = [
20
- `name: ${channel.name}`,
21
- `connectors: ${channel.connectors.length > 0 ? channel.connectors.join(", ") : "(none)"}`,
22
- ]
23
-
24
- return c.text(lines.join("\n"))
25
- },
26
- )
@@ -1,16 +0,0 @@
1
- export const help = `funnel claude — launch Claude Code
2
-
3
- usage:
4
- funnel claude launch "default" profile (or the first profile)
5
- funnel claude --profile <name> launch a named profile
6
- funnel claude --channel <name> [opts] raw launch (no profile)
7
-
8
- options (override profile / raw):
9
- --profile profile name to launch
10
- --channel channel name to subscribe to
11
- --repo switch working directory to the named repo
12
- --sub-agent sub-agent name (passed to claude --agent)
13
- --env-file additional env file to load (relative path)
14
-
15
- Any other arguments are forwarded to the claude CLI.
16
- On launch the FUNNEL_CHANNEL_ID env var is set and MCP connects to the gateway.`
@@ -1,76 +0,0 @@
1
- import { HTTPException } from "hono/http-exception"
2
- import { z } from "zod"
3
- import { factory } from "@/factory"
4
- import { queryToCliArgs } from "@/modules/router/query-to-cli-args"
5
- import { zValidator } from "@/modules/router/validator"
6
- import { help } from "@/routes/claude/claude.help"
7
-
8
- const RESERVED_KEYS = ["profile", "channel", "repo", "sub-agent", "env-file"]
9
-
10
- const DEFAULT_PROFILE_NAME = "default"
11
-
12
- export const claudeHandler = factory.createHandlers(
13
- zValidator(
14
- "query",
15
- z
16
- .object({
17
- profile: z.string().optional(),
18
- channel: z.string().optional(),
19
- repo: z.string().optional(),
20
- "sub-agent": z.string().optional(),
21
- "env-file": z.string().optional(),
22
- })
23
- .passthrough(),
24
- help,
25
- ),
26
- async (c) => {
27
- const query = c.req.valid("query")
28
- const funnel = c.var.funnel
29
-
30
- if (query.profile) {
31
- const profile = funnel.profiles.get(query.profile)
32
-
33
- if (!profile) {
34
- throw new HTTPException(404, { message: `profile "${query.profile}" not found` })
35
- }
36
-
37
- const exitCode = await funnel.claude.launch({
38
- channel: query.channel ?? profile.channel,
39
- repo: query.repo ?? profile.repo,
40
- subAgent: query["sub-agent"] ?? profile.subAgent,
41
- envFiles: query["env-file"] ? [query["env-file"]] : profile.envFiles,
42
- userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
43
- profileName: profile.name,
44
- })
45
-
46
- process.exit(exitCode)
47
- }
48
-
49
- if (query.channel) {
50
- const exitCode = await funnel.claude.launch({
51
- channel: query.channel,
52
- repo: query.repo,
53
- subAgent: query["sub-agent"],
54
- envFiles: query["env-file"] ? [query["env-file"]] : undefined,
55
- userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
56
- })
57
-
58
- process.exit(exitCode)
59
- }
60
-
61
- const defaultProfile = funnel.profiles.get(DEFAULT_PROFILE_NAME) ?? funnel.profiles.list()[0]
62
-
63
- if (!defaultProfile) return c.text(help)
64
-
65
- const exitCode = await funnel.claude.launch({
66
- channel: defaultProfile.channel,
67
- repo: query.repo ?? defaultProfile.repo,
68
- subAgent: query["sub-agent"] ?? defaultProfile.subAgent,
69
- envFiles: query["env-file"] ? [query["env-file"]] : defaultProfile.envFiles,
70
- userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
71
- profileName: defaultProfile.name,
72
- })
73
-
74
- process.exit(exitCode)
75
- },
76
- )
@@ -1,4 +0,0 @@
1
- import { factory } from "@/factory"
2
- import { claudeHandler } from "@/routes/claude/claude"
3
-
4
- export const claudeRoutes = factory.createApp().get("/", ...claudeHandler)
@@ -1,28 +0,0 @@
1
- export const help = `funnel connectors add — add a connector
2
-
3
- usage:
4
- funnel connectors add <name> --type slack --bot-token xoxb-... --app-token xapp-...
5
- funnel connectors add <name> --type gh [--poll-interval <seconds>]
6
- funnel connectors add <name> --type discord --bot-token <discord-bot-token>
7
- funnel connectors add <name> --type schedule
8
-
9
- slack (Socket Mode):
10
- --bot-token Slack Bot Token (starts with xoxb-)
11
- --app-token Slack App Token (starts with xapp-)
12
-
13
- gh (GitHub, gh CLI):
14
- --poll-interval polling interval for /notifications (seconds, default 60)
15
- note: uses the gh CLI (must be authenticated via gh auth login); no token required
16
-
17
- discord (Discord Gateway):
18
- --bot-token Discord Bot Token
19
-
20
- schedule (cron-driven prompts):
21
- no extra flags at connector level — add entries with:
22
- funnel connectors <name> schedules add --cron "* * * * *" --prompt "..."
23
-
24
- examples:
25
- funnel connectors add prod-slack --type slack --bot-token xoxb-... --app-token xapp-...
26
- funnel connectors add my-gh --type gh --poll-interval 30
27
- funnel connectors add my-discord --type discord --bot-token MTI...
28
- funnel connectors add my-cron --type schedule`
@@ -1,64 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/connectors/add.help"
5
-
6
- export const connectorsAddHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator(
9
- "query",
10
- z.discriminatedUnion("type", [
11
- z.object({
12
- type: z.literal("slack"),
13
- "bot-token": z.string().startsWith("xoxb-"),
14
- "app-token": z.string().startsWith("xapp-"),
15
- }),
16
- z.object({
17
- type: z.literal("gh"),
18
- "poll-interval": z.string().optional(),
19
- }),
20
- z.object({
21
- type: z.literal("discord"),
22
- "bot-token": z.string().min(10),
23
- }),
24
- z.object({
25
- type: z.literal("schedule"),
26
- }),
27
- ]),
28
- help,
29
- ),
30
- (c) => {
31
- const param = c.req.valid("param")
32
- const query = c.req.valid("query")
33
- const funnel = c.var.funnel
34
-
35
- if (query.type === "slack") {
36
- funnel.connectors.add({
37
- type: "slack",
38
- name: param.name,
39
- botToken: query["bot-token"],
40
- appToken: query["app-token"],
41
- })
42
- } else if (query.type === "gh") {
43
- funnel.connectors.add({
44
- type: "gh",
45
- name: param.name,
46
- pollInterval: query["poll-interval"] ? Number(query["poll-interval"]) : undefined,
47
- })
48
- } else if (query.type === "discord") {
49
- funnel.connectors.add({
50
- type: "discord",
51
- name: param.name,
52
- botToken: query["bot-token"],
53
- })
54
- } else {
55
- funnel.connectors.add({
56
- type: "schedule",
57
- name: param.name,
58
- entries: [],
59
- })
60
- }
61
-
62
- return c.text(`added connector "${param.name}"`)
63
- },
64
- )
@@ -1,14 +0,0 @@
1
- export const help = `funnel connectors — manage external connections (Slack, etc.)
2
-
3
- usage: funnel connectors [subcommand]
4
-
5
- subcommands:
6
- (none) list
7
- add <name> --type slack --bot-token <t> --app-token <t>
8
- remove <name>
9
- <name> show details
10
-
11
- examples:
12
- funnel connectors add prod-slack --type slack --bot-token xoxb-... --app-token xapp-...
13
- funnel connectors
14
- funnel connectors remove prod-slack`
@@ -1,18 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/connectors/group.help"
5
-
6
- export const connectorsGroupHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- (c) => {
9
- const funnel = c.var.funnel
10
- const connectors = funnel.connectors.list()
11
-
12
- if (connectors.length === 0) return c.text("no connectors")
13
-
14
- const lines = connectors.map((con) => `${con.name} (${con.type})`)
15
-
16
- return c.text(lines.join("\n"))
17
- },
18
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel connectors remove — remove a connector
2
-
3
- usage: funnel connectors remove <name>`
@@ -1,17 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/connectors/remove.help"
5
-
6
- export const connectorsRemoveHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator("query", z.object({}), help),
9
- (c) => {
10
- const param = c.req.valid("param")
11
- const funnel = c.var.funnel
12
-
13
- funnel.connectors.remove(param.name)
14
-
15
- return c.text(`removed connector "${param.name}"`)
16
- },
17
- )
@@ -1,5 +0,0 @@
1
- export const help = `funnel connectors rename — rename a connector
2
-
3
- usage:
4
- funnel connectors rename <old> <new>
5
- funnel connectors <old> rename <new>`