@interactive-inc/claude-funnel 0.8.1 → 0.10.1

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 (354) hide show
  1. package/README.md +179 -80
  2. package/dist/bin.js +726 -658
  3. package/dist/connector-adapter-CXB-q_XC.d.ts +11 -0
  4. package/dist/connector-adapter-D5Utumgz.js +4 -0
  5. package/dist/connectors/discord.d.ts +76 -0
  6. package/dist/connectors/discord.js +2 -0
  7. package/dist/connectors/gh.d.ts +38 -0
  8. package/dist/connectors/gh.js +2 -0
  9. package/dist/connectors/schedule.d.ts +53 -0
  10. package/dist/connectors/schedule.js +2 -0
  11. package/dist/connectors/slack.d.ts +34 -0
  12. package/dist/connectors/slack.js +2 -0
  13. package/dist/discord-connector-schema-Dww2I4zH.d.ts +14 -0
  14. package/dist/discord-connector-schema-ygf5Df-2.js +173 -0
  15. package/dist/file-system-Co60LrmR.d.ts +74 -0
  16. package/dist/gateway/daemon.js +233 -183
  17. package/dist/gh-connector-schema-2ml29MBC.js +218 -0
  18. package/dist/gh-connector-schema-BZFAS-p-.d.ts +45 -0
  19. package/dist/index.d.ts +3881 -36
  20. package/dist/index.js +6217 -3483
  21. package/dist/logger-CTlXs7z4.d.ts +33 -0
  22. package/dist/node-logger-DQz_BGOD.js +61 -0
  23. package/dist/schedule-connector-schema-CkuIQ0JQ.js +325 -0
  24. package/dist/slack-connector-schema-Cd22WiHB.js +153 -0
  25. package/dist/slack-event-processor-CS-bAit9.d.ts +43 -0
  26. package/package.json +34 -28
  27. package/dist/cli/factory.d.ts +0 -7
  28. package/dist/cli/router/query-to-cli-args.d.ts +0 -1
  29. package/dist/cli/router/to-request.d.ts +0 -5
  30. package/dist/cli/router/validator.d.ts +0 -5
  31. package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +0 -42
  32. package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +0 -46
  33. package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +0 -54
  34. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +0 -66
  35. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +0 -42
  36. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +0 -46
  37. package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +0 -90
  38. package/dist/cli/routes/channels.$channel.connectors.d.ts +0 -38
  39. package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +0 -42
  40. package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +0 -62
  41. package/dist/cli/routes/channels.$channel.d.ts +0 -38
  42. package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +0 -42
  43. package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +0 -28
  44. package/dist/cli/routes/channels.add.$channel.d.ts +0 -46
  45. package/dist/cli/routes/channels.d.ts +0 -16
  46. package/dist/cli/routes/channels.remove.$channel.d.ts +0 -38
  47. package/dist/cli/routes/claude.d.ts +0 -32
  48. package/dist/cli/routes/gateway.d.ts +0 -20
  49. package/dist/cli/routes/gateway.listeners.d.ts +0 -17
  50. package/dist/cli/routes/gateway.logs.d.ts +0 -24
  51. package/dist/cli/routes/gateway.restart.d.ts +0 -24
  52. package/dist/cli/routes/gateway.run.d.ts +0 -24
  53. package/dist/cli/routes/gateway.start.d.ts +0 -24
  54. package/dist/cli/routes/gateway.status.d.ts +0 -13
  55. package/dist/cli/routes/gateway.stop.d.ts +0 -16
  56. package/dist/cli/routes/index.d.ts +0 -1222
  57. package/dist/cli/routes/profiles.$profile.as-default.d.ts +0 -38
  58. package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +0 -42
  59. package/dist/cli/routes/profiles.$profile.run.d.ts +0 -46
  60. package/dist/cli/routes/profiles.add.$profile.d.ts +0 -54
  61. package/dist/cli/routes/profiles.d.ts +0 -16
  62. package/dist/cli/routes/profiles.remove.$profile.d.ts +0 -38
  63. package/dist/cli/routes/profiles.set.$profile.d.ts +0 -54
  64. package/dist/cli/routes/status.d.ts +0 -16
  65. package/dist/cli/routes/update.d.ts +0 -16
  66. package/dist/connectors/connector-adapter.d.ts +0 -8
  67. package/dist/connectors/connector-config-schema.d.ts +0 -43
  68. package/dist/connectors/connector-factory.d.ts +0 -32
  69. package/dist/connectors/connector-listener.d.ts +0 -17
  70. package/dist/connectors/discord-adapter.d.ts +0 -14
  71. package/dist/connectors/discord-connector-schema.d.ts +0 -10
  72. package/dist/connectors/discord-event-processor.d.ts +0 -26
  73. package/dist/connectors/discord-listener.d.ts +0 -17
  74. package/dist/connectors/gh-adapter.d.ts +0 -11
  75. package/dist/connectors/gh-connector-schema.d.ts +0 -10
  76. package/dist/connectors/gh-listener.d.ts +0 -26
  77. package/dist/connectors/match-cron.d.ts +0 -1
  78. package/dist/connectors/schedule-connector-schema.d.ts +0 -45
  79. package/dist/connectors/schedule-listener.d.ts +0 -30
  80. package/dist/connectors/schedule-state-store.d.ts +0 -19
  81. package/dist/connectors/slack-adapter.d.ts +0 -15
  82. package/dist/connectors/slack-connector-schema.d.ts +0 -11
  83. package/dist/connectors/slack-event-processor.d.ts +0 -27
  84. package/dist/connectors/slack-listener.d.ts +0 -17
  85. package/dist/engine/channels/channels.d.ts +0 -106
  86. package/dist/engine/claude/claude.d.ts +0 -49
  87. package/dist/engine/claude/gateway-controller.d.ts +0 -6
  88. package/dist/engine/fs/file-system.d.ts +0 -24
  89. package/dist/engine/fs/memory-file-system.d.ts +0 -31
  90. package/dist/engine/fs/node-file-system.d.ts +0 -15
  91. package/dist/engine/http/http-client.d.ts +0 -15
  92. package/dist/engine/http/memory-http-client.d.ts +0 -12
  93. package/dist/engine/http/node-http-client.d.ts +0 -5
  94. package/dist/engine/id/id-generator.d.ts +0 -7
  95. package/dist/engine/id/memory-id-generator.d.ts +0 -11
  96. package/dist/engine/id/node-id-generator.d.ts +0 -4
  97. package/dist/engine/logger/logger.d.ts +0 -11
  98. package/dist/engine/logger/memory-logger.d.ts +0 -14
  99. package/dist/engine/logger/node-logger.d.ts +0 -15
  100. package/dist/engine/logger/noop-logger.d.ts +0 -7
  101. package/dist/engine/mcp/channel-server.d.ts +0 -1
  102. package/dist/engine/mcp/mcp.d.ts +0 -22
  103. package/dist/engine/process/memory-process-runner.d.ts +0 -43
  104. package/dist/engine/process/node-process-runner.d.ts +0 -9
  105. package/dist/engine/process/process-runner.d.ts +0 -29
  106. package/dist/engine/profiles/profile-channel-checker.d.ts +0 -7
  107. package/dist/engine/profiles/profiles.d.ts +0 -31
  108. package/dist/engine/settings/mock-settings-reader.d.ts +0 -9
  109. package/dist/engine/settings/settings-reader.d.ts +0 -5
  110. package/dist/engine/settings/settings-schema.d.ts +0 -132
  111. package/dist/engine/settings/settings-store.d.ts +0 -18
  112. package/dist/engine/time/clock.d.ts +0 -9
  113. package/dist/engine/time/memory-clock.d.ts +0 -12
  114. package/dist/engine/time/node-clock.d.ts +0 -4
  115. package/dist/funnel.d.ts +0 -95
  116. package/dist/gateway/auth-middleware.d.ts +0 -14
  117. package/dist/gateway/broadcaster.d.ts +0 -122
  118. package/dist/gateway/daemon.d.ts +0 -2
  119. package/dist/gateway/factory.d.ts +0 -7
  120. package/dist/gateway/funnel-event-store.d.ts +0 -81
  121. package/dist/gateway/gateway-server.d.ts +0 -94
  122. package/dist/gateway/gateway-token.d.ts +0 -33
  123. package/dist/gateway/gateway.d.ts +0 -58
  124. package/dist/gateway/kill-competing-slack-gateways.d.ts +0 -9
  125. package/dist/gateway/listener-supervisor.d.ts +0 -85
  126. package/dist/gateway/listeners-client.d.ts +0 -53
  127. package/dist/gateway/resolve-daemon-script.d.ts +0 -11
  128. package/dist/gateway/routes/channels.connectors.call.d.ts +0 -41
  129. package/dist/gateway/routes/health.d.ts +0 -17
  130. package/dist/gateway/routes/index.d.ts +0 -209
  131. package/dist/gateway/routes/listeners.list.d.ts +0 -14
  132. package/dist/gateway/routes/listeners.restart.d.ts +0 -34
  133. package/dist/gateway/routes/listeners.start.d.ts +0 -34
  134. package/dist/gateway/routes/listeners.stop.d.ts +0 -34
  135. package/dist/gateway/routes/route-deps.d.ts +0 -10
  136. package/dist/gateway/routes/status.d.ts +0 -30
  137. package/dist/gateway/routes/validator.d.ts +0 -19
  138. package/dist/logger/leuco-human-file-writer.d.ts +0 -33
  139. package/dist/logger/leuco-human-logger.d.ts +0 -46
  140. package/dist/logger/leuco-human-record.d.ts +0 -15
  141. package/dist/logger/leuco-human-stdout-writer.d.ts +0 -20
  142. package/dist/logger/leuco-human-writer.d.ts +0 -13
  143. package/dist/logger/leuco-logger-memory-sink.d.ts +0 -33
  144. package/dist/logger/leuco-logger-record.d.ts +0 -13
  145. package/dist/logger/leuco-logger-sink.d.ts +0 -34
  146. package/dist/logger/leuco-logger-sqlite-sink.d.ts +0 -102
  147. package/dist/logger/leuco-logger.d.ts +0 -56
  148. package/lib/bin.ts +0 -78
  149. package/lib/cli/factory.ts +0 -10
  150. package/lib/cli/router/query-to-cli-args.ts +0 -20
  151. package/lib/cli/router/to-request.ts +0 -112
  152. package/lib/cli/router/validator.ts +0 -27
  153. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +0 -27
  154. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +0 -40
  155. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +0 -41
  156. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +0 -22
  157. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +0 -23
  158. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +0 -26
  159. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +0 -92
  160. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +0 -22
  161. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +0 -63
  162. package/lib/cli/routes/channels.$channel.connectors.ts +0 -26
  163. package/lib/cli/routes/channels.$channel.rename.$newName.ts +0 -22
  164. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +0 -34
  165. package/lib/cli/routes/channels.$channel.ts +0 -34
  166. package/lib/cli/routes/channels.add.$channel.ts +0 -33
  167. package/lib/cli/routes/channels.remove.$channel.ts +0 -20
  168. package/lib/cli/routes/channels.ts +0 -39
  169. package/lib/cli/routes/claude.ts +0 -69
  170. package/lib/cli/routes/gateway.listeners.ts +0 -41
  171. package/lib/cli/routes/gateway.logs.ts +0 -123
  172. package/lib/cli/routes/gateway.restart.ts +0 -50
  173. package/lib/cli/routes/gateway.run.ts +0 -41
  174. package/lib/cli/routes/gateway.start.ts +0 -50
  175. package/lib/cli/routes/gateway.status.ts +0 -19
  176. package/lib/cli/routes/gateway.stop.ts +0 -32
  177. package/lib/cli/routes/gateway.ts +0 -55
  178. package/lib/cli/routes/index.ts +0 -202
  179. package/lib/cli/routes/profiles.$profile.as-default.ts +0 -22
  180. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +0 -22
  181. package/lib/cli/routes/profiles.$profile.run.ts +0 -36
  182. package/lib/cli/routes/profiles.add.$profile.ts +0 -46
  183. package/lib/cli/routes/profiles.remove.$profile.ts +0 -20
  184. package/lib/cli/routes/profiles.set.$profile.ts +0 -46
  185. package/lib/cli/routes/profiles.ts +0 -40
  186. package/lib/cli/routes/status.ts +0 -93
  187. package/lib/cli/routes/update.ts +0 -27
  188. package/lib/connectors/connector-adapter.ts +0 -9
  189. package/lib/connectors/connector-config-schema.ts +0 -16
  190. package/lib/connectors/connector-factory.ts +0 -94
  191. package/lib/connectors/connector-listener.ts +0 -20
  192. package/lib/connectors/discord-adapter.ts +0 -51
  193. package/lib/connectors/discord-connector-schema.ts +0 -12
  194. package/lib/connectors/discord-event-processor.ts +0 -48
  195. package/lib/connectors/discord-listener.ts +0 -111
  196. package/lib/connectors/gh-adapter.ts +0 -48
  197. package/lib/connectors/gh-connector-schema.ts +0 -12
  198. package/lib/connectors/gh-listener.ts +0 -137
  199. package/lib/connectors/match-cron.ts +0 -78
  200. package/lib/connectors/schedule-connector-schema.ts +0 -33
  201. package/lib/connectors/schedule-listener.ts +0 -207
  202. package/lib/connectors/schedule-state-store.ts +0 -54
  203. package/lib/connectors/slack-adapter.ts +0 -36
  204. package/lib/connectors/slack-connector-schema.ts +0 -13
  205. package/lib/connectors/slack-event-processor.ts +0 -97
  206. package/lib/connectors/slack-listener.ts +0 -97
  207. package/lib/engine/channels/channels.ts +0 -520
  208. package/lib/engine/claude/claude.ts +0 -199
  209. package/lib/engine/claude/gateway-controller.ts +0 -4
  210. package/lib/engine/fs/file-system.ts +0 -23
  211. package/lib/engine/fs/memory-file-system.ts +0 -102
  212. package/lib/engine/fs/node-file-system.ts +0 -68
  213. package/lib/engine/http/http-client.ts +0 -17
  214. package/lib/engine/http/memory-http-client.ts +0 -36
  215. package/lib/engine/http/node-http-client.ts +0 -23
  216. package/lib/engine/id/id-generator.ts +0 -7
  217. package/lib/engine/id/memory-id-generator.ts +0 -20
  218. package/lib/engine/id/node-id-generator.ts +0 -7
  219. package/lib/engine/logger/logger.ts +0 -11
  220. package/lib/engine/logger/memory-logger.ts +0 -28
  221. package/lib/engine/logger/node-logger.ts +0 -49
  222. package/lib/engine/logger/noop-logger.ts +0 -9
  223. package/lib/engine/mcp/channel-server.ts +0 -204
  224. package/lib/engine/mcp/mcp.ts +0 -126
  225. package/lib/engine/process/memory-process-runner.ts +0 -88
  226. package/lib/engine/process/node-process-runner.ts +0 -91
  227. package/lib/engine/process/process-runner.ts +0 -33
  228. package/lib/engine/profiles/profile-channel-checker.ts +0 -7
  229. package/lib/engine/profiles/profiles.ts +0 -126
  230. package/lib/engine/settings/mock-settings-reader.ts +0 -27
  231. package/lib/engine/settings/settings-reader.ts +0 -6
  232. package/lib/engine/settings/settings-schema.ts +0 -46
  233. package/lib/engine/settings/settings-store.ts +0 -110
  234. package/lib/engine/time/clock.ts +0 -15
  235. package/lib/engine/time/memory-clock.ts +0 -26
  236. package/lib/engine/time/node-clock.ts +0 -7
  237. package/lib/funnel.ts +0 -187
  238. package/lib/gateway/auth-middleware.ts +0 -44
  239. package/lib/gateway/broadcaster.ts +0 -319
  240. package/lib/gateway/daemon.ts +0 -47
  241. package/lib/gateway/factory.ts +0 -10
  242. package/lib/gateway/funnel-event-store.ts +0 -155
  243. package/lib/gateway/gateway-server.ts +0 -414
  244. package/lib/gateway/gateway-token.ts +0 -79
  245. package/lib/gateway/gateway.ts +0 -209
  246. package/lib/gateway/kill-competing-slack-gateways.ts +0 -56
  247. package/lib/gateway/listener-supervisor.ts +0 -339
  248. package/lib/gateway/listeners-client.ts +0 -128
  249. package/lib/gateway/resolve-daemon-script.ts +0 -26
  250. package/lib/gateway/routes/channels.connectors.call.ts +0 -39
  251. package/lib/gateway/routes/health.ts +0 -13
  252. package/lib/gateway/routes/index.ts +0 -24
  253. package/lib/gateway/routes/listeners.list.ts +0 -6
  254. package/lib/gateway/routes/listeners.restart.ts +0 -15
  255. package/lib/gateway/routes/listeners.start.ts +0 -15
  256. package/lib/gateway/routes/listeners.stop.ts +0 -15
  257. package/lib/gateway/routes/route-deps.ts +0 -11
  258. package/lib/gateway/routes/status.ts +0 -15
  259. package/lib/gateway/routes/validator.ts +0 -17
  260. package/lib/index.ts +0 -52
  261. package/lib/logger/leuco-human-file-writer.ts +0 -65
  262. package/lib/logger/leuco-human-logger.ts +0 -98
  263. package/lib/logger/leuco-human-record.ts +0 -16
  264. package/lib/logger/leuco-human-stdout-writer.ts +0 -26
  265. package/lib/logger/leuco-human-writer.ts +0 -14
  266. package/lib/logger/leuco-logger-memory-sink.ts +0 -67
  267. package/lib/logger/leuco-logger-record.ts +0 -13
  268. package/lib/logger/leuco-logger-sink.ts +0 -33
  269. package/lib/logger/leuco-logger-sqlite-sink.ts +0 -355
  270. package/lib/logger/leuco-logger.ts +0 -135
  271. package/lib/tui/app.tsx +0 -357
  272. package/lib/tui/components/add-row.tsx +0 -18
  273. package/lib/tui/components/brand.tsx +0 -27
  274. package/lib/tui/components/card.tsx +0 -44
  275. package/lib/tui/components/detail-bar.tsx +0 -46
  276. package/lib/tui/components/editable-field.tsx +0 -33
  277. package/lib/tui/components/empty-state.tsx +0 -11
  278. package/lib/tui/components/gateway-status.tsx +0 -66
  279. package/lib/tui/components/keymap.tsx +0 -29
  280. package/lib/tui/components/menu-item.tsx +0 -73
  281. package/lib/tui/components/menu.tsx +0 -26
  282. package/lib/tui/components/panel-header.tsx +0 -22
  283. package/lib/tui/components/readonly-field.tsx +0 -18
  284. package/lib/tui/components/section-header.tsx +0 -25
  285. package/lib/tui/components/selection-accent.tsx +0 -32
  286. package/lib/tui/components/session-item.tsx +0 -33
  287. package/lib/tui/components/session-list.tsx +0 -33
  288. package/lib/tui/components/ui/hascii/accordion-item.tsx +0 -88
  289. package/lib/tui/components/ui/hascii/accordion.tsx +0 -96
  290. package/lib/tui/components/ui/hascii/alert-dialog.tsx +0 -43
  291. package/lib/tui/components/ui/hascii/badge.tsx +0 -51
  292. package/lib/tui/components/ui/hascii/breadcrumb.tsx +0 -58
  293. package/lib/tui/components/ui/hascii/button.tsx +0 -194
  294. package/lib/tui/components/ui/hascii/card-content.tsx +0 -14
  295. package/lib/tui/components/ui/hascii/card-description.tsx +0 -13
  296. package/lib/tui/components/ui/hascii/card-footer.tsx +0 -14
  297. package/lib/tui/components/ui/hascii/card-header.tsx +0 -14
  298. package/lib/tui/components/ui/hascii/card-title.tsx +0 -13
  299. package/lib/tui/components/ui/hascii/card.tsx +0 -27
  300. package/lib/tui/components/ui/hascii/checkbox.tsx +0 -65
  301. package/lib/tui/components/ui/hascii/command.tsx +0 -159
  302. package/lib/tui/components/ui/hascii/dialog-content.tsx +0 -14
  303. package/lib/tui/components/ui/hascii/dialog-description.tsx +0 -13
  304. package/lib/tui/components/ui/hascii/dialog-footer.tsx +0 -14
  305. package/lib/tui/components/ui/hascii/dialog-header.tsx +0 -14
  306. package/lib/tui/components/ui/hascii/dialog-title.tsx +0 -13
  307. package/lib/tui/components/ui/hascii/dialog.tsx +0 -27
  308. package/lib/tui/components/ui/hascii/file-tree.tsx +0 -142
  309. package/lib/tui/components/ui/hascii/focus-group.tsx +0 -62
  310. package/lib/tui/components/ui/hascii/form-item.tsx +0 -43
  311. package/lib/tui/components/ui/hascii/input-otp.tsx +0 -86
  312. package/lib/tui/components/ui/hascii/input.tsx +0 -130
  313. package/lib/tui/components/ui/hascii/pagination.tsx +0 -105
  314. package/lib/tui/components/ui/hascii/progress.tsx +0 -28
  315. package/lib/tui/components/ui/hascii/select.tsx +0 -131
  316. package/lib/tui/components/ui/hascii/separator.tsx +0 -35
  317. package/lib/tui/components/ui/hascii/sidebar-content.tsx +0 -23
  318. package/lib/tui/components/ui/hascii/sidebar-header.tsx +0 -14
  319. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +0 -67
  320. package/lib/tui/components/ui/hascii/sidebar.tsx +0 -24
  321. package/lib/tui/components/ui/hascii/skeleton.tsx +0 -60
  322. package/lib/tui/components/ui/hascii/slider.tsx +0 -91
  323. package/lib/tui/components/ui/hascii/snackbar.tsx +0 -75
  324. package/lib/tui/components/ui/hascii/sparkline.tsx +0 -53
  325. package/lib/tui/components/ui/hascii/spinner.tsx +0 -47
  326. package/lib/tui/components/ui/hascii/stepper.tsx +0 -54
  327. package/lib/tui/components/ui/hascii/switch.tsx +0 -66
  328. package/lib/tui/components/ui/hascii/table.tsx +0 -95
  329. package/lib/tui/components/ui/hascii/tabs.tsx +0 -59
  330. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +0 -45
  331. package/lib/tui/components/ui/hascii/toggle-group.tsx +0 -99
  332. package/lib/tui/components/ui/hascii/tree.tsx +0 -104
  333. package/lib/tui/components/view-shell.tsx +0 -44
  334. package/lib/tui/filter-input.tsx +0 -33
  335. package/lib/tui/hooks/hascii/use-pressable.ts +0 -54
  336. package/lib/tui/parse-comma-list.ts +0 -14
  337. package/lib/tui/profile-launcher.tsx +0 -61
  338. package/lib/tui/scrollbar-options.ts +0 -19
  339. package/lib/tui/sidebar.tsx +0 -50
  340. package/lib/tui/theme.ts +0 -40
  341. package/lib/tui/tui.tsx +0 -20
  342. package/lib/tui/types.ts +0 -38
  343. package/lib/tui/unique-name.ts +0 -18
  344. package/lib/tui/use-event-stream.ts +0 -133
  345. package/lib/tui/use-snapshot.ts +0 -99
  346. package/lib/tui/utils/hascii/form-item-context.tsx +0 -23
  347. package/lib/tui/utils/hascii/input-focus-context.tsx +0 -31
  348. package/lib/tui/utils/hascii/theme-context.tsx +0 -26
  349. package/lib/tui/utils/hascii/theme.ts +0 -176
  350. package/lib/tui/views/channels-view.tsx +0 -108
  351. package/lib/tui/views/connectors-view.tsx +0 -164
  352. package/lib/tui/views/events-view.tsx +0 -160
  353. package/lib/tui/views/listeners-view.tsx +0 -80
  354. package/lib/tui/views/profiles-view.tsx +0 -152
