@interactive-inc/claude-funnel 0.10.0 → 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 (228) hide show
  1. package/dist/bin.js +448 -448
  2. package/dist/connectors/slack.d.ts +1 -29
  3. package/dist/gateway/daemon.js +166 -166
  4. package/dist/index.d.ts +4 -11
  5. package/dist/index.js +133 -120
  6. package/dist/slack-event-processor-CS-bAit9.d.ts +43 -0
  7. package/package.json +1 -6
  8. package/dist/slack-connector-schema-D7zAHN8k.d.ts +0 -15
  9. package/lib/bin.ts +0 -3
  10. package/lib/cli/factory.ts +0 -10
  11. package/lib/cli/index.ts +0 -85
  12. package/lib/cli/router/query-to-cli-args.ts +0 -20
  13. package/lib/cli/router/to-request.ts +0 -113
  14. package/lib/cli/router/validator.ts +0 -27
  15. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +0 -27
  16. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +0 -40
  17. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +0 -41
  18. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +0 -22
  19. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +0 -23
  20. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +0 -26
  21. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +0 -92
  22. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +0 -22
  23. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +0 -63
  24. package/lib/cli/routes/channels.$channel.connectors.ts +0 -26
  25. package/lib/cli/routes/channels.$channel.publish.ts +0 -52
  26. package/lib/cli/routes/channels.$channel.rename.$newName.ts +0 -22
  27. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +0 -34
  28. package/lib/cli/routes/channels.$channel.ts +0 -34
  29. package/lib/cli/routes/channels.add.$channel.ts +0 -33
  30. package/lib/cli/routes/channels.remove.$channel.ts +0 -20
  31. package/lib/cli/routes/channels.ts +0 -39
  32. package/lib/cli/routes/claude.ts +0 -70
  33. package/lib/cli/routes/gateway.listeners.ts +0 -41
  34. package/lib/cli/routes/gateway.logs.ts +0 -123
  35. package/lib/cli/routes/gateway.restart.ts +0 -50
  36. package/lib/cli/routes/gateway.run.ts +0 -41
  37. package/lib/cli/routes/gateway.start.ts +0 -50
  38. package/lib/cli/routes/gateway.status.ts +0 -19
  39. package/lib/cli/routes/gateway.stop.ts +0 -32
  40. package/lib/cli/routes/gateway.ts +0 -55
  41. package/lib/cli/routes/index.ts +0 -219
  42. package/lib/cli/routes/profiles.$profile.as-default.ts +0 -22
  43. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +0 -22
  44. package/lib/cli/routes/profiles.$profile.run.ts +0 -36
  45. package/lib/cli/routes/profiles.add.$profile.ts +0 -49
  46. package/lib/cli/routes/profiles.remove.$profile.ts +0 -20
  47. package/lib/cli/routes/profiles.set.$profile.ts +0 -45
  48. package/lib/cli/routes/profiles.ts +0 -40
  49. package/lib/cli/routes/status.ts +0 -93
  50. package/lib/cli/routes/update.ts +0 -27
  51. package/lib/connectors/connector-adapter.ts +0 -9
  52. package/lib/connectors/connector-config-schema.ts +0 -16
  53. package/lib/connectors/connector-factory.ts +0 -94
  54. package/lib/connectors/connector-listener.ts +0 -20
  55. package/lib/connectors/discord-adapter.ts +0 -51
  56. package/lib/connectors/discord-connector-schema.ts +0 -12
  57. package/lib/connectors/discord-event-processor.ts +0 -48
  58. package/lib/connectors/discord-listener.ts +0 -111
  59. package/lib/connectors/discord.ts +0 -4
  60. package/lib/connectors/gh-adapter.ts +0 -48
  61. package/lib/connectors/gh-connector-schema.ts +0 -12
  62. package/lib/connectors/gh-listener.ts +0 -137
  63. package/lib/connectors/gh.ts +0 -3
  64. package/lib/connectors/match-cron.ts +0 -78
  65. package/lib/connectors/schedule-connector-schema.ts +0 -33
  66. package/lib/connectors/schedule-listener.ts +0 -207
  67. package/lib/connectors/schedule-state-store.ts +0 -54
  68. package/lib/connectors/schedule.ts +0 -4
  69. package/lib/connectors/slack-adapter.ts +0 -36
  70. package/lib/connectors/slack-connector-schema.ts +0 -13
  71. package/lib/connectors/slack-event-processor.ts +0 -97
  72. package/lib/connectors/slack-listener.ts +0 -97
  73. package/lib/connectors/slack.ts +0 -4
  74. package/lib/engine/channels/channels.ts +0 -520
  75. package/lib/engine/claude/claude.ts +0 -205
  76. package/lib/engine/claude/gateway-controller.ts +0 -4
  77. package/lib/engine/fs/file-system.ts +0 -23
  78. package/lib/engine/fs/memory-file-system.ts +0 -102
  79. package/lib/engine/fs/node-file-system.ts +0 -68
  80. package/lib/engine/http/http-client.ts +0 -17
  81. package/lib/engine/http/memory-http-client.ts +0 -36
  82. package/lib/engine/http/node-http-client.ts +0 -23
  83. package/lib/engine/id/id-generator.ts +0 -7
  84. package/lib/engine/id/memory-id-generator.ts +0 -20
  85. package/lib/engine/id/node-id-generator.ts +0 -7
  86. package/lib/engine/logger/logger.ts +0 -11
  87. package/lib/engine/logger/memory-logger.ts +0 -28
  88. package/lib/engine/logger/node-logger.ts +0 -49
  89. package/lib/engine/logger/noop-logger.ts +0 -9
  90. package/lib/engine/mcp/channel-server.ts +0 -123
  91. package/lib/engine/mcp/channel-subscriber.ts +0 -82
  92. package/lib/engine/mcp/mcp.ts +0 -126
  93. package/lib/engine/mcp/read-channel-connectors.ts +0 -34
  94. package/lib/engine/mcp/read-gateway-token.ts +0 -16
  95. package/lib/engine/mcp/usage-hint-for-type.ts +0 -15
  96. package/lib/engine/process/memory-process-runner.ts +0 -88
  97. package/lib/engine/process/node-process-runner.ts +0 -91
  98. package/lib/engine/process/process-runner.ts +0 -33
  99. package/lib/engine/profiles/profile-channel-checker.ts +0 -7
  100. package/lib/engine/profiles/profiles.ts +0 -126
  101. package/lib/engine/settings/mock-settings-reader.ts +0 -27
  102. package/lib/engine/settings/settings-reader.ts +0 -6
  103. package/lib/engine/settings/settings-schema.ts +0 -48
  104. package/lib/engine/settings/settings-store.ts +0 -110
  105. package/lib/engine/time/clock.ts +0 -15
  106. package/lib/engine/time/memory-clock.ts +0 -26
  107. package/lib/engine/time/node-clock.ts +0 -7
  108. package/lib/funnel.ts +0 -294
  109. package/lib/gateway/auth-middleware.ts +0 -44
  110. package/lib/gateway/broadcaster.ts +0 -319
  111. package/lib/gateway/channel-publisher.ts +0 -67
  112. package/lib/gateway/daemon.ts +0 -47
  113. package/lib/gateway/factory.ts +0 -10
  114. package/lib/gateway/funnel-event-store.ts +0 -155
  115. package/lib/gateway/gateway-server.ts +0 -426
  116. package/lib/gateway/gateway-token.ts +0 -79
  117. package/lib/gateway/gateway.ts +0 -209
  118. package/lib/gateway/kill-competing-slack-gateways.ts +0 -56
  119. package/lib/gateway/listener-supervisor.ts +0 -339
  120. package/lib/gateway/listeners-client.ts +0 -128
  121. package/lib/gateway/publish-schema.ts +0 -27
  122. package/lib/gateway/resolve-daemon-script.ts +0 -26
  123. package/lib/gateway/routes/channels.connectors.call.ts +0 -39
  124. package/lib/gateway/routes/channels.publish.ts +0 -44
  125. package/lib/gateway/routes/health.ts +0 -13
  126. package/lib/gateway/routes/index.ts +0 -26
  127. package/lib/gateway/routes/listeners.list.ts +0 -6
  128. package/lib/gateway/routes/listeners.restart.ts +0 -15
  129. package/lib/gateway/routes/listeners.start.ts +0 -15
  130. package/lib/gateway/routes/listeners.stop.ts +0 -15
  131. package/lib/gateway/routes/route-deps.ts +0 -19
  132. package/lib/gateway/routes/status.ts +0 -15
  133. package/lib/gateway/routes/validator.ts +0 -17
  134. package/lib/index.ts +0 -67
  135. package/lib/logger/leuco-human-file-writer.ts +0 -65
  136. package/lib/logger/leuco-human-logger.ts +0 -98
  137. package/lib/logger/leuco-human-record.ts +0 -16
  138. package/lib/logger/leuco-human-stdout-writer.ts +0 -26
  139. package/lib/logger/leuco-human-writer.ts +0 -14
  140. package/lib/logger/leuco-logger-memory-sink.ts +0 -67
  141. package/lib/logger/leuco-logger-record.ts +0 -13
  142. package/lib/logger/leuco-logger-sink.ts +0 -33
  143. package/lib/logger/leuco-logger-sqlite-sink.ts +0 -355
  144. package/lib/logger/leuco-logger.ts +0 -135
  145. package/lib/tui/app.tsx +0 -357
  146. package/lib/tui/components/add-row.tsx +0 -18
  147. package/lib/tui/components/brand.tsx +0 -27
  148. package/lib/tui/components/card.tsx +0 -44
  149. package/lib/tui/components/detail-bar.tsx +0 -46
  150. package/lib/tui/components/editable-field.tsx +0 -33
  151. package/lib/tui/components/empty-state.tsx +0 -11
  152. package/lib/tui/components/gateway-status.tsx +0 -66
  153. package/lib/tui/components/keymap.tsx +0 -29
  154. package/lib/tui/components/menu-item.tsx +0 -73
  155. package/lib/tui/components/menu.tsx +0 -26
  156. package/lib/tui/components/panel-header.tsx +0 -22
  157. package/lib/tui/components/readonly-field.tsx +0 -18
  158. package/lib/tui/components/section-header.tsx +0 -25
  159. package/lib/tui/components/selection-accent.tsx +0 -32
  160. package/lib/tui/components/session-item.tsx +0 -33
  161. package/lib/tui/components/session-list.tsx +0 -33
  162. package/lib/tui/components/ui/hascii/accordion-item.tsx +0 -88
  163. package/lib/tui/components/ui/hascii/accordion.tsx +0 -96
  164. package/lib/tui/components/ui/hascii/alert-dialog.tsx +0 -43
  165. package/lib/tui/components/ui/hascii/badge.tsx +0 -51
  166. package/lib/tui/components/ui/hascii/breadcrumb.tsx +0 -58
  167. package/lib/tui/components/ui/hascii/button.tsx +0 -194
  168. package/lib/tui/components/ui/hascii/card-content.tsx +0 -14
  169. package/lib/tui/components/ui/hascii/card-description.tsx +0 -13
  170. package/lib/tui/components/ui/hascii/card-footer.tsx +0 -14
  171. package/lib/tui/components/ui/hascii/card-header.tsx +0 -14
  172. package/lib/tui/components/ui/hascii/card-title.tsx +0 -13
  173. package/lib/tui/components/ui/hascii/card.tsx +0 -27
  174. package/lib/tui/components/ui/hascii/checkbox.tsx +0 -65
  175. package/lib/tui/components/ui/hascii/command.tsx +0 -159
  176. package/lib/tui/components/ui/hascii/dialog-content.tsx +0 -14
  177. package/lib/tui/components/ui/hascii/dialog-description.tsx +0 -13
  178. package/lib/tui/components/ui/hascii/dialog-footer.tsx +0 -14
  179. package/lib/tui/components/ui/hascii/dialog-header.tsx +0 -14
  180. package/lib/tui/components/ui/hascii/dialog-title.tsx +0 -13
  181. package/lib/tui/components/ui/hascii/dialog.tsx +0 -27
  182. package/lib/tui/components/ui/hascii/file-tree.tsx +0 -142
  183. package/lib/tui/components/ui/hascii/focus-group.tsx +0 -62
  184. package/lib/tui/components/ui/hascii/form-item.tsx +0 -43
  185. package/lib/tui/components/ui/hascii/input-otp.tsx +0 -86
  186. package/lib/tui/components/ui/hascii/input.tsx +0 -130
  187. package/lib/tui/components/ui/hascii/pagination.tsx +0 -105
  188. package/lib/tui/components/ui/hascii/progress.tsx +0 -28
  189. package/lib/tui/components/ui/hascii/select.tsx +0 -131
  190. package/lib/tui/components/ui/hascii/separator.tsx +0 -35
  191. package/lib/tui/components/ui/hascii/sidebar-content.tsx +0 -23
  192. package/lib/tui/components/ui/hascii/sidebar-header.tsx +0 -14
  193. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +0 -67
  194. package/lib/tui/components/ui/hascii/sidebar.tsx +0 -24
  195. package/lib/tui/components/ui/hascii/skeleton.tsx +0 -60
  196. package/lib/tui/components/ui/hascii/slider.tsx +0 -91
  197. package/lib/tui/components/ui/hascii/snackbar.tsx +0 -75
  198. package/lib/tui/components/ui/hascii/sparkline.tsx +0 -53
  199. package/lib/tui/components/ui/hascii/spinner.tsx +0 -47
  200. package/lib/tui/components/ui/hascii/stepper.tsx +0 -54
  201. package/lib/tui/components/ui/hascii/switch.tsx +0 -66
  202. package/lib/tui/components/ui/hascii/table.tsx +0 -95
  203. package/lib/tui/components/ui/hascii/tabs.tsx +0 -59
  204. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +0 -45
  205. package/lib/tui/components/ui/hascii/toggle-group.tsx +0 -99
  206. package/lib/tui/components/ui/hascii/tree.tsx +0 -104
  207. package/lib/tui/components/view-shell.tsx +0 -44
  208. package/lib/tui/filter-input.tsx +0 -33
  209. package/lib/tui/hooks/hascii/use-pressable.ts +0 -54
  210. package/lib/tui/parse-comma-list.ts +0 -14
  211. package/lib/tui/profile-launcher.tsx +0 -61
  212. package/lib/tui/scrollbar-options.ts +0 -19
  213. package/lib/tui/sidebar.tsx +0 -50
  214. package/lib/tui/theme.ts +0 -40
  215. package/lib/tui/tui.tsx +0 -20
  216. package/lib/tui/types.ts +0 -38
  217. package/lib/tui/unique-name.ts +0 -18
  218. package/lib/tui/use-event-stream.ts +0 -133
  219. package/lib/tui/use-snapshot.ts +0 -99
  220. package/lib/tui/utils/hascii/form-item-context.tsx +0 -23
  221. package/lib/tui/utils/hascii/input-focus-context.tsx +0 -31
  222. package/lib/tui/utils/hascii/theme-context.tsx +0 -26
  223. package/lib/tui/utils/hascii/theme.ts +0 -176
  224. package/lib/tui/views/channels-view.tsx +0 -108
  225. package/lib/tui/views/connectors-view.tsx +0 -164
  226. package/lib/tui/views/events-view.tsx +0 -160
  227. package/lib/tui/views/listeners-view.tsx +0 -80
  228. package/lib/tui/views/profiles-view.tsx +0 -152
