@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
@@ -0,0 +1,73 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { funnel } from "@/tui/theme"
3
+ import { usePressable } from "@/tui/hooks/hascii/use-pressable"
4
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
5
+
6
+ type Props = {
7
+ label: string
8
+ active: boolean
9
+ count?: number
10
+ onSelect: () => void
11
+ }
12
+
13
+ const ROW_HEIGHT = funnel.paddingY * 2 + 1
14
+
15
+ /**
16
+ * One row in the sidebar nav.
17
+ *
18
+ * Active state: a thin `▏` (U+258F LEFT ONE EIGHTH BLOCK) rule painted
19
+ * in the primary color, stacked to fill the row height. The rule is
20
+ * narrower than a full character cell so it reads as a delicate accent
21
+ * instead of a heavy block. The sidebar's own background is `muted`,
22
+ * so hover/active lift the row to `secondaryHover` / `secondaryActive`
23
+ * to read against it.
24
+ */
25
+ export function MenuItem(props: Props) {
26
+ const theme = useHasciiTheme()
27
+ const press = usePressable({ onPress: props.onSelect })
28
+
29
+ const bg = press.isPressed
30
+ ? theme.color.secondaryActive
31
+ : props.active || press.isHovered
32
+ ? theme.color.secondaryHover
33
+ : undefined
34
+
35
+ const isLifted = props.active || press.isHovered || press.isPressed
36
+ const fg = isLifted ? theme.color.foreground : theme.color.foreground
37
+ const countFg = isLifted ? theme.color.foreground : funnel.faint
38
+
39
+ return (
40
+ <box
41
+ {...press.bind}
42
+ style={{
43
+ flexDirection: "row",
44
+ justifyContent: "space-between",
45
+ backgroundColor: bg,
46
+ paddingLeft: funnel.paddingX,
47
+ paddingRight: funnel.paddingX,
48
+ paddingTop: funnel.paddingY,
49
+ paddingBottom: funnel.paddingY,
50
+ }}
51
+ >
52
+ {props.active ? (
53
+ <box
54
+ style={{
55
+ position: "absolute",
56
+ left: 0,
57
+ top: 0,
58
+ bottom: 0,
59
+ flexDirection: "column",
60
+ }}
61
+ >
62
+ {Array.from({ length: ROW_HEIGHT }, (_, index) => (
63
+ <text key={index} fg={funnel.primary}>
64
+
65
+ </text>
66
+ ))}
67
+ </box>
68
+ ) : null}
69
+ <text fg={fg}>{props.label}</text>
70
+ {props.count !== undefined ? <text fg={countFg}>{String(props.count)}</text> : null}
71
+ </box>
72
+ )
73
+ }
@@ -0,0 +1,26 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { MenuItem } from "@/tui/components/menu-item"
3
+ import type { MenuItem as MenuItemType, View } from "@/tui/types"
4
+
5
+ type Props = {
6
+ items: MenuItemType[]
7
+ active: View
8
+ onSelect: (view: View) => void
9
+ }
10
+
11
+ /** Vertical list of clickable nav rows. Each row is a `MenuItem`. */
12
+ export function Menu(props: Props) {
13
+ return (
14
+ <box style={{ flexDirection: "column" }}>
15
+ {props.items.map((item) => (
16
+ <MenuItem
17
+ key={item.view}
18
+ label={item.label}
19
+ active={item.view === props.active}
20
+ count={item.count}
21
+ onSelect={() => props.onSelect(item.view)}
22
+ />
23
+ ))}
24
+ </box>
25
+ )
26
+ }
@@ -0,0 +1,22 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { funnel } from "@/tui/theme"
3
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
4
+
5
+ type Props = {
6
+ label: string
7
+ count?: number
8
+ hint?: string
9
+ }
10
+
11
+ /** Dim section label rendered at the top of every Panel. */
12
+ export function PanelHeader(props: Props) {
13
+ const theme = useHasciiTheme()
14
+ const text = props.count !== undefined ? `${props.label} (${props.count})` : props.label
15
+
16
+ return (
17
+ <text fg={theme.color.mutedForeground}>
18
+ {text}
19
+ {props.hint ? <span fg={funnel.faint}>{` · ${props.hint}`}</span> : null}
20
+ </text>
21
+ )
22
+ }
@@ -0,0 +1,18 @@
1
+ import { HasciiFormItem } from "@/tui/components/ui/hascii/form-item"
2
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
3
+
4
+ type Props = {
5
+ label: string
6
+ value: string
7
+ }
8
+
9
+ /** Static label + value row that mirrors the EditableField layout. */
10
+ export function ReadonlyField(props: Props) {
11
+ const theme = useHasciiTheme()
12
+
13
+ return (
14
+ <HasciiFormItem label={props.label} labelWidth={12}>
15
+ <text fg={theme.color.foreground}>{props.value}</text>
16
+ </HasciiFormItem>
17
+ )
18
+ }
@@ -0,0 +1,25 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { funnel } from "@/tui/theme"
3
+
4
+ type Props = {
5
+ label: string
6
+ }
7
+
8
+ /**
9
+ * Tiny faint label rendered above each sidebar section. Wrapped in a box
10
+ * because OpenTUI ignores padding on `<text>`; only boxes lay out with
11
+ * padding.
12
+ */
13
+ export function SectionHeader(props: Props) {
14
+ return (
15
+ <box
16
+ style={{
17
+ flexDirection: "row",
18
+ paddingLeft: funnel.paddingX,
19
+ paddingRight: funnel.paddingX,
20
+ }}
21
+ >
22
+ <text fg={funnel.faint}>{props.label}</text>
23
+ </box>
24
+ )
25
+ }
@@ -0,0 +1,32 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { funnel } from "@/tui/theme"
3
+
4
+ const RULE_LENGTH = 20
5
+
6
+ /**
7
+ * A thin `▏` (U+258F) primary-colour rule pinned to the left edge of
8
+ * the parent box. Stacked tall enough to span any reasonable card or
9
+ * field-group height; rows past the parent's bottom edge are clipped.
10
+ *
11
+ * Use inside a `position: relative` parent to mark it as the "current"
12
+ * selection — same look as the sidebar `MenuItem` active accent.
13
+ */
14
+ export function SelectionAccent() {
15
+ return (
16
+ <box
17
+ style={{
18
+ position: "absolute",
19
+ left: 0,
20
+ top: 0,
21
+ bottom: 0,
22
+ flexDirection: "column",
23
+ }}
24
+ >
25
+ {Array.from({ length: RULE_LENGTH }, (_, index) => (
26
+ <text key={index} fg={funnel.primary}>
27
+
28
+ </text>
29
+ ))}
30
+ </box>
31
+ )
32
+ }
@@ -0,0 +1,33 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { funnel } from "@/tui/theme"
3
+ import type { Session } from "@/tui/types"
4
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
5
+
6
+ type Props = {
7
+ session: Session
8
+ }
9
+
10
+ /** One connected WebSocket session — channel name + connector summary. */
11
+ export function SessionItem(props: Props) {
12
+ const theme = useHasciiTheme()
13
+ const { session } = props
14
+ const summary =
15
+ session.connectors.length === 0
16
+ ? "(no connectors)"
17
+ : session.connectors.length === 1
18
+ ? session.connectors[0]
19
+ : `${session.connectors.length} connectors`
20
+
21
+ return (
22
+ <box
23
+ style={{
24
+ flexDirection: "column",
25
+ paddingLeft: funnel.paddingX,
26
+ paddingRight: funnel.paddingX,
27
+ }}
28
+ >
29
+ <text fg={theme.color.foreground}>{session.channel || "(unnamed)"}</text>
30
+ <text fg={funnel.faint}>{summary}</text>
31
+ </box>
32
+ )
33
+ }
@@ -0,0 +1,33 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ import { SessionItem } from "@/tui/components/session-item"
3
+ import { funnel } from "@/tui/theme"
4
+ import type { Session } from "@/tui/types"
5
+
6
+ type Props = {
7
+ sessions: Session[]
8
+ }
9
+
10
+ /** Vertical list of connected sessions (Claude MCP clients) for the sidebar. */
11
+ export function SessionList(props: Props) {
12
+ if (props.sessions.length === 0) {
13
+ return (
14
+ <box
15
+ style={{
16
+ flexDirection: "row",
17
+ paddingLeft: funnel.paddingX,
18
+ paddingRight: funnel.paddingX,
19
+ }}
20
+ >
21
+ <text fg={funnel.faint}>(none)</text>
22
+ </box>
23
+ )
24
+ }
25
+
26
+ return (
27
+ <box style={{ flexDirection: "column" }}>
28
+ {props.sessions.map((session, index) => (
29
+ <SessionItem key={`${session.channel}-${index}`} session={session} />
30
+ ))}
31
+ </box>
32
+ )
33
+ }
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from "react"
2
+ import type { HasciiTheme } from "@/tui/utils/hascii/theme"
3
+ import { useHasciiAccordion } from "@/tui/components/ui/hascii/accordion"
4
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
5
+ import { usePressable } from "@/tui/hooks/hascii/use-pressable"
6
+
7
+ export type Props = {
8
+ value: string
9
+ title: string
10
+ children?: ReactNode
11
+ }
12
+
13
+ const HEADER_HEIGHT = 3
14
+
15
+ const pickHeaderBg = (
16
+ isOpen: boolean,
17
+ isHovered: boolean,
18
+ isPressed: boolean,
19
+ theme: HasciiTheme,
20
+ ): string | undefined => {
21
+ if (isPressed) return theme.color.secondaryActive
22
+ if (isHovered && isOpen) return theme.color.hoverActive
23
+ if (isHovered) return theme.color.secondaryHover
24
+ if (isOpen) return theme.color.secondaryActive
25
+ return undefined
26
+ }
27
+
28
+ /** Single collapsible row inside HasciiAccordion. Header tracks the same hover/active palette as HasciiSidebarMenuItem; body uses a muted text color. */
29
+ export function HasciiAccordionItem(props: Props) {
30
+ const accordion = useHasciiAccordion()
31
+ const theme = useHasciiTheme()
32
+
33
+ const isOpen = accordion?.isOpen(props.value) ?? false
34
+
35
+ const press = usePressable({
36
+ onPress: () => accordion?.toggle(props.value),
37
+ })
38
+
39
+ const headerBg = pickHeaderBg(isOpen, press.isHovered, press.isPressed, theme)
40
+
41
+ const titleFg = isOpen || press.isHovered ? theme.color.foreground : theme.color.mutedForeground
42
+
43
+ const indicator = isOpen ? "▾" : "▸"
44
+
45
+ return (
46
+ <box flexDirection="column">
47
+ <box
48
+ flexDirection="row"
49
+ alignItems="center"
50
+ gap={1}
51
+ paddingLeft={2}
52
+ paddingRight={2}
53
+ paddingTop={1}
54
+ paddingBottom={1}
55
+ height={HEADER_HEIGHT}
56
+ backgroundColor={headerBg}
57
+ {...press.bind}
58
+ >
59
+ {isOpen ? (
60
+ <box position="absolute" left={0} top={0} bottom={0} flexDirection="column">
61
+ {Array.from({ length: HEADER_HEIGHT }, (_, index) => (
62
+ <text key={index} fg={theme.color.primary}>
63
+
64
+ </text>
65
+ ))}
66
+ </box>
67
+ ) : null}
68
+ <text fg={titleFg}>{indicator}</text>
69
+ <text fg={titleFg}>{props.title}</text>
70
+ </box>
71
+ {isOpen ? (
72
+ <box
73
+ paddingLeft={4}
74
+ paddingRight={2}
75
+ paddingTop={1}
76
+ paddingBottom={1}
77
+ backgroundColor={theme.color.muted}
78
+ >
79
+ {typeof props.children === "string" ? (
80
+ <text fg={theme.color.mutedForeground}>{props.children}</text>
81
+ ) : (
82
+ props.children
83
+ )}
84
+ </box>
85
+ ) : null}
86
+ </box>
87
+ )
88
+ }
@@ -0,0 +1,96 @@
1
+ import { createContext, useContext, useState } from "react"
2
+ import type { ReactNode } from "react"
3
+
4
+ type Mode = "single" | "multiple"
5
+
6
+ type SingleProps = {
7
+ type?: "single"
8
+ value?: string
9
+ defaultValue?: string
10
+ onChange?: (value: string) => void
11
+ }
12
+
13
+ type MultipleProps = {
14
+ type: "multiple"
15
+ value?: string[]
16
+ defaultValue?: string[]
17
+ onChange?: (value: string[]) => void
18
+ }
19
+
20
+ export type Props = (SingleProps | MultipleProps) & {
21
+ children?: ReactNode
22
+ }
23
+
24
+ type ContextValue = {
25
+ mode: Mode
26
+ isOpen: (value: string) => boolean
27
+ toggle: (value: string) => void
28
+ }
29
+
30
+ const AccordionContext = createContext<ContextValue | null>(null)
31
+
32
+ /** Read the current Accordion context. Returns null when called outside HasciiAccordion. */
33
+ export function useHasciiAccordion(): ContextValue | null {
34
+ return useContext(AccordionContext)
35
+ }
36
+
37
+ const isSingle = (props: Props): props is SingleProps & { children?: ReactNode } =>
38
+ props.type !== "multiple"
39
+
40
+ /** Vertical stack of collapsible HasciiAccordionItem children. type="single" only opens one section at a time. */
41
+ export function HasciiAccordion(props: Props) {
42
+ const internalSingleState = useState<string>(isSingle(props) ? (props.defaultValue ?? "") : "")
43
+ const internalMultipleState = useState<string[]>(
44
+ !isSingle(props) ? (props.defaultValue ?? []) : [],
45
+ )
46
+
47
+ if (isSingle(props)) {
48
+ const internal = internalSingleState[0]
49
+ const setInternal = internalSingleState[1]
50
+ const current = props.value ?? internal
51
+
52
+ const toggle = (value: string) => {
53
+ const next = current === value ? "" : value
54
+
55
+ if (props.value === undefined) setInternal(next)
56
+ props.onChange?.(next)
57
+ }
58
+
59
+ const ctx: ContextValue = {
60
+ mode: "single",
61
+ isOpen: (value) => value === current,
62
+ toggle,
63
+ }
64
+
65
+ return (
66
+ <AccordionContext.Provider value={ctx}>
67
+ <box flexDirection="column">{props.children}</box>
68
+ </AccordionContext.Provider>
69
+ )
70
+ }
71
+
72
+ const internal = internalMultipleState[0]
73
+ const setInternal = internalMultipleState[1]
74
+ const current = props.value ?? internal
75
+
76
+ const toggle = (value: string) => {
77
+ const next = current.includes(value)
78
+ ? current.filter((entry) => entry !== value)
79
+ : [...current, value]
80
+
81
+ if (props.value === undefined) setInternal(next)
82
+ props.onChange?.(next)
83
+ }
84
+
85
+ const ctx: ContextValue = {
86
+ mode: "multiple",
87
+ isOpen: (value) => current.includes(value),
88
+ toggle,
89
+ }
90
+
91
+ return (
92
+ <AccordionContext.Provider value={ctx}>
93
+ <box flexDirection="column">{props.children}</box>
94
+ </AccordionContext.Provider>
95
+ )
96
+ }
@@ -0,0 +1,43 @@
1
+ import { HasciiButton } from "@/tui/components/ui/hascii/button"
2
+ import { HasciiDialog } from "@/tui/components/ui/hascii/dialog"
3
+ import { HasciiDialogDescription } from "@/tui/components/ui/hascii/dialog-description"
4
+ import { HasciiDialogFooter } from "@/tui/components/ui/hascii/dialog-footer"
5
+ import { HasciiDialogHeader } from "@/tui/components/ui/hascii/dialog-header"
6
+ import { HasciiDialogTitle } from "@/tui/components/ui/hascii/dialog-title"
7
+
8
+ export type Props = {
9
+ title: string
10
+ description?: string
11
+ okText?: string
12
+ cancelText?: string
13
+ width?: number
14
+ onOk?: () => void
15
+ onCancel?: () => void
16
+ onClose?: () => void
17
+ }
18
+
19
+ /** Convenience wrapper around HasciiDialog. Renders a title, optional description, and one or two footer buttons (OK / optional Cancel). */
20
+ export function HasciiAlertDialog(props: Props) {
21
+ const okText = props.okText ?? "OK"
22
+
23
+ return (
24
+ <HasciiDialog width={props.width} onClose={props.onClose}>
25
+ <HasciiDialogHeader>
26
+ <HasciiDialogTitle>{props.title}</HasciiDialogTitle>
27
+ {props.description !== undefined ? (
28
+ <HasciiDialogDescription>{props.description}</HasciiDialogDescription>
29
+ ) : null}
30
+ </HasciiDialogHeader>
31
+ <HasciiDialogFooter>
32
+ {props.cancelText !== undefined ? (
33
+ <HasciiButton variant="secondary" size="default" onPress={props.onCancel}>
34
+ {props.cancelText}
35
+ </HasciiButton>
36
+ ) : null}
37
+ <HasciiButton variant="default" size="default" onPress={props.onOk}>
38
+ {okText}
39
+ </HasciiButton>
40
+ </HasciiDialogFooter>
41
+ </HasciiDialog>
42
+ )
43
+ }
@@ -0,0 +1,51 @@
1
+ import type { ReactNode } from "react"
2
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
3
+
4
+ type Variant = "default" | "secondary" | "outline" | "destructive"
5
+
6
+ export type Props = {
7
+ variant?: Variant
8
+ children?: ReactNode
9
+ }
10
+
11
+ /** Compact status indicator with default, secondary, outline, and destructive variants. */
12
+ export function HasciiBadge(props: Props) {
13
+ const variant = props.variant ?? "default"
14
+ const theme = useHasciiTheme()
15
+
16
+ if (variant === "secondary") {
17
+ return (
18
+ <box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.secondary}>
19
+ <text fg={theme.color.secondaryForeground}>{props.children}</text>
20
+ </box>
21
+ )
22
+ }
23
+
24
+ if (variant === "outline") {
25
+ return (
26
+ <box
27
+ paddingLeft={1}
28
+ paddingRight={1}
29
+ border
30
+ borderStyle="rounded"
31
+ borderColor={theme.color.border}
32
+ >
33
+ <text fg={theme.color.foreground}>{props.children}</text>
34
+ </box>
35
+ )
36
+ }
37
+
38
+ if (variant === "destructive") {
39
+ return (
40
+ <box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.destructive}>
41
+ <text fg={theme.color.destructiveForeground}>{props.children}</text>
42
+ </box>
43
+ )
44
+ }
45
+
46
+ return (
47
+ <box paddingLeft={1} paddingRight={1} backgroundColor={theme.color.primary}>
48
+ <text fg={theme.color.primaryForeground}>{props.children}</text>
49
+ </box>
50
+ )
51
+ }
@@ -0,0 +1,58 @@
1
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
2
+
3
+ export type BreadcrumbItem = {
4
+ label: string
5
+ value?: string
6
+ }
7
+
8
+ export type Props = {
9
+ items: BreadcrumbItem[]
10
+ separator?: string
11
+ onSelect?: (value: string) => void
12
+ }
13
+
14
+ /** Horizontal trail of crumbs joined by a separator. The last item is rendered as the current location. */
15
+ export function HasciiBreadcrumb(props: Props) {
16
+ const separator = props.separator ?? "›"
17
+ const theme = useHasciiTheme()
18
+
19
+ const cells: { id: string; node: import("react").ReactNode }[] = []
20
+
21
+ for (let index = 0; index < props.items.length; index++) {
22
+ const item = props.items[index]
23
+ if (item === undefined) continue
24
+
25
+ const isLast = index === props.items.length - 1
26
+ const fg = isLast ? theme.color.foreground : theme.color.mutedForeground
27
+ const onPress =
28
+ isLast || item.value === undefined ? undefined : () => props.onSelect?.(item.value as string)
29
+
30
+ cells.push({
31
+ id: `crumb-${index}`,
32
+ node: (
33
+ <box paddingLeft={0} paddingRight={0} onMouseUp={onPress}>
34
+ <text fg={fg}>{item.label}</text>
35
+ </box>
36
+ ),
37
+ })
38
+
39
+ if (!isLast) {
40
+ cells.push({
41
+ id: `sep-${index}`,
42
+ node: (
43
+ <box paddingLeft={1} paddingRight={1}>
44
+ <text fg={theme.color.mutedForeground}>{separator}</text>
45
+ </box>
46
+ ),
47
+ })
48
+ }
49
+ }
50
+
51
+ return (
52
+ <box flexDirection="row" alignItems="center">
53
+ {cells.map((cell) => (
54
+ <box key={cell.id}>{cell.node}</box>
55
+ ))}
56
+ </box>
57
+ )
58
+ }