@interactive-inc/claude-funnel 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/README.md +155 -133
  2. package/dist/bin.js +1417 -0
  3. package/dist/gateway/daemon.js +513 -0
  4. package/dist/highlights-eq9cgrbb.scm +604 -0
  5. package/dist/highlights-ghv9g403.scm +205 -0
  6. package/dist/highlights-hk7bwhj4.scm +284 -0
  7. package/dist/highlights-r812a2qc.scm +150 -0
  8. package/dist/highlights-x6tmsnaa.scm +115 -0
  9. package/dist/injections-73j83es3.scm +27 -0
  10. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  11. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  12. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  13. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  14. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  15. package/lib/bin.ts +78 -0
  16. package/lib/{modules → cli}/router/to-request.ts +13 -20
  17. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +27 -0
  18. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +40 -0
  19. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +41 -0
  20. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +22 -0
  21. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +23 -0
  22. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +26 -0
  23. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +92 -0
  24. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +22 -0
  25. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +63 -0
  26. package/lib/cli/routes/channels.$channel.connectors.ts +26 -0
  27. package/lib/cli/routes/channels.$channel.rename.$newName.ts +22 -0
  28. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +34 -0
  29. package/lib/cli/routes/channels.$channel.ts +34 -0
  30. package/lib/cli/routes/channels.add.$channel.ts +33 -0
  31. package/lib/cli/routes/channels.remove.$channel.ts +20 -0
  32. package/lib/cli/routes/channels.ts +39 -0
  33. package/lib/cli/routes/claude.ts +69 -0
  34. package/lib/cli/routes/gateway.listeners.ts +41 -0
  35. package/lib/cli/routes/gateway.logs.ts +123 -0
  36. package/lib/{routes/gateway/restart.ts → cli/routes/gateway.restart.ts} +20 -5
  37. package/lib/cli/routes/gateway.run.ts +41 -0
  38. package/lib/cli/routes/gateway.start.ts +50 -0
  39. package/lib/cli/routes/gateway.status.ts +19 -0
  40. package/lib/cli/routes/gateway.stop.ts +32 -0
  41. package/lib/cli/routes/gateway.ts +55 -0
  42. package/lib/cli/routes/index.ts +202 -0
  43. package/lib/cli/routes/profiles.$profile.as-default.ts +22 -0
  44. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +22 -0
  45. package/lib/cli/routes/profiles.$profile.run.ts +36 -0
  46. package/lib/cli/routes/profiles.add.$profile.ts +46 -0
  47. package/lib/cli/routes/profiles.remove.$profile.ts +20 -0
  48. package/lib/cli/routes/profiles.set.$profile.ts +46 -0
  49. package/lib/cli/routes/profiles.ts +40 -0
  50. package/lib/cli/routes/status.ts +93 -0
  51. package/lib/cli/routes/update.ts +27 -0
  52. package/lib/connectors/connector-config-schema.ts +16 -0
  53. package/lib/connectors/connector-factory.ts +94 -0
  54. package/lib/connectors/connector-listener.ts +20 -0
  55. package/lib/{modules/connectors/funnel-discord-adapter.ts → connectors/discord-adapter.ts} +6 -11
  56. package/lib/{modules/connectors → connectors}/discord-connector-schema.ts +4 -1
  57. package/lib/connectors/discord-listener.ts +111 -0
  58. package/lib/{modules/connectors/funnel-gh-adapter.ts → connectors/gh-adapter.ts} +3 -6
  59. package/lib/{modules/connectors → connectors}/gh-connector-schema.ts +4 -1
  60. package/lib/{modules/connectors/funnel-gh-listener.ts → connectors/gh-listener.ts} +45 -19
  61. package/lib/{modules/connectors → connectors}/match-cron.ts +10 -4
  62. package/lib/connectors/schedule-connector-schema.ts +33 -0
  63. package/lib/connectors/schedule-listener.ts +207 -0
  64. package/lib/connectors/schedule-state-store.ts +54 -0
  65. package/lib/connectors/slack-adapter.ts +36 -0
  66. package/lib/{modules/connectors → connectors}/slack-connector-schema.ts +4 -1
  67. package/lib/{modules/connectors/funnel-slack-event-processor.ts → connectors/slack-event-processor.ts} +15 -9
  68. package/lib/{modules/connectors/funnel-slack-listener.ts → connectors/slack-listener.ts} +33 -14
  69. package/lib/engine/channels/channels.ts +520 -0
  70. package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +28 -55
  71. package/lib/engine/claude/gateway-controller.ts +4 -0
  72. package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +4 -0
  73. package/lib/{modules/fs/memory-funnel-file-system.ts → engine/fs/memory-file-system.ts} +20 -3
  74. package/lib/{modules/fs/node-funnel-file-system.ts → engine/fs/node-file-system.ts} +14 -2
  75. package/lib/{modules/http/memory-funnel-http-client.ts → engine/http/memory-http-client.ts} +1 -5
  76. package/lib/{modules/http/node-funnel-http-client.ts → engine/http/node-http-client.ts} +1 -5
  77. package/lib/{modules/id/memory-funnel-id-generator.ts → engine/id/memory-id-generator.ts} +1 -1
  78. package/lib/{modules/id/node-funnel-id-generator.ts → engine/id/node-id-generator.ts} +1 -1
  79. package/lib/{modules/logger/memory-funnel-logger.ts → engine/logger/memory-logger.ts} +1 -1
  80. package/lib/{modules/logger/node-funnel-logger.ts → engine/logger/node-logger.ts} +1 -1
  81. package/lib/{modules/logger/noop-funnel-logger.ts → engine/logger/noop-logger.ts} +1 -1
  82. package/lib/engine/mcp/channel-server.ts +204 -0
  83. package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +24 -10
  84. package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
  85. package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
  86. package/lib/engine/profiles/profile-channel-checker.ts +7 -0
  87. package/lib/{modules/profiles/funnel-profiles.ts → engine/profiles/profiles.ts} +41 -43
  88. package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
  89. package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
  90. package/lib/engine/settings/settings-schema.ts +46 -0
  91. package/lib/engine/settings/settings-store.ts +110 -0
  92. package/lib/{modules/time/memory-funnel-clock.ts → engine/time/memory-clock.ts} +1 -1
  93. package/lib/{modules/time/node-funnel-clock.ts → engine/time/node-clock.ts} +1 -1
  94. package/lib/funnel.ts +83 -78
  95. package/lib/gateway/auth-middleware.ts +44 -0
  96. package/lib/gateway/broadcaster.ts +319 -0
  97. package/lib/gateway/daemon.ts +47 -0
  98. package/lib/gateway/factory.ts +10 -0
  99. package/lib/gateway/funnel-event-store.ts +155 -0
  100. package/lib/gateway/gateway-server.ts +414 -0
  101. package/lib/gateway/gateway-token.ts +79 -0
  102. package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +27 -13
  103. package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +4 -4
  104. package/lib/gateway/listener-supervisor.ts +339 -0
  105. package/lib/gateway/listeners-client.ts +128 -0
  106. package/lib/gateway/resolve-daemon-script.ts +26 -0
  107. package/lib/gateway/routes/channels.connectors.call.ts +39 -0
  108. package/lib/gateway/routes/health.ts +13 -0
  109. package/lib/gateway/routes/index.ts +24 -0
  110. package/lib/gateway/routes/listeners.list.ts +6 -0
  111. package/lib/gateway/routes/listeners.restart.ts +15 -0
  112. package/lib/gateway/routes/listeners.start.ts +15 -0
  113. package/lib/gateway/routes/listeners.stop.ts +15 -0
  114. package/lib/gateway/routes/route-deps.ts +11 -0
  115. package/lib/gateway/routes/status.ts +15 -0
  116. package/lib/gateway/routes/validator.ts +17 -0
  117. package/lib/index.ts +50 -92
  118. package/lib/logger/leuco-human-file-writer.ts +65 -0
  119. package/lib/logger/leuco-human-logger.ts +98 -0
  120. package/lib/logger/leuco-human-record.ts +16 -0
  121. package/lib/logger/leuco-human-stdout-writer.ts +26 -0
  122. package/lib/logger/leuco-human-writer.ts +14 -0
  123. package/lib/logger/leuco-logger-memory-sink.ts +67 -0
  124. package/lib/logger/leuco-logger-record.ts +13 -0
  125. package/lib/logger/leuco-logger-sink.ts +33 -0
  126. package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
  127. package/lib/logger/leuco-logger.ts +135 -0
  128. package/lib/tui/app.tsx +357 -0
  129. package/lib/tui/components/add-row.tsx +18 -0
  130. package/lib/tui/components/brand.tsx +27 -0
  131. package/lib/tui/components/card.tsx +44 -0
  132. package/lib/tui/components/detail-bar.tsx +46 -0
  133. package/lib/tui/components/editable-field.tsx +33 -0
  134. package/lib/tui/components/empty-state.tsx +11 -0
  135. package/lib/tui/components/gateway-status.tsx +66 -0
  136. package/lib/tui/components/keymap.tsx +29 -0
  137. package/lib/tui/components/menu-item.tsx +73 -0
  138. package/lib/tui/components/menu.tsx +26 -0
  139. package/lib/tui/components/panel-header.tsx +22 -0
  140. package/lib/tui/components/readonly-field.tsx +18 -0
  141. package/lib/tui/components/section-header.tsx +25 -0
  142. package/lib/tui/components/selection-accent.tsx +32 -0
  143. package/lib/tui/components/session-item.tsx +33 -0
  144. package/lib/tui/components/session-list.tsx +33 -0
  145. package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
  146. package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
  147. package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
  148. package/lib/tui/components/ui/hascii/badge.tsx +51 -0
  149. package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
  150. package/lib/tui/components/ui/hascii/button.tsx +194 -0
  151. package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
  152. package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
  153. package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
  154. package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
  155. package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
  156. package/lib/tui/components/ui/hascii/card.tsx +27 -0
  157. package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
  158. package/lib/tui/components/ui/hascii/command.tsx +159 -0
  159. package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
  160. package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
  161. package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
  162. package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
  163. package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
  164. package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
  165. package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
  166. package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
  167. package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
  168. package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
  169. package/lib/tui/components/ui/hascii/input.tsx +130 -0
  170. package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
  171. package/lib/tui/components/ui/hascii/progress.tsx +28 -0
  172. package/lib/tui/components/ui/hascii/select.tsx +131 -0
  173. package/lib/tui/components/ui/hascii/separator.tsx +35 -0
  174. package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
  175. package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
  176. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
  177. package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
  178. package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
  179. package/lib/tui/components/ui/hascii/slider.tsx +91 -0
  180. package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
  181. package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
  182. package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
  183. package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
  184. package/lib/tui/components/ui/hascii/switch.tsx +66 -0
  185. package/lib/tui/components/ui/hascii/table.tsx +95 -0
  186. package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
  187. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
  188. package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
  189. package/lib/tui/components/ui/hascii/tree.tsx +104 -0
  190. package/lib/tui/components/view-shell.tsx +44 -0
  191. package/lib/tui/filter-input.tsx +33 -0
  192. package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
  193. package/lib/tui/parse-comma-list.ts +14 -0
  194. package/lib/tui/profile-launcher.tsx +61 -0
  195. package/lib/tui/scrollbar-options.ts +19 -0
  196. package/lib/tui/sidebar.tsx +50 -0
  197. package/lib/tui/theme.ts +40 -0
  198. package/lib/tui/tui.tsx +20 -0
  199. package/lib/tui/types.ts +38 -0
  200. package/lib/tui/unique-name.ts +18 -0
  201. package/lib/tui/use-event-stream.ts +133 -0
  202. package/lib/tui/use-snapshot.ts +99 -0
  203. package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
  204. package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
  205. package/lib/tui/utils/hascii/theme-context.tsx +26 -0
  206. package/lib/tui/utils/hascii/theme.ts +176 -0
  207. package/lib/tui/views/channels-view.tsx +108 -0
  208. package/lib/tui/views/connectors-view.tsx +164 -0
  209. package/lib/tui/views/events-view.tsx +160 -0
  210. package/lib/tui/views/listeners-view.tsx +80 -0
  211. package/lib/tui/views/profiles-view.tsx +152 -0
  212. package/package.json +50 -44
  213. package/lib/api.ts +0 -54
  214. package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
  215. package/lib/modules/channels/funnel-channels.ts +0 -160
  216. package/lib/modules/connectors/connector-config-schema.ts +0 -16
  217. package/lib/modules/connectors/connector-existence-checker.ts +0 -3
  218. package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
  219. package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
  220. package/lib/modules/connectors/funnel-connector-stores.ts +0 -52
  221. package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
  222. package/lib/modules/connectors/funnel-connectors.ts +0 -151
  223. package/lib/modules/connectors/funnel-discord-listener.ts +0 -71
  224. package/lib/modules/connectors/funnel-discord-store.ts +0 -88
  225. package/lib/modules/connectors/funnel-gh-store.ts +0 -101
  226. package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
  227. package/lib/modules/connectors/funnel-schedule-listener.ts +0 -130
  228. package/lib/modules/connectors/funnel-schedule-store.ts +0 -195
  229. package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
  230. package/lib/modules/connectors/funnel-slack-store.ts +0 -90
  231. package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -81
  232. package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
  233. package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
  234. package/lib/modules/gateway/daemon.ts +0 -74
  235. package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
  236. package/lib/modules/gateway/funnel-event-logger.ts +0 -59
  237. package/lib/modules/gateway/funnel-gateway-server.ts +0 -241
  238. package/lib/modules/mcp/channel-server.ts +0 -76
  239. package/lib/modules/profiles/profile-channel-checker.ts +0 -3
  240. package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
  241. package/lib/modules/repos/funnel-repositories.ts +0 -112
  242. package/lib/modules/schedule/funnel-schedule.ts +0 -39
  243. package/lib/modules/settings/funnel-settings-store.ts +0 -56
  244. package/lib/modules/settings/settings-schema.ts +0 -33
  245. package/lib/modules/tui/app.tsx +0 -44
  246. package/lib/modules/tui/tui.tsx +0 -13
  247. package/lib/routes/channels/add.help.ts +0 -3
  248. package/lib/routes/channels/add.ts +0 -21
  249. package/lib/routes/channels/connectors-attach.help.ts +0 -3
  250. package/lib/routes/channels/connectors-attach.ts +0 -17
  251. package/lib/routes/channels/connectors-detach.help.ts +0 -3
  252. package/lib/routes/channels/connectors-detach.ts +0 -17
  253. package/lib/routes/channels/group.help.ts +0 -16
  254. package/lib/routes/channels/group.ts +0 -22
  255. package/lib/routes/channels/remove.help.ts +0 -3
  256. package/lib/routes/channels/remove.ts +0 -17
  257. package/lib/routes/channels/rename.help.ts +0 -5
  258. package/lib/routes/channels/rename.ts +0 -17
  259. package/lib/routes/channels/routes.ts +0 -19
  260. package/lib/routes/channels/show.help.ts +0 -1
  261. package/lib/routes/channels/show.ts +0 -26
  262. package/lib/routes/claude/claude.help.ts +0 -16
  263. package/lib/routes/claude/claude.ts +0 -76
  264. package/lib/routes/claude/routes.ts +0 -4
  265. package/lib/routes/connectors/add.help.ts +0 -28
  266. package/lib/routes/connectors/add.ts +0 -64
  267. package/lib/routes/connectors/group.help.ts +0 -14
  268. package/lib/routes/connectors/group.ts +0 -18
  269. package/lib/routes/connectors/remove.help.ts +0 -3
  270. package/lib/routes/connectors/remove.ts +0 -17
  271. package/lib/routes/connectors/rename.help.ts +0 -5
  272. package/lib/routes/connectors/rename.ts +0 -17
  273. package/lib/routes/connectors/routes.ts +0 -23
  274. package/lib/routes/connectors/schedules-add.help.ts +0 -11
  275. package/lib/routes/connectors/schedules-add.ts +0 -33
  276. package/lib/routes/connectors/schedules-group.help.ts +0 -1
  277. package/lib/routes/connectors/schedules-group.ts +0 -38
  278. package/lib/routes/connectors/schedules-remove.help.ts +0 -3
  279. package/lib/routes/connectors/schedules-remove.ts +0 -17
  280. package/lib/routes/connectors/set.help.ts +0 -8
  281. package/lib/routes/connectors/set.ts +0 -72
  282. package/lib/routes/connectors/show.help.ts +0 -1
  283. package/lib/routes/connectors/show.ts +0 -41
  284. package/lib/routes/gateway/group.help.ts +0 -15
  285. package/lib/routes/gateway/group.ts +0 -28
  286. package/lib/routes/gateway/logs.help.ts +0 -13
  287. package/lib/routes/gateway/logs.ts +0 -102
  288. package/lib/routes/gateway/restart.help.ts +0 -10
  289. package/lib/routes/gateway/routes.ts +0 -18
  290. package/lib/routes/gateway/run.help.ts +0 -12
  291. package/lib/routes/gateway/run.ts +0 -35
  292. package/lib/routes/gateway/start.help.ts +0 -15
  293. package/lib/routes/gateway/start.ts +0 -32
  294. package/lib/routes/gateway/status.help.ts +0 -9
  295. package/lib/routes/gateway/status.ts +0 -28
  296. package/lib/routes/gateway/stop.help.ts +0 -8
  297. package/lib/routes/gateway/stop.ts +0 -21
  298. package/lib/routes/profiles/add.help.ts +0 -3
  299. package/lib/routes/profiles/add.ts +0 -33
  300. package/lib/routes/profiles/group.help.ts +0 -16
  301. package/lib/routes/profiles/group.ts +0 -25
  302. package/lib/routes/profiles/launch.help.ts +0 -4
  303. package/lib/routes/profiles/launch.ts +0 -36
  304. package/lib/routes/profiles/remove.help.ts +0 -3
  305. package/lib/routes/profiles/remove.ts +0 -17
  306. package/lib/routes/profiles/rename.help.ts +0 -5
  307. package/lib/routes/profiles/rename.ts +0 -17
  308. package/lib/routes/profiles/routes.ts +0 -18
  309. package/lib/routes/profiles/set.help.ts +0 -5
  310. package/lib/routes/profiles/set.ts +0 -32
  311. package/lib/routes/repos/add.help.ts +0 -6
  312. package/lib/routes/repos/add.ts +0 -20
  313. package/lib/routes/repos/group.help.ts +0 -11
  314. package/lib/routes/repos/group.ts +0 -18
  315. package/lib/routes/repos/remove.help.ts +0 -3
  316. package/lib/routes/repos/remove.ts +0 -17
  317. package/lib/routes/repos/rename.help.ts +0 -5
  318. package/lib/routes/repos/rename.ts +0 -17
  319. package/lib/routes/repos/routes.ts +0 -17
  320. package/lib/routes/repos/set.help.ts +0 -5
  321. package/lib/routes/repos/set.ts +0 -21
  322. package/lib/routes/repos/show.help.ts +0 -1
  323. package/lib/routes/repos/show.ts +0 -19
  324. package/lib/routes/request/discord-help.ts +0 -9
  325. package/lib/routes/request/discord.help.ts +0 -19
  326. package/lib/routes/request/discord.ts +0 -65
  327. package/lib/routes/request/group.help.ts +0 -15
  328. package/lib/routes/request/group.ts +0 -9
  329. package/lib/routes/request/routes.ts +0 -14
  330. package/lib/routes/request/slack-help.ts +0 -9
  331. package/lib/routes/request/slack.help.ts +0 -19
  332. package/lib/routes/request/slack.ts +0 -61
  333. package/lib/routes/status/routes.ts +0 -4
  334. package/lib/routes/status/status.help.ts +0 -6
  335. package/lib/routes/status/status.ts +0 -77
  336. package/lib/routes/update/routes.ts +0 -4
  337. package/lib/routes/update/update.help.ts +0 -5
  338. package/lib/routes/update/update.ts +0 -21
  339. package/lib/routes.ts +0 -40
  340. /package/lib/{factory.ts → cli/factory.ts} +0 -0
  341. /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
  342. /package/lib/{modules → cli}/router/validator.ts +0 -0
  343. /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
  344. /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
  345. /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
  346. /package/lib/{modules/id/funnel-id-generator.ts → engine/id/id-generator.ts} +0 -0
  347. /package/lib/{modules/logger/funnel-logger.ts → engine/logger/logger.ts} +0 -0
  348. /package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +0 -0
  349. /package/lib/{modules/time/funnel-clock.ts → engine/time/clock.ts} +0 -0