@@ -1,520 +0,0 @@
1
- import type { CallInput } from "@/connectors/connector-adapter"
2
- import type { ConnectorConfig } from "@/connectors/connector-config-schema"
3
- import type { FunnelConnectorFactory } from "@/connectors/connector-factory"
4
- import type { FunnelConnectorListener } from "@/connectors/connector-listener"
5
- import type { DiscordConnectorConfig } from "@/connectors/discord-connector-schema"
6
- import type { GhConnectorConfig } from "@/connectors/gh-connector-schema"
7
- import type { ScheduleConnectorConfig, ScheduleEntry } from "@/connectors/schedule-connector-schema"
8
- import type { SlackConnectorConfig } from "@/connectors/slack-connector-schema"
9
- import type { ProfileChannelChecker } from "@/engine/profiles/profile-channel-checker"
10
- import { FunnelClock } from "@/engine/time/clock"
11
- import { NodeFunnelClock } from "@/engine/time/node-clock"
12
- import { FunnelIdGenerator } from "@/engine/id/id-generator"
13
- import { NodeFunnelIdGenerator } from "@/engine/id/node-id-generator"
14
- import { FunnelSettingsReader } from "@/engine/settings/settings-reader"
15
- import type {
16
- ChannelConfig,
17
- ChannelDeliveryMode,
18
- Settings,
19
- } from "@/engine/settings/settings-schema"
20
-
21
- type Deps = {
22
- store: FunnelSettingsReader
23
- factory: FunnelConnectorFactory
24
- profileChecker: ProfileChannelChecker
25
- clock?: FunnelClock
26
- idGenerator?: FunnelIdGenerator
27
- }
28
-
29
- export type ChannelConnectorView = ConnectorConfig & {
30
- channelId: string
31
- channelName: string
32
- }
33
-
34
- type AddConnectorInput =
35
- | { type: "slack"; name: string; botToken: string; appToken: string }
36
- | { type: "gh"; name: string; pollInterval?: number }
37
- | { type: "discord"; name: string; botToken: string }
38
- | { type: "schedule"; name: string; entries?: ScheduleEntry[] }
39
-
40
- const defaultClock = new NodeFunnelClock()
41
- const defaultIdGenerator = new NodeFunnelIdGenerator()
42
-
43
- /**
44
- * Channels own their connectors. Each channel has a stable id (UUID); the
45
- * `name` is the human-facing label used by the CLI. Connectors live nested
46
- * inside `channel.connectors[]`, so add/remove/rename are channel-scoped — no
47
- * global connector namespace exists. Token uniqueness is enforced across all
48
- * channels at add/update time so the same Slack/Discord credentials cannot
49
- * be registered twice.
50
- */
51
- export class FunnelChannels {
52
- private readonly store: FunnelSettingsReader
53
- private readonly factory: FunnelConnectorFactory
54
- private readonly profileChecker: ProfileChannelChecker
55
- private readonly clock: FunnelClock
56
- private readonly idGenerator: FunnelIdGenerator
57
-
58
- constructor(deps: Deps) {
59
- this.store = deps.store
60
- this.factory = deps.factory
61
- this.profileChecker = deps.profileChecker
62
- this.clock = deps.clock ?? defaultClock
63
- this.idGenerator = deps.idGenerator ?? defaultIdGenerator
64
- Object.freeze(this)
65
- }
66
-
67
- list(): ChannelConfig[] {
68
- return this.store.read().channels
69
- }
70
-
71
- get(name: string): ChannelConfig | null {
72
- return this.list().find((c) => c.name === name) ?? null
73
- }
74
-
75
- getById(id: string): ChannelConfig | null {
76
- return this.list().find((c) => c.id === id) ?? null
77
- }
78
-
79
- add(input: { name: string; delivery?: ChannelDeliveryMode }): ChannelConfig {
80
- const settings = this.store.read()
81
-
82
- if (settings.channels.some((c) => c.name === input.name)) {
83
- throw new Error(`channel "${input.name}" already exists`)
84
- }
85
-
86
- const channel: ChannelConfig = {
87
- id: this.idGenerator.generate(),
88
- name: input.name,
89
- delivery: input.delivery ?? "fanout",
90
- connectors: [],
91
- }
92
-
93
- settings.channels.push(channel)
94
- this.store.write(settings)
95
-
96
- return channel
97
- }
98
-
99
- setDelivery(name: string, delivery: ChannelDeliveryMode): void {
100
- const settings = this.store.read()
101
- const channel = this.requireChannel(settings, name)
102
-
103
- channel.delivery = delivery
104
-
105
- this.store.write(settings)
106
- }
107
-
108
- remove(name: string): void {
109
- const settings = this.store.read()
110
- const index = settings.channels.findIndex((c) => c.name === name)
111
-
112
- if (index < 0) throw new Error(`channel "${name}" not found`)
113
-
114
- const channel = settings.channels[index]
115
-
116
- if (channel && this.profileChecker.hasChannelRef(channel.id)) {
117
- throw new Error(`channel "${name}" is referenced by a profile`)
118
- }
119
-
120
- settings.channels.splice(index, 1)
121
- this.store.write(settings)
122
- }
123
-
124
- rename(oldName: string, newName: string): void {
125
- const settings = this.store.read()
126
- const channel = settings.channels.find((c) => c.name === oldName)
127
-
128
- if (!channel) throw new Error(`channel "${oldName}" not found`)
129
- if (settings.channels.some((c) => c.name === newName)) {
130
- throw new Error(`channel "${newName}" already exists`)
131
- }
132
-
133
- channel.name = newName
134
- this.store.write(settings)
135
- }
136
-
137
- listConnectors(channelName: string): ConnectorConfig[] {
138
- return this.requireChannel(this.store.read(), channelName).connectors
139
- }
140
-
141
- getConnector(channelName: string, connectorName: string): ConnectorConfig | null {
142
- const channel = this.get(channelName)
143
-
144
- if (!channel) return null
145
-
146
- return channel.connectors.find((c) => c.name === connectorName) ?? null
147
- }
148
-
149
- listAllConnectors(): ChannelConnectorView[] {
150
- const out: ChannelConnectorView[] = []
151
-
152
- for (const channel of this.list()) {
153
- for (const connector of channel.connectors) {
154
- out.push({ ...connector, channelId: channel.id, channelName: channel.name })
155
- }
156
- }
157
-
158
- return out
159
- }
160
-
161
- addConnector(channelName: string, input: AddConnectorInput): ConnectorConfig {
162
- const settings = this.store.read()
163
- const channel = this.requireChannel(settings, channelName)
164
-
165
- if (channel.connectors.some((c) => c.name === input.name)) {
166
- throw new Error(`connector "${input.name}" already exists in channel "${channelName}"`)
167
- }
168
-
169
- const candidate = this.fromInput(input)
170
-
171
- this.assertNoTokenCollision(settings, candidate)
172
-
173
- channel.connectors.push(candidate)
174
- this.store.write(settings)
175
-
176
- return candidate
177
- }
178
-
179
- private fromInput(input: AddConnectorInput): ConnectorConfig {
180
- const id = this.idGenerator.generate()
181
- const now = this.clock.iso()
182
- const createdAt = now
183
- const updatedAt = now
184
-
185
- if (input.type === "slack") {
186
- return {
187
- id,
188
- type: "slack",
189
- name: input.name,
190
- botToken: input.botToken,
191
- appToken: input.appToken,
192
- createdAt,
193
- updatedAt,
194
- }
195
- }
196
-
197
- if (input.type === "gh") {
198
- return {
199
- id,
200
- type: "gh",
201
- name: input.name,
202
- ...(input.pollInterval !== undefined ? { pollInterval: input.pollInterval } : {}),
203
- createdAt,
204
- updatedAt,
205
- }
206
- }
207
-
208
- if (input.type === "discord") {
209
- return {
210
- id,
211
- type: "discord",
212
- name: input.name,
213
- botToken: input.botToken,
214
- createdAt,
215
- updatedAt,
216
- }
217
- }
218
-
219
- return {
220
- id,
221
- type: "schedule",
222
- name: input.name,
223
- entries: input.entries ?? [],
224
- createdAt,
225
- updatedAt,
226
- }
227
- }
228
-
229
- removeConnector(channelName: string, connectorName: string): void {
230
- const settings = this.store.read()
231
- const channel = this.requireChannel(settings, channelName)
232
- const index = channel.connectors.findIndex((c) => c.name === connectorName)
233
-
234
- if (index < 0) {
235
- throw new Error(`connector "${connectorName}" not found in channel "${channelName}"`)
236
- }
237
-
238
- channel.connectors.splice(index, 1)
239
- this.store.write(settings)
240
- }
241
-
242
- renameConnector(channelName: string, oldName: string, newName: string): void {
243
- const settings = this.store.read()
244
- const channel = this.requireChannel(settings, channelName)
245
- const connector = channel.connectors.find((c) => c.name === oldName)
246
-
247
- if (!connector) {
248
- throw new Error(`connector "${oldName}" not found in channel "${channelName}"`)
249
- }
250
-
251
- if (channel.connectors.some((c) => c.name === newName)) {
252
- throw new Error(`connector "${newName}" already exists in channel "${channelName}"`)
253
- }
254
-
255
- connector.name = newName
256
- connector.updatedAt = this.clock.iso()
257
- this.store.write(settings)
258
- }
259
-
260
- updateSlackConnector(
261
- channelName: string,
262
- connectorName: string,
263
- fields: { botToken?: string; appToken?: string },
264
- ): void {
265
- const settings = this.store.read()
266
- const channel = this.requireChannel(settings, channelName)
267
- const connector = this.requireSlackConnector(channel, connectorName)
268
-
269
- const updated: SlackConnectorConfig = {
270
- ...connector,
271
- botToken: fields.botToken ?? connector.botToken,
272
- appToken: fields.appToken ?? connector.appToken,
273
- updatedAt: this.clock.iso(),
274
- }
275
-
276
- this.assertNoTokenCollision(settings, updated)
277
-
278
- Object.assign(connector, updated)
279
- this.store.write(settings)
280
- }
281
-
282
- updateGhConnector(
283
- channelName: string,
284
- connectorName: string,
285
- fields: { pollInterval?: number },
286
- ): void {
287
- const settings = this.store.read()
288
- const channel = this.requireChannel(settings, channelName)
289
- const connector = this.requireGhConnector(channel, connectorName)
290
-
291
- if (fields.pollInterval !== undefined) connector.pollInterval = fields.pollInterval
292
- connector.updatedAt = this.clock.iso()
293
-
294
- this.store.write(settings)
295
- }
296
-
297
- updateDiscordConnector(
298
- channelName: string,
299
- connectorName: string,
300
- fields: { botToken?: string },
301
- ): void {
302
- const settings = this.store.read()
303
- const channel = this.requireChannel(settings, channelName)
304
- const connector = this.requireDiscordConnector(channel, connectorName)
305
-
306
- const updated: DiscordConnectorConfig = {
307
- ...connector,
308
- botToken: fields.botToken ?? connector.botToken,
309
- updatedAt: this.clock.iso(),
310
- }
311
-
312
- this.assertNoTokenCollision(settings, updated)
313
-
314
- Object.assign(connector, updated)
315
- this.store.write(settings)
316
- }
317
-
318
- listScheduleEntries(channelName: string, connectorName: string): ScheduleEntry[] {
319
- const channel = this.requireChannel(this.store.read(), channelName)
320
- const connector = this.requireScheduleConnector(channel, connectorName)
321
-
322
- return connector.entries
323
- }
324
-
325
- addScheduleEntry(
326
- channelName: string,
327
- connectorName: string,
328
- entry: Pick<ScheduleEntry, "cron" | "prompt"> &
329
- Partial<Pick<ScheduleEntry, "id" | "enabled" | "catchupPolicy">>,
330
- ): ScheduleEntry {
331
- const settings = this.store.read()
332
- const channel = this.requireChannel(settings, channelName)
333
- const connector = this.requireScheduleConnector(channel, connectorName)
334
-
335
- const persisted: ScheduleEntry = {
336
- id: entry.id ?? this.idGenerator.generate(),
337
- cron: entry.cron,
338
- prompt: entry.prompt,
339
- enabled: entry.enabled ?? true,
340
- catchupPolicy: entry.catchupPolicy ?? "latest",
341
- }
342
-
343
- connector.entries.push(persisted)
344
- connector.updatedAt = this.clock.iso()
345
- this.store.write(settings)
346
-
347
- return persisted
348
- }
349
-
350
- removeScheduleEntry(channelName: string, connectorName: string, id: string): void {
351
- const settings = this.store.read()
352
- const channel = this.requireChannel(settings, channelName)
353
- const connector = this.requireScheduleConnector(channel, connectorName)
354
- const index = connector.entries.findIndex((e) => e.id === id)
355
-
356
- if (index < 0) throw new Error(`schedule entry "${id}" not found`)
357
-
358
- connector.entries.splice(index, 1)
359
- connector.updatedAt = this.clock.iso()
360
- this.store.write(settings)
361
- }
362
-
363
- async call(channelName: string, connectorName: string, input: CallInput): Promise<unknown> {
364
- const connector = this.getConnector(channelName, connectorName)
365
-
366
- if (!connector) {
367
- throw new Error(`connector "${connectorName}" not found in channel "${channelName}"`)
368
- }
369
-
370
- const adapter = this.factory.createAdapter(connector)
371
-
372
- if (!adapter) {
373
- throw new Error(`connector type "${connector.type}" does not support outbound calls`)
374
- }
375
-
376
- return await adapter.call(input)
377
- }
378
-
379
- createListener(
380
- channelName: string,
381
- connectorName: string,
382
- ): { config: ConnectorConfig; channelId: string; listener: FunnelConnectorListener } | null {
383
- const channel = this.get(channelName)
384
-
385
- if (!channel) return null
386
-
387
- const connector = channel.connectors.find((c) => c.name === connectorName)
388
-
389
- if (!connector) return null
390
-
391
- return {
392
- config: connector,
393
- channelId: channel.id,
394
- listener: this.factory.createListener(channel.id, connector),
395
- }
396
- }
397
-
398
- createAllListeners(): {
399
- config: ConnectorConfig
400
- channelId: string
401
- channelName: string
402
- listener: FunnelConnectorListener
403
- }[] {
404
- const out: {
405
- config: ConnectorConfig
406
- channelId: string
407
- channelName: string
408
- listener: FunnelConnectorListener
409
- }[] = []
410
-
411
- for (const channel of this.list()) {
412
- for (const connector of channel.connectors) {
413
- out.push({
414
- config: connector,
415
- channelId: channel.id,
416
- channelName: channel.name,
417
- listener: this.factory.createListener(channel.id, connector),
418
- })
419
- }
420
- }
421
-
422
- return out
423
- }
424
-
425
- private requireChannel(settings: Settings, name: string): ChannelConfig {
426
- const channel = settings.channels.find((c) => c.name === name)
427
-
428
- if (!channel) throw new Error(`channel "${name}" not found`)
429
-
430
- return channel
431
- }
432
-
433
- private requireConnector(channel: ChannelConfig, connectorName: string): ConnectorConfig {
434
- const connector = channel.connectors.find((c) => c.name === connectorName)
435
-
436
- if (!connector) {
437
- throw new Error(`connector "${connectorName}" not found in channel "${channel.name}"`)
438
- }
439
-
440
- return connector
441
- }
442
-
443
- private requireSlackConnector(
444
- channel: ChannelConfig,
445
- connectorName: string,
446
- ): SlackConnectorConfig {
447
- const connector = this.requireConnector(channel, connectorName)
448
-
449
- if (connector.type !== "slack") {
450
- throw new Error(`connector "${connectorName}" is type "${connector.type}", not "slack"`)
451
- }
452
-
453
- return connector
454
- }
455
-
456
- private requireGhConnector(channel: ChannelConfig, connectorName: string): GhConnectorConfig {
457
- const connector = this.requireConnector(channel, connectorName)
458
-
459
- if (connector.type !== "gh") {
460
- throw new Error(`connector "${connectorName}" is type "${connector.type}", not "gh"`)
461
- }
462
-
463
- return connector
464
- }
465
-
466
- private requireDiscordConnector(
467
- channel: ChannelConfig,
468
- connectorName: string,
469
- ): DiscordConnectorConfig {
470
- const connector = this.requireConnector(channel, connectorName)
471
-
472
- if (connector.type !== "discord") {
473
- throw new Error(`connector "${connectorName}" is type "${connector.type}", not "discord"`)
474
- }
475
-
476
- return connector
477
- }
478
-
479
- private requireScheduleConnector(
480
- channel: ChannelConfig,
481
- connectorName: string,
482
- ): ScheduleConnectorConfig {
483
- const connector = this.requireConnector(channel, connectorName)
484
-
485
- if (connector.type !== "schedule") {
486
- throw new Error(`connector "${connectorName}" is type "${connector.type}", not "schedule"`)
487
- }
488
-
489
- return connector
490
- }
491
-
492
- private assertNoTokenCollision(settings: Settings, candidate: ConnectorConfig): void {
493
- const tokens = this.tokensOf(candidate)
494
-
495
- if (tokens.length === 0) return
496
-
497
- for (const channel of settings.channels) {
498
- for (const other of channel.connectors) {
499
- if (other.id === candidate.id) continue
500
-
501
- for (const token of this.tokensOf(other)) {
502
- if (tokens.includes(token)) {
503
- throw new Error(
504
- `token already in use by connector "${other.name}" in channel "${channel.name}"`,
505
- )
506
- }
507
- }
508
- }
509
- }
510
- }
511
-
512
- private tokensOf(connector: ConnectorConfig): string[] {
513
- if (connector.type === "slack") return [connector.botToken, connector.appToken]
514
- if (connector.type === "discord") return [connector.botToken]
515
-
516
- return []
517
- }
518
- }
519
-
520
- export type { ScheduleConnectorConfig }