@@ -1,204 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs"
2
- import { homedir } from "node:os"
3
- import { join } from "node:path"
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js"
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6
- import {
7
- CallToolRequestSchema,
8
- ListToolsRequestSchema,
9
- } from "@modelcontextprotocol/sdk/types.js"
10
- import { FUNNEL_MCP_NAME } from "@/engine/mcp/mcp"
11
- import { settingsSchema } from "@/engine/settings/settings-schema"
12
-
13
- const GATEWAY_BASE_URL = process.env.FUNNEL_GATEWAY_URL ?? "http://localhost:9742"
14
- const GATEWAY_WS_URL = `${GATEWAY_BASE_URL.replace(/^http/, "ws")}/ws`
15
- const RECONNECT_DELAY = 1000
16
- const MAX_RECONNECT_DELAY = 10000
17
- const SETTINGS_PATH = join(homedir(), ".funnel", "settings.json")
18
- const TOOL_CONNECTOR_TYPES = new Set(["slack", "gh", "discord"])
19
-
20
- const readGatewayToken = (): string | null => {
21
- const fromEnv = process.env.FUNNEL_GATEWAY_TOKEN
22
-
23
- if (fromEnv && fromEnv.length > 0) return fromEnv
24
-
25
- const path = join(homedir(), ".funnel", "gateway.token")
26
-
27
- if (!existsSync(path)) return null
28
-
29
- const value = readFileSync(path, "utf-8").trim()
30
-
31
- return value.length > 0 ? value : null
32
- }
33
-
34
- const readChannelConnectors = (
35
- channelId: string,
36
- ): { channelName: string; connectors: { name: string; type: string }[] } | null => {
37
- if (!existsSync(SETTINGS_PATH)) return null
38
-
39
- const raw = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"))
40
- const parsed = settingsSchema.safeParse(raw)
41
-
42
- if (!parsed.success) return null
43
-
44
- const channel = parsed.data.channels.find((c) => c.id === channelId)
45
-
46
- if (!channel) return null
47
-
48
- const connectors = channel.connectors
49
- .filter((c) => TOOL_CONNECTOR_TYPES.has(c.type))
50
- .map((c) => ({ name: c.name, type: c.type }))
51
-
52
- return { channelName: channel.name, connectors }
53
- }
54
-
55
- const usageHintForType = (type: string): string => {
56
- if (type === "slack") {
57
- return "Slack Web API. method=POST path=chat.postMessage body={channel,text,thread_ts?}"
58
- }
59
-
60
- if (type === "discord") {
61
- return "Discord REST API. method=POST path=/channels/<id>/messages body={content,...}"
62
- }
63
-
64
- if (type === "gh") {
65
- return "GitHub REST via gh CLI. method=POST path=repos/owner/repo/issues/N/comments body={body}"
66
- }
67
-
68
- return "Generic adapter call."
69
- }
70
-
71
- export const startChannelServer = async (): Promise<void> => {
72
- const channelId = process.env.FUNNEL_CHANNEL_ID
73
- const channel = channelId ? readChannelConnectors(channelId) : null
74
- const token = readGatewayToken()
75
-
76
- const server = new Server(
77
- { name: FUNNEL_MCP_NAME, version: "1.0.0" },
78
- {
79
- capabilities: {
80
- experimental: { "claude/channel": {} },
81
- tools: {},
82
- },
83
- instructions: [
84
- `Events arrive inside <channel source="${FUNNEL_MCP_NAME}"> tags. Use meta.event_type to discriminate.`,
85
- "",
86
- "To reply or act, call the connector tool exposed by this MCP (one tool per connector configured on this channel). Each tool takes { method, path, body } matching the underlying adapter's CallInput.",
87
- ].join("\n"),
88
- },
89
- )
90
-
91
- server.setRequestHandler(ListToolsRequestSchema, async () => {
92
- const tools = (channel?.connectors ?? []).map((c) => ({
93
- name: c.name,
94
- description: `Call the "${c.name}" (${c.type}) connector. ${usageHintForType(c.type)}`,
95
- inputSchema: {
96
- type: "object" as const,
97
- properties: {
98
- method: { type: "string", description: "HTTP verb or API method (e.g. POST, chat.postMessage)" },
99
- path: { type: "string", description: "API path or method name (adapter-specific)" },
100
- body: { type: "object", description: "Request body / params (adapter-specific)" },
101
- },
102
- required: ["method", "path"],
103
- },
104
- }))
105
-
106
- return { tools }
107
- })
108
-
109
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
110
- if (!channel) {
111
- throw new Error("FUNNEL_CHANNEL_ID is not set or channel not found in settings.json")
112
- }
113
-
114
- const connectorName = request.params.name
115
- const args = (request.params.arguments ?? {}) as Record<string, unknown>
116
- const method = typeof args.method === "string" ? args.method : ""
117
- const path = typeof args.path === "string" ? args.path : ""
118
- const body = args.body ?? {}
119
-
120
- if (!method || !path) {
121
- throw new Error("`method` and `path` are required")
122
- }
123
-
124
- const url = `${GATEWAY_BASE_URL}/channels/${encodeURIComponent(channel.channelName)}/connectors/${encodeURIComponent(connectorName)}/call`
125
- const headers: Record<string, string> = { "content-type": "application/json" }
126
-
127
- if (token) headers.authorization = `Bearer ${token}`
128
-
129
- const res = await fetch(url, {
130
- method: "POST",
131
- headers,
132
- body: JSON.stringify({ method, path, body }),
133
- })
134
-
135
- const text = await res.text()
136
-
137
- if (!res.ok) {
138
- throw new Error(`gateway call failed (${res.status}): ${text}`)
139
- }
140
-
141
- return {
142
- content: [{ type: "text", text }],
143
- }
144
- })
145
-
146
- const transport = new StdioServerTransport()
147
-
148
- await server.connect(transport)
149
-
150
- if (!channelId) return
151
-
152
- const baseUrl = `${GATEWAY_WS_URL}?channel=${encodeURIComponent(channelId)}`
153
- const protocols = token ? [`funnel.token.${token}`] : undefined
154
- let reconnectDelay = RECONNECT_DELAY
155
- let lastOffset = 0
156
-
157
- const connect = () => {
158
- const sinceQuery = lastOffset > 0 ? `&since=${lastOffset}` : ""
159
- const wsUrl = `${baseUrl}${sinceQuery}`
160
- const ws = new WebSocket(wsUrl, protocols)
161
-
162
- ws.addEventListener("open", () => {
163
- reconnectDelay = RECONNECT_DELAY
164
- process.stderr.write(`funnel: connected (${wsUrl})\n`)
165
- })
166
-
167
- ws.addEventListener("message", async (event) => {
168
- try {
169
- const payload = JSON.parse(String(event.data))
170
- const eventType = payload.meta?.event_type ?? "unknown"
171
-
172
- if (typeof payload.offset === "number" && payload.offset > lastOffset) {
173
- lastOffset = payload.offset
174
- }
175
-
176
- process.stderr.write(`funnel: received event (${eventType})\n`)
177
-
178
- await server.notification({
179
- method: "notifications/claude/channel",
180
- params: {
181
- content: payload.content,
182
- meta: payload.meta,
183
- },
184
- })
185
- } catch (error) {
186
- process.stderr.write(
187
- `funnel: error: ${error instanceof Error ? error.message : String(error)}\n`,
188
- )
189
- }
190
- })
191
-
192
- ws.addEventListener("close", () => {
193
- process.stderr.write(`funnel: disconnected, reconnecting in ${reconnectDelay}ms\n`)
194
- setTimeout(connect, reconnectDelay)
195
- reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY)
196
- })
197
-
198
- ws.addEventListener("error", () => {
199
- // close handler will reconnect
200
- })
201
- }
202
-
203
- connect()
204
- }
@@ -1,126 +0,0 @@
1
- import { join } from "node:path"
2
- import { z } from "zod"
3
- import { FunnelFileSystem } from "@/engine/fs/file-system"
4
- import { NodeFunnelFileSystem } from "@/engine/fs/node-file-system"
5
-
6
- export const FUNNEL_MCP_COMMAND = "funnel"
7
- export const FUNNEL_MCP_NAME = "funnel"
8
-
9
- const mcpEntrySchema = z.object({
10
- command: z.string().optional(),
11
- args: z.array(z.string()).optional(),
12
- })
13
-
14
- const mcpConfigSchema = z.object({
15
- mcpServers: z.record(z.string(), mcpEntrySchema).optional(),
16
- })
17
-
18
- type McpEntry = z.infer<typeof mcpEntrySchema>
19
- type McpConfig = z.infer<typeof mcpConfigSchema>
20
-
21
- type Deps = {
22
- fs?: FunnelFileSystem
23
- }
24
-
25
- const defaultFs = new NodeFunnelFileSystem()
26
-
27
- /**
28
- * Installs/uninstalls the funnel MCP entry into a target repository's
29
- * `.mcp.json`. Detects an existing entry by command match so renaming is
30
- * preserved across re-installs.
31
- */
32
- export class FunnelMcp {
33
- private readonly fs: FunnelFileSystem
34
-
35
- constructor(deps: Deps = {}) {
36
- this.fs = deps.fs ?? defaultFs
37
- Object.freeze(this)
38
- }
39
-
40
- install(repoPath: string): void {
41
- if (!this.fs.existsSync(repoPath)) {
42
- throw new Error(`repository does not exist: ${repoPath}`)
43
- }
44
-
45
- const config = this.readConfig(repoPath)
46
- const servers = config.mcpServers ?? {}
47
-
48
- const existingName = this.findServerName(servers)
49
- const targetName = existingName ?? FUNNEL_MCP_NAME
50
-
51
- servers[targetName] = {
52
- command: FUNNEL_MCP_COMMAND,
53
- args: ["mcp"],
54
- }
55
-
56
- this.writeConfig(repoPath, { ...config, mcpServers: servers })
57
- }
58
-
59
- uninstall(repoPath: string): void {
60
- if (!this.fs.existsSync(repoPath)) return
61
-
62
- const config = this.readConfig(repoPath)
63
- const servers = config.mcpServers ?? {}
64
-
65
- const name = this.findServerName(servers)
66
-
67
- if (!name) return
68
-
69
- const next = { ...servers }
70
-
71
- delete next[name]
72
-
73
- this.writeConfig(repoPath, { ...config, mcpServers: next })
74
- }
75
-
76
- findInstalledName(cwd: string): string | null {
77
- const config = this.readConfig(cwd)
78
-
79
- return this.findServerName(config.mcpServers ?? {})
80
- }
81
-
82
- private findServerName(servers: Record<string, McpEntry>): string | null {
83
- for (const entry of Object.entries(servers)) {
84
- const name = entry[0]
85
- const value = entry[1]
86
-
87
- if (value?.command === FUNNEL_MCP_COMMAND) return name
88
- }
89
-
90
- return null
91
- }
92
-
93
- private readConfig(repoPath: string): McpConfig {
94
- const mcpPath = join(repoPath, ".mcp.json")
95
-
96
- if (!this.fs.existsSync(mcpPath)) return {}
97
-
98
- const content = this.fs.readFileSync(mcpPath).trim()
99
-
100
- if (!content) return {}
101
-
102
- let parsed: unknown
103
-
104
- try {
105
- parsed = JSON.parse(content)
106
- } catch (error) {
107
- throw new Error(
108
- `invalid .mcp.json (${mcpPath}): ${error instanceof Error ? error.message : String(error)}`,
109
- )
110
- }
111
-
112
- const result = mcpConfigSchema.safeParse(parsed)
113
-
114
- if (!result.success) {
115
- throw new Error(`invalid .mcp.json (${mcpPath}): ${result.error.message}`)
116
- }
117
-
118
- return result.data
119
- }
120
-
121
- private writeConfig(repoPath: string, config: McpConfig): void {
122
- const mcpPath = join(repoPath, ".mcp.json")
123
-
124
- this.fs.writeFileSync(mcpPath, `${JSON.stringify(config, null, 2)}\n`)
125
- }
126
- }
@@ -1,88 +0,0 @@
1
- import {
2
- type AttachOptions,
3
- type DetachOptions,
4
- FunnelProcessRunner,
5
- type RunOptions,
6
- type RunResult,
7
- } from "@/engine/process/process-runner"
8
-
9
- export type MemoryProcessResponse = {
10
- exitCode?: number
11
- stdout?: string
12
- stderr?: string
13
- }
14
-
15
- export type MemoryProcessHandler = (
16
- command: string[],
17
- ) => MemoryProcessResponse | Promise<MemoryProcessResponse>
18
-
19
- export type MemoryProcessSyncHandler = (command: string[]) => MemoryProcessResponse
20
-
21
- export type MemoryProcessCall =
22
- | { kind: "run"; command: string[]; options: RunOptions }
23
- | { kind: "runSync"; command: string[] }
24
- | { kind: "attach"; command: string[]; options: AttachOptions }
25
- | { kind: "detach"; command: string[]; options: DetachOptions }
26
- | { kind: "kill"; command: string[] }
27
-
28
- const empty: MemoryProcessResponse = { exitCode: 0, stdout: "", stderr: "" }
29
-
30
- export class MemoryFunnelProcessRunner extends FunnelProcessRunner {
31
- readonly calls: MemoryProcessCall[] = []
32
- readonly killed: { pid: number; signal: string }[] = []
33
- private handler: MemoryProcessHandler = () => empty
34
- private syncHandler: MemoryProcessSyncHandler = () => empty
35
-
36
- on(handler: MemoryProcessHandler): this {
37
- this.handler = handler
38
-
39
- return this
40
- }
41
-
42
- onSync(handler: MemoryProcessSyncHandler): this {
43
- this.syncHandler = handler
44
-
45
- return this
46
- }
47
-
48
- async run(command: string[], options: RunOptions = {}): Promise<RunResult> {
49
- this.calls.push({ kind: "run", command, options })
50
-
51
- const result = await this.handler(command)
52
-
53
- return {
54
- exitCode: result.exitCode ?? 0,
55
- stdout: result.stdout ?? "",
56
- stderr: result.stderr ?? "",
57
- }
58
- }
59
-
60
- runSync(command: string[]): RunResult {
61
- this.calls.push({ kind: "runSync", command })
62
-
63
- const result = this.syncHandler(command)
64
-
65
- return {
66
- exitCode: result.exitCode ?? 0,
67
- stdout: result.stdout ?? "",
68
- stderr: result.stderr ?? "",
69
- }
70
- }
71
-
72
- async attach(command: string[], options: AttachOptions = {}): Promise<number> {
73
- this.calls.push({ kind: "attach", command, options })
74
-
75
- const result = await this.handler(command)
76
-
77
- return result.exitCode ?? 0
78
- }
79
-
80
- detach(command: string[], options: DetachOptions = {}): void {
81
- this.calls.push({ kind: "detach", command, options })
82
- }
83
-
84
- kill(pid: number, signal: string = "SIGTERM"): void {
85
- this.calls.push({ kind: "kill", command: [String(pid), signal] })
86
- this.killed.push({ pid, signal })
87
- }
88
- }
@@ -1,91 +0,0 @@
1
- import {
2
- type AttachOptions,
3
- type DetachOptions,
4
- FunnelProcessRunner,
5
- type RunOptions,
6
- type RunResult,
7
- } from "@/engine/process/process-runner"
8
-
9
- const toEnv = (env?: Record<string, string>): Record<string, string> | undefined => {
10
- if (!env) return undefined
11
-
12
- const merged: Record<string, string> = {}
13
-
14
- for (const [key, value] of Object.entries(process.env)) {
15
- if (typeof value === "string") merged[key] = value
16
- }
17
-
18
- for (const [key, value] of Object.entries(env)) {
19
- merged[key] = value
20
- }
21
-
22
- return merged
23
- }
24
-
25
- export class NodeFunnelProcessRunner extends FunnelProcessRunner {
26
- constructor() {
27
- super()
28
- Object.freeze(this)
29
- }
30
-
31
- runSync(command: string[]): RunResult {
32
- const result = Bun.spawnSync(command, {
33
- stdout: "pipe",
34
- stderr: "pipe",
35
- })
36
-
37
- return {
38
- exitCode: result.exitCode ?? 0,
39
- stdout: result.stdout.toString(),
40
- stderr: result.stderr.toString(),
41
- }
42
- }
43
-
44
- async run(command: string[], options: RunOptions = {}): Promise<RunResult> {
45
- const proc = Bun.spawn(command, {
46
- cwd: options.cwd,
47
- env: toEnv(options.env),
48
- stdin: options.input !== undefined ? "pipe" : "ignore",
49
- stdout: "pipe",
50
- stderr: "pipe",
51
- })
52
-
53
- if (options.input !== undefined && proc.stdin) {
54
- proc.stdin.write(options.input)
55
- proc.stdin.end()
56
- }
57
-
58
- const exitCode = await proc.exited
59
- const stdout = await new Response(proc.stdout).text()
60
- const stderr = await new Response(proc.stderr).text()
61
-
62
- return { exitCode, stdout, stderr }
63
- }
64
-
65
- async attach(command: string[], options: AttachOptions = {}): Promise<number> {
66
- const proc = Bun.spawn(command, {
67
- cwd: options.cwd,
68
- env: toEnv(options.env),
69
- stdio: ["inherit", "inherit", "inherit"],
70
- })
71
-
72
- return await proc.exited
73
- }
74
-
75
- detach(command: string[], options: DetachOptions = {}): void {
76
- const proc = Bun.spawn(command, {
77
- env: toEnv(options.env),
78
- stdio: ["ignore", "ignore", "ignore"],
79
- })
80
-
81
- proc.unref()
82
- }
83
-
84
- kill(pid: number, signal: string = "SIGTERM"): void {
85
- try {
86
- process.kill(pid, signal)
87
- } catch {
88
- // ignore
89
- }
90
- }
91
- }
@@ -1,33 +0,0 @@
1
- export type RunOptions = {
2
- cwd?: string
3
- env?: Record<string, string>
4
- input?: string
5
- }
6
-
7
- export type RunResult = {
8
- exitCode: number
9
- stdout: string
10
- stderr: string
11
- }
12
-
13
- export type AttachOptions = {
14
- cwd?: string
15
- env?: Record<string, string>
16
- }
17
-
18
- export type DetachOptions = {
19
- env?: Record<string, string>
20
- }
21
-
22
- /**
23
- * Process boundary covering one-shot runs, sync runs, foreground attach, and
24
- * detached background spawns. Default is NodeFunnelProcessRunner (Bun.spawn);
25
- * MemoryFunnelProcessRunner records calls and lets tests stub responses.
26
- */
27
- export abstract class FunnelProcessRunner {
28
- abstract run(command: string[], options?: RunOptions): Promise<RunResult>
29
- abstract runSync(command: string[]): RunResult
30
- abstract attach(command: string[], options?: AttachOptions): Promise<number>
31
- abstract detach(command: string[], options?: DetachOptions): void
32
- abstract kill(pid: number, signal?: string): void
33
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Read-side dependency that lets FunnelChannels ask whether a profile
3
- * references a given channel id, without depending on FunnelProfiles directly.
4
- */
5
- export type ProfileChannelChecker = {
6
- hasChannelRef(channelId: string): boolean
7
- }
@@ -1,126 +0,0 @@
1
- import { FunnelSettingsReader } from "@/engine/settings/settings-reader"
2
- import type { ProfileConfig } from "@/engine/settings/settings-schema"
3
-
4
- type Deps = {
5
- store: FunnelSettingsReader
6
- }
7
-
8
- /**
9
- * Named launch presets for `fnl claude`. Each profile bundles a working
10
- * directory, a sub-agent name, and the channel id its Claude instance will
11
- * subscribe to. Implements ProfileChannelChecker so FunnelChannels can refuse
12
- * to remove a channel that is still referenced.
13
- *
14
- * The first entry in the persisted array is treated as the default profile;
15
- * `asDefault` reorders the array to put a named profile first.
16
- *
17
- * `channelId` always stores the channel's stable id (uuid). CLI surfaces
18
- * resolve channel name → id before calling `add`/`update` here.
19
- */
20
- export class FunnelProfiles {
21
- private readonly store: FunnelSettingsReader
22
-
23
- constructor(deps: Deps) {
24
- this.store = deps.store
25
- Object.freeze(this)
26
- }
27
-
28
- list(): ProfileConfig[] {
29
- return this.store.read().profiles
30
- }
31
-
32
- get(name: string): ProfileConfig | null {
33
- return this.list().find((p) => p.name === name) ?? null
34
- }
35
-
36
- getDefault(): ProfileConfig | null {
37
- return this.list()[0] ?? null
38
- }
39
-
40
- add(config: ProfileConfig): void {
41
- const settings = this.store.read()
42
-
43
- if (settings.profiles.some((p) => p.name === config.name)) {
44
- throw new Error(`profile "${config.name}" already exists`)
45
- }
46
-
47
- if (!settings.channels.some((c) => c.id === config.channelId)) {
48
- throw new Error(`channel id "${config.channelId}" not found`)
49
- }
50
-
51
- settings.profiles.push(config)
52
-
53
- this.store.write(settings)
54
- }
55
-
56
- remove(name: string): void {
57
- const settings = this.store.read()
58
-
59
- const index = settings.profiles.findIndex((p) => p.name === name)
60
-
61
- if (index < 0) throw new Error(`profile "${name}" not found`)
62
-
63
- settings.profiles.splice(index, 1)
64
-
65
- this.store.write(settings)
66
- }
67
-
68
- rename(oldName: string, newName: string): void {
69
- const settings = this.store.read()
70
-
71
- const profile = settings.profiles.find((p) => p.name === oldName)
72
-
73
- if (!profile) throw new Error(`profile "${oldName}" not found`)
74
-
75
- if (settings.profiles.some((p) => p.name === newName)) {
76
- throw new Error(`profile "${newName}" already exists`)
77
- }
78
-
79
- profile.name = newName
80
-
81
- this.store.write(settings)
82
- }
83
-
84
- asDefault(name: string): void {
85
- const settings = this.store.read()
86
-
87
- const index = settings.profiles.findIndex((p) => p.name === name)
88
-
89
- if (index < 0) throw new Error(`profile "${name}" not found`)
90
-
91
- if (index === 0) return
92
-
93
- const [profile] = settings.profiles.splice(index, 1)
94
-
95
- if (!profile) return
96
-
97
- settings.profiles.unshift(profile)
98
-
99
- this.store.write(settings)
100
- }
101
-
102
- hasChannelRef(channelId: string): boolean {
103
- return this.store.read().profiles.some((p) => p.channelId === channelId)
104
- }
105
-
106
- update(name: string, fields: Partial<Omit<ProfileConfig, "name">>): void {
107
- const settings = this.store.read()
108
-
109
- const profile = settings.profiles.find((p) => p.name === name)
110
-
111
- if (!profile) throw new Error(`profile "${name}" not found`)
112
-
113
- if (fields.channelId !== undefined) {
114
- if (!settings.channels.some((c) => c.id === fields.channelId)) {
115
- throw new Error(`channel id "${fields.channelId}" not found`)
116
- }
117
-
118
- profile.channelId = fields.channelId
119
- }
120
-
121
- if (fields.path !== undefined) profile.path = fields.path
122
- if (fields.subAgent !== undefined) profile.subAgent = fields.subAgent
123
-
124
- this.store.write(settings)
125
- }
126
- }
@@ -1,27 +0,0 @@
1
- import { FunnelSettingsReader } from "@/engine/settings/settings-reader"
2
- import { SETTINGS_VERSION } from "@/engine/settings/settings-schema"
3
- import type { Settings } from "@/engine/settings/settings-schema"
4
-
5
- export const createSettings = (partial: Partial<Settings> = {}): Settings => ({
6
- version: SETTINGS_VERSION,
7
- channels: [],
8
- profiles: [],
9
- ...partial,
10
- })
11
-
12
- export class MockFunnelSettingsReader extends FunnelSettingsReader {
13
- private state: Settings
14
-
15
- constructor(initial?: Partial<Settings>) {
16
- super()
17
- this.state = createSettings(initial)
18
- }
19
-
20
- read(): Settings {
21
- return this.state
22
- }
23
-
24
- write(settings: Settings): void {
25
- this.state = settings
26
- }
27
- }