@@ -1,102 +0,0 @@
1
- import { readdirSync } from "node:fs"
2
- import { join } from "node:path"
3
- import { stringify } from "yaml"
4
- import { z } from "zod"
5
- import { factory } from "@/factory"
6
- import { NodeFunnelLogger } from "@/modules/logger/node-funnel-logger"
7
- import { zValidator } from "@/modules/router/validator"
8
- import { help } from "@/routes/gateway/logs.help"
9
-
10
- const logger = new NodeFunnelLogger()
11
-
12
- const tryParseJson = (s: string): unknown => {
13
- try {
14
- return JSON.parse(s)
15
- } catch {
16
- return null
17
- }
18
- }
19
-
20
- export const gatewayLogsHandler = factory.createHandlers(
21
- zValidator(
22
- "query",
23
- z.object({
24
- n: z.string().optional(),
25
- }),
26
- help,
27
- ),
28
- async (c) => {
29
- const query = c.req.valid("query")
30
- const funnel = c.var.funnel
31
- const logDir = funnel.gateway.getLogDir()
32
-
33
- const files = readdirSync(logDir)
34
- .filter((name) => name.endsWith(".jsonl"))
35
- .sort()
36
-
37
- if (files.length === 0) {
38
- return c.text("no logs")
39
- }
40
-
41
- const latestFile = join(logDir, files[files.length - 1]!)
42
- const lineCount = query.n ? Number(query.n) : 20
43
-
44
- const tail = Bun.spawn(["tail", "-f", "-n", String(lineCount), latestFile], {
45
- stdout: "pipe",
46
- stderr: "inherit",
47
- })
48
-
49
- const forward = (signal: "SIGINT" | "SIGTERM") => {
50
- tail.kill(signal)
51
- }
52
-
53
- process.on("SIGINT", () => forward("SIGINT"))
54
- process.on("SIGTERM", () => forward("SIGTERM"))
55
-
56
- const reader = tail.stdout.getReader()
57
- const decoder = new TextDecoder()
58
- let buffer = ""
59
-
60
- logger.info("gateway.logs tail start", { file: latestFile })
61
-
62
- while (true) {
63
- const result = await reader.read()
64
-
65
- if (result.done) break
66
-
67
- buffer += decoder.decode(result.value, { stream: true })
68
-
69
- const lines = buffer.split("\n")
70
- buffer = lines.pop() ?? ""
71
-
72
- for (const line of lines) {
73
- if (!line.trim()) continue
74
-
75
- const entry = tryParseJson(line) as {
76
- timestamp: string
77
- eventType: string
78
- meta?: unknown
79
- content: string
80
- } | null
81
-
82
- if (!entry) {
83
- process.stdout.write(`${line}\n`)
84
- continue
85
- }
86
-
87
- const parsedContent = tryParseJson(entry.content) ?? entry.content
88
- const output = {
89
- time: entry.timestamp,
90
- type: entry.eventType,
91
- ...(entry.meta ? { meta: entry.meta } : {}),
92
- content: parsedContent,
93
- }
94
-
95
- process.stdout.write(`---\n${stringify(output)}`)
96
- }
97
- }
98
-
99
- await tail.exited
100
- process.exit(0)
101
- },
102
- )
@@ -1,10 +0,0 @@
1
- export const help = `funnel gateway restart — restart the gateway
2
-
3
- usage: funnel gateway restart [--no-caffeine]
4
-
5
- Stops the running process then starts it again in background.
6
- On macOS wraps with caffeinate -i by default. Use --no-caffeine to disable.
7
-
8
- examples:
9
- funnel gateway restart
10
- funnel gateway restart --no-caffeine`
@@ -1,18 +0,0 @@
1
- import { factory } from "@/factory"
2
- import { gatewayGroupHandler } from "@/routes/gateway/group"
3
- import { gatewayLogsHandler } from "@/routes/gateway/logs"
4
- import { gatewayRestartHandler } from "@/routes/gateway/restart"
5
- import { gatewayRunHandler } from "@/routes/gateway/run"
6
- import { gatewayStartHandler } from "@/routes/gateway/start"
7
- import { gatewayStatusHandler } from "@/routes/gateway/status"
8
- import { gatewayStopHandler } from "@/routes/gateway/stop"
9
-
10
- export const gatewayRoutes = factory
11
- .createApp()
12
- .get("/", ...gatewayGroupHandler)
13
- .get("/status", ...gatewayStatusHandler)
14
- .get("/start", ...gatewayStartHandler)
15
- .get("/stop", ...gatewayStopHandler)
16
- .get("/restart", ...gatewayRestartHandler)
17
- .get("/run", ...gatewayRunHandler)
18
- .get("/logs", ...gatewayLogsHandler)
@@ -1,12 +0,0 @@
1
- export const help = `funnel gateway run — run the gateway in foreground
2
-
3
- usage: funnel gateway run [--no-caffeine]
4
-
5
- For developers. The process is tied to the current terminal and exits on SIGINT / SIGTERM.
6
- On macOS wraps with caffeinate -i by default. Use --no-caffeine to disable.
7
-
8
- For normal usage prefer funnel gateway start.
9
-
10
- examples:
11
- funnel gateway run
12
- funnel gateway run --no-caffeine`
@@ -1,35 +0,0 @@
1
- import { resolve } from "node:path"
2
- import { z } from "zod"
3
- import { factory } from "@/factory"
4
- import { zValidator } from "@/modules/router/validator"
5
- import { help } from "@/routes/gateway/run.help"
6
-
7
- export const gatewayRunHandler = factory.createHandlers(
8
- zValidator(
9
- "query",
10
- z.object({
11
- "no-caffeine": z.string().optional(),
12
- }),
13
- help,
14
- ),
15
- async (c) => {
16
- const query = c.req.valid("query")
17
- const gatewayScript = resolve(import.meta.dir, "../../modules/gateway/daemon.ts")
18
-
19
- const useCaffeinate = query["no-caffeine"] !== "true" && process.platform === "darwin"
20
- const command = useCaffeinate
21
- ? ["caffeinate", "-i", "bun", gatewayScript]
22
- : ["bun", gatewayScript]
23
-
24
- const proc = Bun.spawn(command, {
25
- stdio: ["inherit", "inherit", "inherit"],
26
- })
27
-
28
- process.on("SIGINT", () => proc.kill("SIGINT"))
29
- process.on("SIGTERM", () => proc.kill("SIGTERM"))
30
-
31
- const exitCode = await proc.exited
32
-
33
- process.exit(exitCode)
34
- },
35
- )
@@ -1,15 +0,0 @@
1
- export const help = `funnel gateway start — start the gateway in background
2
-
3
- usage: funnel gateway start [--no-caffeine]
4
-
5
- Daemonized with nohup, so it keeps running after the terminal is closed.
6
- On macOS wraps the process with caffeinate -i by default to prevent idle sleep.
7
- Use --no-caffeine to disable caffeinate.
8
-
9
- port: 9742 (override via FUNNEL_PORT)
10
- pid: ~/.funnel/gateway.pid
11
- log: /tmp/funnel/gateway.log
12
-
13
- examples:
14
- funnel gateway start
15
- funnel gateway start --no-caffeine`
@@ -1,32 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/gateway/start.help"
5
-
6
- export const gatewayStartHandler = factory.createHandlers(
7
- zValidator(
8
- "query",
9
- z.object({
10
- "no-caffeine": z.string().optional(),
11
- }),
12
- help,
13
- ),
14
- async (c) => {
15
- const query = c.req.valid("query")
16
- const funnel = c.var.funnel
17
-
18
- if (funnel.gateway.isRunning()) {
19
- const status = funnel.gateway.getStatus()
20
-
21
- return c.text(`funnel gateway: already running (pid ${status.pid})`)
22
- }
23
-
24
- const started = await funnel.gateway.start({
25
- caffeinate: query["no-caffeine"] !== "true",
26
- })
27
-
28
- return started
29
- ? c.text("funnel gateway: started")
30
- : c.text("funnel gateway: failed to start", 500)
31
- },
32
- )
@@ -1,9 +0,0 @@
1
- export const help = `funnel gateway status — show gateway running status
2
-
3
- usage: funnel gateway status
4
-
5
- When running, prints PID, port, and connected channel count. When not running, exits with 503.
6
-
7
- examples:
8
- funnel gateway status
9
- funnel gateway`
@@ -1,28 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/gateway/status.help"
5
-
6
- export const gatewayStatusHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- async (c) => {
9
- const funnel = c.var.funnel
10
- const status = funnel.gateway.getStatus()
11
-
12
- if (!status.running) {
13
- return c.text("funnel gateway: not running", 503)
14
- }
15
-
16
- const res = await fetch(`http://localhost:${status.port}/health`).catch(() => null)
17
-
18
- if (!res) {
19
- return c.text(`funnel gateway: running (pid ${status.pid}) — health check failed`)
20
- }
21
-
22
- const health = (await res.json()) as Record<string, unknown>
23
-
24
- return c.text(
25
- `funnel gateway: running (pid ${status.pid})\n port: ${status.port}\n clients: ${health.clients ?? 0}`,
26
- )
27
- },
28
- )
@@ -1,8 +0,0 @@
1
- export const help = `funnel gateway stop — stop the gateway
2
-
3
- usage: funnel gateway stop
4
-
5
- Terminates the process whose PID is stored in ~/.funnel/gateway.pid.
6
-
7
- examples:
8
- funnel gateway stop`
@@ -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/gateway/stop.help"
5
-
6
- export const gatewayStopHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- async (c) => {
9
- const funnel = c.var.funnel
10
-
11
- if (!funnel.gateway.isRunning()) {
12
- return c.text("funnel gateway: no running process")
13
- }
14
-
15
- const stopped = await funnel.gateway.stop()
16
-
17
- return stopped
18
- ? c.text("funnel gateway: stopped")
19
- : c.text("funnel gateway: failed to stop", 500)
20
- },
21
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel profiles add — add a profile
2
-
3
- usage: funnel profiles add <name> --channel <ch> [--repo <r>] [--sub-agent <s>] [--env-file <f>]`
@@ -1,33 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/profiles/add.help"
5
-
6
- export const profilesAddHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator(
9
- "query",
10
- z.object({
11
- channel: z.string(),
12
- repo: z.string().optional(),
13
- "sub-agent": z.string().optional(),
14
- "env-file": z.string().optional(),
15
- }),
16
- help,
17
- ),
18
- (c) => {
19
- const param = c.req.valid("param")
20
- const query = c.req.valid("query")
21
- const funnel = c.var.funnel
22
-
23
- funnel.profiles.add({
24
- name: param.name,
25
- channel: query.channel,
26
- repo: query.repo,
27
- subAgent: query["sub-agent"],
28
- envFiles: query["env-file"] ? [query["env-file"]] : undefined,
29
- })
30
-
31
- return c.text(`added profile "${param.name}"`)
32
- },
33
- )
@@ -1,16 +0,0 @@
1
- export const help = `funnel profiles — manage launch profiles
2
-
3
- usage: funnel profiles [subcommand]
4
-
5
- subcommands:
6
- (none) list
7
- add <name> --channel <ch> [--repo <r>] [--sub-agent <s>] [--env-file <f>]
8
- <name> set [--channel ...] [--repo ...] [--sub-agent ...] [--env-file ...]
9
- rename <old> <new> rename
10
- remove <name> remove
11
- <name> run launch (sugar for fnl claude)
12
- <name> launch (alias for run)
13
-
14
- examples:
15
- funnel profiles add cto --channel prod-inbox --repo myapp --sub-agent cto
16
- funnel profiles cto run`
@@ -1,25 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/profiles/group.help"
5
-
6
- export const profilesGroupHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- (c) => {
9
- const funnel = c.var.funnel
10
- const profiles = funnel.profiles.list()
11
-
12
- if (profiles.length === 0) return c.text("no profiles")
13
-
14
- const lines = profiles.map((profile) => {
15
- const parts = [`channel=${profile.channel}`]
16
-
17
- if (profile.repo) parts.push(`repo=${profile.repo}`)
18
- if (profile.subAgent) parts.push(`subAgent=${profile.subAgent}`)
19
-
20
- return `${profile.name} [${parts.join(", ")}]`
21
- })
22
-
23
- return c.text(lines.join("\n"))
24
- },
25
- )
@@ -1,4 +0,0 @@
1
- export const help = `funnel profiles <name> run — launch a profile (sugar for fnl claude)
2
-
3
- usage: funnel profiles <name> run [additional claude args...]
4
- funnel profiles <name> (alias)`
@@ -1,36 +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/profiles/launch.help"
7
-
8
- const RESERVED_KEYS = ["channel", "repo", "sub-agent", "env-file"]
9
-
10
- export const profilesLaunchHandler = factory.createHandlers(
11
- zValidator("param", z.object({ name: z.string() })),
12
- zValidator("query", z.object({}).passthrough(), help),
13
- async (c) => {
14
- const param = c.req.valid("param")
15
- const funnel = c.var.funnel
16
- const profile = funnel.profiles.get(param.name)
17
-
18
- if (!profile) throw new HTTPException(404, { message: `profile "${param.name}" not found` })
19
-
20
- const overrideChannel = c.req.query("channel")
21
- const overrideRepo = c.req.query("repo")
22
- const overrideSubAgent = c.req.query("sub-agent")
23
- const overrideEnvFile = c.req.query("env-file")
24
-
25
- const exitCode = await funnel.claude.launch({
26
- channel: overrideChannel ?? profile.channel,
27
- repo: overrideRepo ?? profile.repo,
28
- subAgent: overrideSubAgent ?? profile.subAgent,
29
- envFiles: overrideEnvFile ? [overrideEnvFile] : profile.envFiles,
30
- userArgs: queryToCliArgs(c.req.url, RESERVED_KEYS),
31
- profileName: profile.name,
32
- })
33
-
34
- process.exit(exitCode)
35
- },
36
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel profiles remove — remove a profile
2
-
3
- usage: funnel profiles 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/profiles/remove.help"
5
-
6
- export const profilesRemoveHandler = 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.profiles.remove(param.name)
14
-
15
- return c.text(`removed profile "${param.name}"`)
16
- },
17
- )
@@ -1,5 +0,0 @@
1
- export const help = `funnel profiles rename — rename a profile
2
-
3
- usage:
4
- funnel profiles rename <old> <new>
5
- funnel profiles <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/profiles/rename.help"
5
-
6
- export const profilesRenameHandler = 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.profiles.rename(param.name, param["newName"])
14
-
15
- return c.text(`renamed profile "${param.name}" to "${param["newName"]}"`)
16
- },
17
- )
@@ -1,18 +0,0 @@
1
- import { factory } from "@/factory"
2
- import { profilesAddHandler } from "@/routes/profiles/add"
3
- import { profilesGroupHandler } from "@/routes/profiles/group"
4
- import { profilesLaunchHandler } from "@/routes/profiles/launch"
5
- import { profilesRemoveHandler } from "@/routes/profiles/remove"
6
- import { profilesRenameHandler } from "@/routes/profiles/rename"
7
- import { profilesSetHandler } from "@/routes/profiles/set"
8
-
9
- export const profilesRoutes = factory
10
- .createApp()
11
- .get("/", ...profilesGroupHandler)
12
- .put("/:name/rename/:newName", ...profilesRenameHandler)
13
- .put("/rename/:name/:newName", ...profilesRenameHandler)
14
- .post("/:name", ...profilesAddHandler)
15
- .put("/:name", ...profilesSetHandler)
16
- .delete("/:name", ...profilesRemoveHandler)
17
- .get("/:name/run", ...profilesLaunchHandler)
18
- .get("/:name", ...profilesLaunchHandler)
@@ -1,5 +0,0 @@
1
- export const help = `funnel profiles <name> set — update a profile
2
-
3
- usage: funnel profiles <name> set [--channel <ch>] [--repo <r>] [--sub-agent <s>] [--env-file <f>]
4
-
5
- pass an empty string to --repo / --sub-agent to unset them.`
@@ -1,32 +0,0 @@
1
- import { z } from "zod"
2
- import { factory } from "@/factory"
3
- import { zValidator } from "@/modules/router/validator"
4
- import { help } from "@/routes/profiles/set.help"
5
-
6
- export const profilesSetHandler = factory.createHandlers(
7
- zValidator("param", z.object({ name: z.string() })),
8
- zValidator(
9
- "query",
10
- z.object({
11
- channel: z.string().optional(),
12
- repo: z.string().optional(),
13
- "sub-agent": z.string().optional(),
14
- "env-file": z.string().optional(),
15
- }),
16
- help,
17
- ),
18
- (c) => {
19
- const param = c.req.valid("param")
20
- const query = c.req.valid("query")
21
- const funnel = c.var.funnel
22
-
23
- funnel.profiles.update(param.name, {
24
- channel: query.channel,
25
- repo: query.repo,
26
- subAgent: query["sub-agent"],
27
- envFiles: query["env-file"] !== undefined ? [query["env-file"]] : undefined,
28
- })
29
-
30
- return c.text(`updated profile "${param.name}"`)
31
- },
32
- )
@@ -1,6 +0,0 @@
1
- export const help = `funnel repos add — add a repo
2
-
3
- usage: funnel repos add <name> [--path <path>]
4
-
5
- Writes the funnel MCP server into the .mcp.json of the given directory.
6
- When --path is omitted, the current working directory is used.`
@@ -1,20 +0,0 @@
1
- import { resolve } from "node:path"
2
- import { z } from "zod"
3
- import { factory } from "@/factory"
4
- import { zValidator } from "@/modules/router/validator"
5
- import { help } from "@/routes/repos/add.help"
6
-
7
- export const reposAddHandler = factory.createHandlers(
8
- zValidator("param", z.object({ name: z.string() })),
9
- zValidator("query", z.object({ path: z.string().optional() }), help),
10
- (c) => {
11
- const param = c.req.valid("param")
12
- const query = c.req.valid("query")
13
- const funnel = c.var.funnel
14
- const path = resolve(query.path ?? process.cwd())
15
-
16
- funnel.repositories.add({ name: param.name, path })
17
-
18
- return c.text(`added repo "${param.name}"`)
19
- },
20
- )
@@ -1,11 +0,0 @@
1
- export const help = `funnel repos — manage repositories (extra)
2
-
3
- usage: funnel repos [subcommand]
4
-
5
- subcommands:
6
- (none) list
7
- add <name> [--path <p>] add (path defaults to cwd; writes funnel into .mcp.json)
8
- <name> set --path <p> change path
9
- rename <old> <new> rename
10
- remove <name> remove
11
- <name> show details`
@@ -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/repos/group.help"
5
-
6
- export const reposGroupHandler = factory.createHandlers(
7
- zValidator("query", z.object({}), help),
8
- (c) => {
9
- const funnel = c.var.funnel
10
- const repos = funnel.repositories.list()
11
-
12
- if (repos.length === 0) return c.text("no repos")
13
-
14
- const lines = repos.map((repo) => `${repo.name} ${repo.path}`)
15
-
16
- return c.text(lines.join("\n"))
17
- },
18
- )
@@ -1,3 +0,0 @@
1
- export const help = `funnel repos remove — remove a repo
2
-
3
- usage: funnel repos 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/repos/remove.help"
5
-
6
- export const reposRemoveHandler = 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.repositories.remove(param.name)
14
-
15
- return c.text(`removed repo "${param.name}"`)
16
- },
17
- )
@@ -1,5 +0,0 @@
1
- export const help = `funnel repos rename — rename a repo
2
-
3
- usage:
4
- funnel repos rename <old> <new>
5
- funnel repos <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/repos/rename.help"
5
-
6
- export const reposRenameHandler = 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.repositories.rename(param.name, param["newName"])
14
-
15
- return c.text(`renamed repo "${param.name}" to "${param["newName"]}"`)
16
- },
17
- )