@interactive-inc/claude-funnel 0.60.0 → 0.63.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 (88) hide show
  1. package/README.md +2 -2
  2. package/dist/bin.js +428 -761
  3. package/dist/{channels-2g_BU1N0.d.ts → channels-B8RQPrVq.d.ts} +17 -16
  4. package/dist/claude.d.ts +5 -7
  5. package/dist/claude.js +143 -36
  6. package/dist/{connector-descriptor-6SXJoszo.d.ts → connector-descriptor-ClEEbuW3.d.ts} +50 -11
  7. package/dist/connector-diagnostics-recorder-COtNEmUp.js +42 -0
  8. package/dist/connectors/discord.d.ts +31 -37
  9. package/dist/connectors/discord.js +3 -3
  10. package/dist/connectors/gh.d.ts +37 -33
  11. package/dist/connectors/gh.js +3 -3
  12. package/dist/connectors/schedule.d.ts +9 -57
  13. package/dist/connectors/schedule.js +3 -3
  14. package/dist/connectors/slack.d.ts +71 -131
  15. package/dist/connectors/slack.js +4 -3
  16. package/dist/diagnostics.d.ts +1 -1
  17. package/dist/diagnostics.js +1 -1
  18. package/dist/discord-connector-DIFkYBbi.js +250 -0
  19. package/dist/discord-connector-schema-D-bOVAKt.d.ts +22 -0
  20. package/dist/docs.js +1 -1
  21. package/dist/doctor.d.ts +1 -1
  22. package/dist/doctor.js +1 -1
  23. package/dist/{file-process-guard-DOlCr4GF.d.ts → file-process-guard-DGHxALfI.d.ts} +8 -6
  24. package/dist/{file-system-o51IsM0W.d.ts → file-system-VhwwXZbm.d.ts} +8 -0
  25. package/dist/flume-source-listener-Dim5szHG.d.ts +133 -0
  26. package/dist/{funnel-diagnostics-CSiJmPlZ.js → funnel-diagnostics-Cvk6Sk4x.js} +193 -43
  27. package/dist/{funnel-diagnostics-DpXOsCty.d.ts → funnel-diagnostics-b9ar0Ing.d.ts} +67 -5
  28. package/dist/{funnel-docs-BxXZ9Ksx.js → funnel-docs-C-ge0MuB.js} +42 -6
  29. package/dist/{funnel-doctor-CZf_0Luq.d.ts → funnel-doctor-CnRQi4kM.d.ts} +2 -2
  30. package/dist/{funnel-doctor-DiJCjHsg.js → funnel-doctor-XrI2GBH8.js} +1 -1
  31. package/dist/funnel-error-0t1MK1R6.js +75 -0
  32. package/dist/{funnel-recovery-DnLrdWO9.d.ts → funnel-recovery-CMhY8Jfk.d.ts} +1 -1
  33. package/dist/gateway/daemon.js +167 -527
  34. package/dist/gateway.d.ts +3 -3
  35. package/dist/gateway.js +3 -3
  36. package/dist/gh-connector-BUGCOEWS.js +187 -0
  37. package/dist/{gh-connector-schema-Rzwc1c1N.js → gh-connector-schema-CAqIhzGr.js} +7 -0
  38. package/dist/gh-connector-schema-DWQaB6gX.d.ts +16 -0
  39. package/dist/{index-CgY8NdMz.d.ts → index-DxRikYmu.d.ts} +37 -19
  40. package/dist/index.d.ts +182 -22
  41. package/dist/index.js +365 -174
  42. package/dist/{local-config-json-schema-JyLqOQNX.js → local-config-json-schema-DexV8vX3.js} +24 -4
  43. package/dist/local-config.d.ts +39 -2
  44. package/dist/local-config.js +53 -2
  45. package/dist/logger.js +1 -1
  46. package/dist/loopback-fetch-CVNuN3YZ.js +40 -0
  47. package/dist/{local-config-sync-Dh1Croqe.d.ts → memory-token-prompter-DP_YV9xX.d.ts} +30 -3
  48. package/dist/node-file-system-BOXIHW_Q.js +174 -0
  49. package/dist/{profiles-DSzTeKQw.js → profiles-ZHLONml4.js} +49 -49
  50. package/dist/{profiles-Cy5wXQ0L.d.ts → profiles-cVZQkM69.d.ts} +3 -3
  51. package/dist/profiles.d.ts +1 -1
  52. package/dist/profiles.js +1 -1
  53. package/dist/recovery.d.ts +1 -1
  54. package/dist/recovery.js +1 -1
  55. package/dist/resolve-connector-token-DxDG9mhf.js +22 -0
  56. package/dist/{schedule-connector-L4uzg5M8.js → schedule-connector-9k3gOIgl.js} +54 -55
  57. package/dist/schedule-connector-schema-Z0RXLgPI.d.ts +49 -0
  58. package/dist/settings-reader-BNxjsxCB.d.ts +27 -0
  59. package/dist/{settings-store-CUKSeTXC.js → settings-store-C2QdOH-t.js} +23 -4
  60. package/dist/slack-connector-BU86fIge.js +359 -0
  61. package/dist/slack-event-processor-BhCf5Wiy.d.ts +95 -0
  62. package/dist/slack-event-processor-xFDG3US0.js +176 -0
  63. package/dist/slot-fields-D-pvMgTK.js +249 -0
  64. package/dist/{memory-diagnostic-log-CI60kNfB.js → sqlite-diagnostic-log-DOTPW-tG.js} +373 -249
  65. package/dist/{yaml-render-qW34NlYz.js → yaml-render--J1_3BSA.js} +28 -21
  66. package/package.json +2 -4
  67. package/dist/discord-connector-BL36yvbL.js +0 -250
  68. package/dist/gateway-base-url-Dy4Ykuoh.js +0 -14
  69. package/dist/gh-connector-DpiixfQZ.js +0 -226
  70. package/dist/http-client-oICicjuO.d.ts +0 -18
  71. package/dist/memory-token-prompter-B4sjyaAq.d.ts +0 -57
  72. package/dist/memory-token-prompter-CZde7e6y.js +0 -61
  73. package/dist/node-file-system-Blr8pAir.js +0 -48
  74. package/dist/settings-reader-BIFB_j2f.d.ts +0 -18
  75. package/dist/slack-connector-DQIFPdBF.js +0 -484
  76. package/dist/slot-fields-CMoRpwuy.js +0 -45
  77. /package/dist/{connector-adapter-DU9Rvyec.js → connector-adapter-Dvs8N7ew.js} +0 -0
  78. /package/dist/{connector-listener-DR3aKOuK.js → connector-listener-mPGZYa8e.js} +0 -0
  79. /package/dist/{diagnostic-sql-reader-C9zR-Csp.js → diagnostic-sql-reader-oXZnWFf_.js} +0 -0
  80. /package/dist/{discord-connector-schema-B_N6IXLz.js → discord-connector-schema-B4YpWpR3.js} +0 -0
  81. /package/dist/{error-message-of-Byi4y0Uf.js → error-message-of-ColuYmAk.js} +0 -0
  82. /package/dist/{funnel-log-sqlite-sink-kqJbx2H7.js → funnel-log-sqlite-sink-DLYkY0pZ.js} +0 -0
  83. /package/dist/{funnel-recovery-BFdPjL6Z.js → funnel-recovery-DKnEutUS.js} +0 -0
  84. /package/dist/{node-http-client-lowp60Oa.js → node-http-client-u00atiKx.js} +0 -0
  85. /package/dist/{schedule-connector-schema-CfyuMCMh.js → schedule-connector-schema-DKEPZnVv.js} +0 -0
  86. /package/dist/{settings-reader-CtQ-Ix8_.js → settings-reader-9FcX3qS1.js} +0 -0
  87. /package/dist/{settings-schema-D1xcOqRu.d.ts → settings-schema-BL_c2Udm.d.ts} +0 -0
  88. /package/dist/{slack-connector-schema-C1zEf4TG.js → slack-connector-schema-Dem8to4P.js} +0 -0
@@ -1,7 +1,7 @@
1
- import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-D1xcOqRu.js";
2
- import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BIFB_j2f.js";
3
- import { A as FunnelConnectorAdapter, B as FunnelLogger, D as FunnelConnectorListener, I as FunnelProcessRunner, M as BaseConnectorConfig, k as CallInput, n as ConnectorBuildContext, o as ConnectorOperationContext, p as ConnectorDiagnosticLog, r as ConnectorDescriptor, s as ConnectorUpdateContext } from "./connector-descriptor-6SXJoszo.js";
4
- import { n as FunnelFileSystem } from "./file-system-o51IsM0W.js";
1
+ import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-BL_c2Udm.js";
2
+ import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BNxjsxCB.js";
3
+ import { B as FunnelProcessRunner, I as BaseConnectorConfig, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, c as FunnelClock, j as FunnelConnectorListener, l as FunnelHttpClient, n as ConnectorBuildContext, o as ConnectorOperationContext, r as ConnectorDescriptor, s as ConnectorUpdateContext } from "./connector-descriptor-ClEEbuW3.js";
4
+ import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
5
5
 
6
6
  //#region lib/engine/connectors/connector-registry.d.ts
7
7
  type Deps$1 = {
@@ -11,9 +11,18 @@ type Deps$1 = {
11
11
  descriptors: ConnectorDescriptor[];
12
12
  fs?: FunnelFileSystem;
13
13
  process?: FunnelProcessRunner;
14
+ http?: FunnelHttpClient;
15
+ clock?: FunnelClock;
14
16
  logger?: FunnelLogger;
15
17
  diagnosticLog?: ConnectorDiagnosticLog;
16
18
  dir?: string;
19
+ /**
20
+ * Shared shutdown signal forwarded to every listener built by this
21
+ * registry. Hosts wire one signal here (e.g. from a per-Funnel
22
+ * AbortController in a SIGTERM handler) and every listener tears down
23
+ * together when it aborts.
24
+ */
25
+ signal?: AbortSignal;
17
26
  };
18
27
  /**
19
28
  * Dispatches connector work to injected descriptors by `type`. Replaces the old
@@ -28,8 +37,11 @@ declare class FunnelConnectorRegistry {
28
37
  private readonly descriptors;
29
38
  private readonly fs;
30
39
  private readonly process;
40
+ private readonly http;
41
+ private readonly clock;
31
42
  private readonly logger;
32
43
  private readonly diagnosticLog;
44
+ private readonly signal;
33
45
  private readonly dir;
34
46
  constructor(deps: Deps$1);
35
47
  has(type: string): boolean;
@@ -59,17 +71,6 @@ type ProfileChannelChecker = {
59
71
  hasChannelRef(channelId: string): boolean;
60
72
  };
61
73
  //#endregion
62
- //#region lib/engine/time/clock.d.ts
63
- /**
64
- * Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
65
- * is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
66
- */
67
- declare abstract class FunnelClock {
68
- abstract now(): Date;
69
- millis(): number;
70
- iso(): string;
71
- }
72
- //#endregion
73
74
  //#region lib/engine/channels/channels.d.ts
74
75
  type Deps = {
75
76
  store: FunnelSettingsReader;
@@ -171,4 +172,4 @@ declare class FunnelChannels {
171
172
  private assertNoTokenCollision;
172
173
  }
173
174
  //#endregion
174
- export { FunnelConnectorRegistry as a, FunnelClock as i, ChannelConnectorView as n, FunnelChannels as r, AddConnectorInput as t };
175
+ export { FunnelConnectorRegistry as i, ChannelConnectorView as n, FunnelChannels as r, AddConnectorInput as t };
package/dist/claude.d.ts CHANGED
@@ -1,9 +1,7 @@
1
- import { a as ProcessGuard, c as ChannelResolver, i as SessionStore, n as FunnelClaude, o as McpInstaller, r as LaunchOptions, s as GatewayController, t as FileProcessGuard } from "./file-process-guard-DOlCr4GF.js";
2
- import { r as ConnectorDescriptor } from "./connector-descriptor-6SXJoszo.js";
3
- import { n as FunnelFileSystem } from "./file-system-o51IsM0W.js";
4
- import { t as FunnelProfiles } from "./profiles-Cy5wXQ0L.js";
5
- import { a as FunnelLocalConfig, c as LOCAL_CONFIG_FILENAME, d as channelSpecSchema, f as connectorSpecSchema, i as FunnelTokenPrompter, l as LocalConfig, m as profileSpecSchema, n as FunnelLocalConfigSync, o as ChannelSpec, p as localConfigSchema, r as LocalConfigSyncResult, s as ConnectorSpec, t as ConnectorSyncOutcome, u as ProfileSpec } from "./local-config-sync-Dh1Croqe.js";
6
- import { i as funnelJsonSchema, n as NodeFunnelTokenPrompter, r as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-B4sjyaAq.js";
1
+ import { a as ProcessGuard, c as ChannelResolver, i as SessionStore, n as FunnelClaude, o as McpInstaller, r as LaunchOptions, s as GatewayController, t as FunnelFileProcessGuard } from "./file-process-guard-DGHxALfI.js";
2
+ import { r as ConnectorDescriptor } from "./connector-descriptor-ClEEbuW3.js";
3
+ import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
4
+ import { t as FunnelProfiles } from "./profiles-cVZQkM69.js";
7
5
 
8
6
  //#region lib/engine/mcp/mcp.d.ts
9
7
  declare const FUNNEL_MCP_COMMAND = "bun";
@@ -58,4 +56,4 @@ type ChannelServerOptions = {
58
56
  */
59
57
  declare const startChannelServer: (options?: ChannelServerOptions) => Promise<void>;
60
58
  //#endregion
61
- export { ChannelResolver, ChannelServerOptions, ChannelSpec, ConnectorSpec, ConnectorSyncOutcome, FUNNEL_MCP_ARGS, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FileProcessGuard, FunnelClaude, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelMcp, FunnelProfiles, FunnelTokenPrompter, GatewayController, LOCAL_CONFIG_FILENAME, LaunchOptions, LocalConfig, LocalConfigSyncResult, McpInstaller, MemoryFunnelTokenPrompter, NodeFunnelTokenPrompter, ProcessGuard, ProfileSpec, SessionStore, channelSpecSchema, connectorSpecSchema, funnelJsonSchema, localConfigSchema, profileSpecSchema, startChannelServer };
59
+ export { ChannelResolver, ChannelServerOptions, FUNNEL_MCP_ARGS, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FunnelClaude, FunnelFileProcessGuard, FunnelMcp, FunnelProfiles, GatewayController, LaunchOptions, McpInstaller, ProcessGuard, SessionStore, startChannelServer };
package/dist/claude.js CHANGED
@@ -1,15 +1,13 @@
1
- import { t as gatewayLoopbackUrl } from "./gateway-base-url-Dy4Ykuoh.js";
2
- import { d as settingsSchema, o as resolveFunnelPort } from "./settings-store-CUKSeTXC.js";
3
- import { a as FunnelMcp, i as FUNNEL_MCP_NAME, n as FUNNEL_MCP_ARGS, o as FileProcessGuard, r as FUNNEL_MCP_COMMAND, s as FunnelClaude, t as renderYaml } from "./yaml-render-qW34NlYz.js";
4
- import { t as FunnelDocs } from "./funnel-docs-BxXZ9Ksx.js";
5
- import { a as FunnelLocalConfig, c as connectorSpecSchema, i as FunnelTokenPrompter, l as localConfigSchema, n as NodeFunnelTokenPrompter, o as LOCAL_CONFIG_FILENAME, r as FunnelLocalConfigSync, s as channelSpecSchema, t as funnelJsonSchema, u as profileSpecSchema } from "./local-config-json-schema-JyLqOQNX.js";
6
- import { t as FunnelProfiles } from "./profiles-DSzTeKQw.js";
7
- import { t as errorMessageOf } from "./error-message-of-Byi4y0Uf.js";
8
- import { t as discordConnector } from "./discord-connector-BL36yvbL.js";
9
- import { t as ghConnector } from "./gh-connector-DpiixfQZ.js";
10
- import { t as scheduleConnector } from "./schedule-connector-L4uzg5M8.js";
11
- import { t as slackConnector } from "./slack-connector-DQIFPdBF.js";
12
- import { n as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-CZde7e6y.js";
1
+ import { n as gatewayLoopbackUrl, t as loopbackFetch } from "./loopback-fetch-CVNuN3YZ.js";
2
+ import { d as settingsSchema, o as resolveFunnelPort } from "./settings-store-C2QdOH-t.js";
3
+ import { a as FunnelMcp, i as FUNNEL_MCP_NAME, n as FUNNEL_MCP_ARGS, o as FunnelFileProcessGuard, r as FUNNEL_MCP_COMMAND, s as FunnelClaude, t as renderYaml } from "./yaml-render--J1_3BSA.js";
4
+ import { t as errorMessageOf } from "./error-message-of-ColuYmAk.js";
5
+ import { t as FunnelDocs } from "./funnel-docs-C-ge0MuB.js";
6
+ import { t as FunnelProfiles } from "./profiles-ZHLONml4.js";
7
+ import { t as discordConnector } from "./discord-connector-DIFkYBbi.js";
8
+ import { t as ghConnector } from "./gh-connector-BUGCOEWS.js";
9
+ import { t as scheduleConnector } from "./schedule-connector-9k3gOIgl.js";
10
+ import { t as slackConnector } from "./slack-connector-BU86fIge.js";
13
11
  import { join } from "node:path";
14
12
  import { existsSync, readFileSync } from "node:fs";
15
13
  import { homedir } from "node:os";
@@ -63,7 +61,12 @@ const toYamlResult = async (res) => {
63
61
  }
64
62
  };
65
63
  const getJson = async (url, headers, options = {}) => {
66
- const res = await fetch(url, { headers }).catch(() => null);
64
+ let res = null;
65
+ try {
66
+ res = await loopbackFetch(url, { headers });
67
+ } catch {
68
+ res = null;
69
+ }
67
70
  if (!res) {
68
71
  if (options.offlineFallback !== void 0) return yamlResult(options.offlineFallback);
69
72
  return errorResult("gateway unreachable", GATEWAY_OFFLINE_HINT);
@@ -71,14 +74,19 @@ const getJson = async (url, headers, options = {}) => {
71
74
  return toYamlResult(res);
72
75
  };
73
76
  const postJson = async (url, headers, body, options = {}) => {
74
- const res = await fetch(url, {
75
- method: "POST",
76
- headers: {
77
- ...headers,
78
- "content-type": "application/json"
79
- },
80
- body: JSON.stringify(body)
81
- }).catch(() => null);
77
+ let res = null;
78
+ try {
79
+ res = await loopbackFetch(url, {
80
+ method: "POST",
81
+ headers: {
82
+ ...headers,
83
+ "content-type": "application/json"
84
+ },
85
+ body: JSON.stringify(body)
86
+ }, 3e4);
87
+ } catch {
88
+ res = null;
89
+ }
82
90
  if (!res) {
83
91
  if (options.offlineFallback !== void 0) return yamlResult(options.offlineFallback);
84
92
  return errorResult("gateway unreachable", GATEWAY_OFFLINE_HINT);
@@ -99,7 +107,9 @@ const handleBuiltinTool = async (deps) => {
99
107
  if (deps.token) headers.authorization = `Bearer ${deps.token}`;
100
108
  const args = deps.args;
101
109
  const channelArg = typeof args?.channel === "string" ? args.channel : null;
110
+ const connectorArg = typeof args?.connector === "string" ? args.connector : null;
102
111
  const limitArg = typeof args?.limit === "number" ? args.limit : void 0;
112
+ const grepArg = typeof args?.grep === "string" ? args.grep : null;
103
113
  const seqArg = typeof args?.seq === "number" ? args.seq : void 0;
104
114
  const modeArg = args?.mode === "safe" || args?.mode === "aggressive" || args?.mode === "off" ? args.mode : "off";
105
115
  const topicArg = typeof args?.topic === "string" ? args.topic : null;
@@ -121,9 +131,12 @@ const handleBuiltinTool = async (deps) => {
121
131
  },
122
132
  knownChannels: deps.allChannels.map((ch) => ch.name)
123
133
  } });
124
- if (deps.name === "fnl_recent_events") return getJson(`${base}/diagnostics/events?${eventListQuery(channelArg, limitArg)}`, headers);
125
- if (deps.name === "fnl_dropped_events") return getJson(`${base}/diagnostics/dropped?${eventListQuery(channelArg, limitArg)}`, headers);
126
- if (deps.name === "fnl_connection_errors") return getJson(`${base}/diagnostics/errors?${eventListQuery(channelArg, limitArg)}`, headers);
134
+ if (deps.name === "fnl_recent_events") return getJson(`${base}/diagnostics/events?${eventListQuery(channelArg, connectorArg, limitArg)}`, headers);
135
+ if (deps.name === "fnl_dropped_events") return getJson(`${base}/diagnostics/dropped?${eventListQuery(channelArg, connectorArg, limitArg)}`, headers);
136
+ if (deps.name === "fnl_raw_events") return getJson(`${base}/diagnostics/raw?${eventListQuery(channelArg, connectorArg, limitArg)}`, headers);
137
+ if (deps.name === "fnl_connection_errors") return getJson(`${base}/diagnostics/errors?${eventListQuery(channelArg, connectorArg, limitArg)}`, headers);
138
+ if (deps.name === "fnl_connection_timeline") return getJson(`${base}/diagnostics/connection?${eventListQuery(channelArg, connectorArg, limitArg)}`, headers);
139
+ if (deps.name === "fnl_logs") return getJson(`${base}/diagnostics/logs?${logsQuery(grepArg, limitArg)}`, headers);
127
140
  if (deps.name === "fnl_replay_event") {
128
141
  if (!channelArg) return errorResult("channel is required", "Provide the channel name (see fnl_status)");
129
142
  return postJson(`${base}/diagnostics/replay`, headers, {
@@ -145,9 +158,16 @@ const handleBuiltinTool = async (deps) => {
145
158
  }
146
159
  return errorResult(`unknown built-in tool: ${deps.name}`, null);
147
160
  };
148
- const eventListQuery = (channel, limit) => {
161
+ const eventListQuery = (channel, connector, limit) => {
149
162
  const params = new URLSearchParams();
150
163
  if (channel) params.set("channel", channel);
164
+ if (connector) params.set("connector", connector);
165
+ if (limit !== void 0) params.set("limit", String(limit));
166
+ return params.toString();
167
+ };
168
+ const logsQuery = (grep, limit) => {
169
+ const params = new URLSearchParams();
170
+ if (grep) params.set("grep", grep);
151
171
  if (limit !== void 0) params.set("limit", String(limit));
152
172
  return params.toString();
153
173
  };
@@ -210,7 +230,10 @@ const buildChannelServerInstructions = (allChannels, currentChannelName) => {
210
230
  " fnl_debug per-channel diagnosis (subset of fnl_doctor)",
211
231
  " fnl_recent_events last N processed events with outcome",
212
232
  " fnl_dropped_events events filtered out (skip:*) with reason",
233
+ " fnl_raw_events raw inbound rows before any processing",
213
234
  " fnl_connection_errors listener auth-failed / error events",
235
+ " fnl_connection_timeline full lifecycle (started/connected/disconnected/stopped)",
236
+ " fnl_logs tail of funnel.log (flume internal + structured app logs)",
214
237
  " fnl_replay_event replay a past event to test a fix",
215
238
  " fnl_docs embedded docs (architecture / debugging / recipes / …)",
216
239
  "",
@@ -252,7 +275,10 @@ const BUILTIN_TOOL_NAMES = [
252
275
  "fnl_debug",
253
276
  "fnl_recent_events",
254
277
  "fnl_dropped_events",
278
+ "fnl_raw_events",
255
279
  "fnl_connection_errors",
280
+ "fnl_connection_timeline",
281
+ "fnl_logs",
256
282
  "fnl_replay_event",
257
283
  "fnl_docs"
258
284
  ];
@@ -342,6 +368,10 @@ const buildBuiltinTools = (allChannels) => {
342
368
  type: "object",
343
369
  properties: {
344
370
  channel: channelArgSchema,
371
+ connector: {
372
+ type: "string",
373
+ description: "Connector name to filter (optional)"
374
+ },
345
375
  limit: {
346
376
  type: "number",
347
377
  description: "Max rows (default 20)"
@@ -356,6 +386,10 @@ const buildBuiltinTools = (allChannels) => {
356
386
  type: "object",
357
387
  properties: {
358
388
  channel: channelArgSchema,
389
+ connector: {
390
+ type: "string",
391
+ description: "Connector name to filter (optional)"
392
+ },
359
393
  limit: {
360
394
  type: "number",
361
395
  description: "Max rows (default 20)"
@@ -370,6 +404,28 @@ const buildBuiltinTools = (allChannels) => {
370
404
  type: "object",
371
405
  properties: {
372
406
  channel: channelArgSchema,
407
+ connector: {
408
+ type: "string",
409
+ description: "Connector name to filter (optional)"
410
+ },
411
+ limit: {
412
+ type: "number",
413
+ description: "Max rows (default 20)"
414
+ }
415
+ }
416
+ }
417
+ },
418
+ {
419
+ name: "fnl_connection_timeline",
420
+ description: "Full connection lifecycle (started / connected / disconnected / stopped + auth-failed / error). Use when fnl_connection_errors only shows the failure but you need to see how the listener got there — was it ever connected, is it flapping, how many reconnects.",
421
+ inputSchema: {
422
+ type: "object",
423
+ properties: {
424
+ channel: channelArgSchema,
425
+ connector: {
426
+ type: "string",
427
+ description: "Connector name to filter (optional)"
428
+ },
373
429
  limit: {
374
430
  type: "number",
375
431
  description: "Max rows (default 20)"
@@ -377,6 +433,41 @@ const buildBuiltinTools = (allChannels) => {
377
433
  }
378
434
  }
379
435
  },
436
+ {
437
+ name: "fnl_raw_events",
438
+ description: "Return raw inbound rows the connector recorded BEFORE any processing. Use when fnl_dropped_events and fnl_recent_events both come up empty — this is the table that captures every event the listener ever saw, including ones it dropped pre-processor (envelope shape change, pre-READY, malformed).",
439
+ inputSchema: {
440
+ type: "object",
441
+ properties: {
442
+ channel: channelArgSchema,
443
+ connector: {
444
+ type: "string",
445
+ description: "Connector name to filter (optional)"
446
+ },
447
+ limit: {
448
+ type: "number",
449
+ description: "Max rows (default 20)"
450
+ }
451
+ }
452
+ }
453
+ },
454
+ {
455
+ name: "fnl_logs",
456
+ description: "Return the tail of the gateway daemon log file (funnel.log) where structured FunnelLogger output and forwarded flume logs land. Use when fnl_connection_timeline shows a status change but you need the upstream reason (slack/auth.test failed, github http 401, discord gateway close 4014) — those messages only appear in the log file.",
457
+ inputSchema: {
458
+ type: "object",
459
+ properties: {
460
+ grep: {
461
+ type: "string",
462
+ description: "Case-insensitive substring filter (e.g. 'slack', 'auth')"
463
+ },
464
+ limit: {
465
+ type: "number",
466
+ description: "Max lines to return (default 200)"
467
+ }
468
+ }
469
+ }
470
+ },
380
471
  {
381
472
  name: "fnl_replay_event",
382
473
  description: "Re-publish a past event back into a channel so subscribers receive it again. Call after applying a fix to verify Claude handles the previously-failed event correctly. Without `seq`, replays the most recent emitted event for that channel.",
@@ -449,13 +540,17 @@ var FunnelChannelSubscriber = class {
449
540
  }, this.state.reconnectDelay);
450
541
  this.state.reconnectDelay = Math.min(this.state.reconnectDelay * 2, MAX_RECONNECT_DELAY);
451
542
  });
452
- ws.addEventListener("error", () => {});
543
+ ws.addEventListener("error", (event) => {
544
+ const reason = readErrorEventMessage(event);
545
+ process.stderr.write(`funnel: ws error: ${reason}\n`);
546
+ });
453
547
  }
454
548
  async handleMessage(event) {
455
549
  try {
456
550
  const payload = JSON.parse(String(event.data));
457
551
  const eventType = payload.meta?.event_type ?? "unknown";
458
- if (typeof payload.offset === "number" && payload.offset > this.state.lastOffset) this.state.lastOffset = payload.offset;
552
+ const offset = typeof payload.offset === "number" ? payload.offset : null;
553
+ if (offset !== null && offset > this.state.lastOffset) this.state.lastOffset = offset;
459
554
  process.stderr.write(`funnel: received event (${eventType})\n`);
460
555
  await this.props.server.notification({
461
556
  method: "notifications/claude/channel",
@@ -465,18 +560,25 @@ var FunnelChannelSubscriber = class {
465
560
  }
466
561
  });
467
562
  } catch (error) {
468
- process.stderr.write(`funnel: error: ${errorMessageOf(error)}\n`);
563
+ process.stderr.write(`funnel: error handling ws message (offset=${this.state.lastOffset}): ${errorMessageOf(error)}\n`);
469
564
  }
470
565
  }
471
566
  };
567
+ const readErrorEventMessage = (event) => {
568
+ if (event instanceof Error) return event.message;
569
+ if ("message" in event && typeof event.message === "string") return event.message;
570
+ if ("error" in event && event.error instanceof Error) return event.error.message;
571
+ return "unknown";
572
+ };
472
573
  //#endregion
473
574
  //#region lib/engine/connectors/builtin-connectors.ts
474
575
  /**
475
- * The four built-in connector descriptors. Importing this pulls every connector
476
- * SDK (@slack/bolt, discord.js, …) into the bundle, so it is used ONLY by
477
- * full-bundle entry points (the `fnl` CLI, the gateway daemon, the MCP server)
478
- * never by the library's public barrel. Programmatic hosts pass the descriptors
479
- * they actually need to `new Funnel({ connectors: [...] })` instead.
576
+ * The four built-in connector descriptors. Importing this pulls every
577
+ * connector's protocol code (Slack Socket Mode, Discord Gateway, GitHub
578
+ * polling) into the bundle, so it is used ONLY by full-bundle entry points
579
+ * (the `fnl` CLI, the gateway daemon, the MCP server) never by the
580
+ * library's public barrel. Programmatic hosts pass the descriptors they
581
+ * actually need to `new Funnel({ connectors: [...] })` instead.
480
582
  */
481
583
  const builtinConnectors = () => [
482
584
  slackConnector(),
@@ -533,12 +635,17 @@ const readAllChannels = (dir) => {
533
635
  try {
534
636
  const raw = JSON.parse(readFileSync(settingsPath, "utf-8"));
535
637
  const parsed = settingsSchema.safeParse(raw);
536
- if (!parsed.success) return [];
638
+ if (!parsed.success) {
639
+ process.stderr.write(`funnel: ${settingsPath} failed schema validation: ${parsed.error.message}\n`);
640
+ return [];
641
+ }
537
642
  return parsed.data.channels.map((c) => ({
538
643
  id: c.id,
539
644
  name: c.name
540
645
  }));
541
- } catch {
646
+ } catch (error) {
647
+ const message = error instanceof Error ? error.message : String(error);
648
+ process.stderr.write(`funnel: failed to read ${settingsPath}: ${message}\n`);
542
649
  return [];
543
650
  }
544
651
  };
@@ -643,4 +750,4 @@ const dispatchConnectorTool = async (input) => {
643
750
  }
644
751
  };
645
752
  //#endregion
646
- export { FUNNEL_MCP_ARGS, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FileProcessGuard, FunnelClaude, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelMcp, FunnelProfiles, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, MemoryFunnelTokenPrompter, NodeFunnelTokenPrompter, channelSpecSchema, connectorSpecSchema, funnelJsonSchema, localConfigSchema, profileSpecSchema, startChannelServer };
753
+ export { FUNNEL_MCP_ARGS, FUNNEL_MCP_COMMAND, FUNNEL_MCP_NAME, FunnelClaude, FunnelFileProcessGuard, FunnelMcp, FunnelProfiles, startChannelServer };
@@ -1,4 +1,4 @@
1
- import { n as FunnelFileSystem } from "./file-system-o51IsM0W.js";
1
+ import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region lib/engine/logger/logger.d.ts
@@ -185,10 +185,10 @@ declare const connectorConnectionEventSchema: z.ZodObject<{
185
185
  channel_id: z.ZodNullable<z.ZodString>;
186
186
  status: z.ZodEnum<{
187
187
  error: "error";
188
+ "auth-failed": "auth-failed";
188
189
  started: "started";
189
190
  connected: "connected";
190
191
  disconnected: "disconnected";
191
- "auth-failed": "auth-failed";
192
192
  stopped: "stopped";
193
193
  }>;
194
194
  detail: z.ZodString;
@@ -298,20 +298,59 @@ declare abstract class ConnectorDiagnosticLog {
298
298
  abstract close(): void;
299
299
  }
300
300
  //#endregion
301
+ //#region lib/engine/http/http-client.d.ts
302
+ type HttpRequest = {
303
+ method: string;
304
+ url: string;
305
+ headers?: Record<string, string>;
306
+ body?: string;
307
+ };
308
+ type HttpResponse = {
309
+ status: number;
310
+ ok: boolean;
311
+ text(): Promise<string>;
312
+ json(): Promise<unknown>;
313
+ };
314
+ declare abstract class FunnelHttpClient {
315
+ abstract fetch(request: HttpRequest): Promise<HttpResponse>;
316
+ }
317
+ //#endregion
318
+ //#region lib/engine/time/clock.d.ts
319
+ /**
320
+ * Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
321
+ * is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
322
+ */
323
+ declare abstract class FunnelClock {
324
+ abstract now(): Date;
325
+ millis(): number;
326
+ iso(): string;
327
+ }
328
+ //#endregion
301
329
  //#region lib/engine/connectors/connector-descriptor.d.ts
302
330
  /** Boundaries a listener needs, supplied by the registry at build time. */
303
331
  type ConnectorListenerDeps = {
304
332
  channelId: string;
305
333
  fs: FunnelFileSystem;
306
- process: FunnelProcessRunner;
334
+ process: FunnelProcessRunner; /** HTTP client for self-detection (Slack auth.test), reactions, and other listener-side REST calls. */
335
+ http: FunnelHttpClient; /** Wall clock — listeners that own a timer (schedule) read it from here so tests can inject a fake. */
336
+ clock: FunnelClock;
307
337
  logger?: FunnelLogger;
308
- diagnosticLog?: ConnectorDiagnosticLog; /** Resolves the per-connector state directory (`<dir>/channels/<id>/connectors/<id>`). */
338
+ diagnosticLog?: ConnectorDiagnosticLog;
339
+ /**
340
+ * Optional shutdown signal forwarded to flume-backed listeners. When the
341
+ * host aborts the signal, the listener's Flume tears down its WebSocket /
342
+ * fetch loop without waiting for the supervisor's `stop()`. Hosts that want
343
+ * a clean SIGTERM story wire `controller.signal` here and call
344
+ * `controller.abort()` in their shutdown handler.
345
+ */
346
+ signal?: AbortSignal; /** Resolves the per-connector state directory (`<dir>/channels/<id>/connectors/<id>`). */
309
347
  connectorDir: (channelId: string, connectorId: string) => string;
310
348
  };
311
349
  /** Boundaries an adapter needs. Adapters are channel-agnostic. */
312
350
  type ConnectorAdapterDeps = {
313
351
  fs: FunnelFileSystem;
314
- process: FunnelProcessRunner;
352
+ process: FunnelProcessRunner; /** HTTP client for outbound calls. Used by the Slack adapter, future REST adapters. */
353
+ http: FunnelHttpClient;
315
354
  logger?: FunnelLogger;
316
355
  };
317
356
  type ConnectorBuildContext = {
@@ -344,11 +383,11 @@ type ConnectorOperation = (props: {
344
383
  * descriptor accepts/returns `BaseConnectorConfig` at every boundary and parses
345
384
  * to its concrete config internally (via `schema`), so the registry can hold a
346
385
  * homogeneous `ConnectorDescriptor[]` with no variance gymnastics. Type-specific
347
- * launch hooks (Slack `onAppCreated`, Schedule `onFired`) are closed over by the
348
- * descriptor factory, not threaded through here. Each descriptor parses configs
349
- * to its concrete shape internally (it owns its zod schema), so no schema is
350
- * exposed here — that also sidesteps the variance trap of a `ZodType<Slack>`
351
- * field declared as `ZodType<BaseConnectorConfig>`.
386
+ * launch hooks (e.g. Schedule `onFired`) are closed over by the descriptor
387
+ * factory, not threaded through here. Each descriptor parses configs to its
388
+ * concrete shape internally (it owns its zod schema), so no schema is exposed
389
+ * here — that also sidesteps the variance trap of a `ZodType<Slack>` field
390
+ * declared as `ZodType<BaseConnectorConfig>`.
352
391
  */
353
392
  type ConnectorDescriptor = {
354
393
  type: string;
@@ -361,4 +400,4 @@ type ConnectorDescriptor = {
361
400
  operations: Record<string, ConnectorOperation>;
362
401
  };
363
402
  //#endregion
364
- export { FunnelConnectorAdapter as A, FunnelLogger as B, StoredRawEvent as C, FunnelConnectorListener as D, connectorRawEventSchema as E, DetachOptions as F, FunnelProcessRunner as I, ProcessSnapshot as L, BaseConnectorConfig as M, baseConnectorConfigSchema as N, NotifyFn as O, AttachOptions as P, RunOptions as R, StoredProcessedEvent as S, connectorProcessedEventSchema as T, ConnectorQuery as _, ConnectorOperation as a, ConnectorRawRecord as b, CONNECTOR_CONNECTION_STATUSES as c, ConnectorConnectionRecord as d, ConnectorConnectionStatus as f, ConnectorProcessedRecord as g, ConnectorProcessedQuery as h, ConnectorListenerDeps as i, JsonValue as j, CallInput as k, ConnectorConnectionEvent as l, ConnectorProcessedEvent as m, ConnectorBuildContext as n, ConnectorOperationContext as o, ConnectorDiagnosticLog as p, ConnectorDescriptor as r, ConnectorUpdateContext as s, ConnectorAdapterDeps as t, ConnectorConnectionQuery as u, ConnectorRawEvent as v, connectorConnectionEventSchema as w, StoredConnectionEvent as x, ConnectorRawQuery as y, RunResult as z };
403
+ export { connectorRawEventSchema as A, FunnelProcessRunner as B, ConnectorRawQuery as C, StoredRawEvent as D, StoredProcessedEvent as E, JsonValue as F, RunOptions as H, BaseConnectorConfig as I, baseConnectorConfigSchema as L, NotifyFn as M, CallInput as N, connectorConnectionEventSchema as O, FunnelConnectorAdapter as P, AttachOptions as R, ConnectorRawEvent as S, StoredConnectionEvent as T, RunResult as U, ProcessSnapshot as V, FunnelLogger as W, ConnectorDiagnosticLog as _, ConnectorOperation as a, ConnectorProcessedRecord as b, FunnelClock as c, HttpResponse as d, CONNECTOR_CONNECTION_STATUSES as f, ConnectorConnectionStatus as g, ConnectorConnectionRecord as h, ConnectorListenerDeps as i, FunnelConnectorListener as j, connectorProcessedEventSchema as k, FunnelHttpClient as l, ConnectorConnectionQuery as m, ConnectorBuildContext as n, ConnectorOperationContext as o, ConnectorConnectionEvent as p, ConnectorDescriptor as r, ConnectorUpdateContext as s, ConnectorAdapterDeps as t, HttpRequest as u, ConnectorProcessedEvent as v, ConnectorRawRecord as w, ConnectorQuery as x, ConnectorProcessedQuery as y, DetachOptions as z };
@@ -0,0 +1,42 @@
1
+ //#region lib/engine/connectors/connector-diagnostics-recorder.ts
2
+ /**
3
+ * Wraps a `ConnectorDiagnosticLog` with the per-listener axes (`type` /
4
+ * `connectorId` / `channelId`) so call sites only pass the row-specific
5
+ * fields. When no log is wired every call is a silent no-op.
6
+ */
7
+ var FunnelConnectorDiagnosticsRecorder = class {
8
+ constructor(props) {
9
+ this.props = props;
10
+ Object.freeze(this);
11
+ }
12
+ recordRaw(eventId, payload) {
13
+ this.props.log?.recordRaw({
14
+ eventId,
15
+ type: this.props.type,
16
+ connectorId: this.props.connectorId,
17
+ channelId: this.props.channelId,
18
+ payload
19
+ });
20
+ }
21
+ recordProcessed(eventId, outcome, payload) {
22
+ this.props.log?.recordProcessed({
23
+ eventId,
24
+ type: this.props.type,
25
+ connectorId: this.props.connectorId,
26
+ channelId: this.props.channelId,
27
+ outcome,
28
+ payload
29
+ });
30
+ }
31
+ recordConnection(status, detail) {
32
+ this.props.log?.recordConnection({
33
+ type: this.props.type,
34
+ connectorId: this.props.connectorId,
35
+ channelId: this.props.channelId,
36
+ status,
37
+ detail
38
+ });
39
+ }
40
+ };
41
+ //#endregion
42
+ export { FunnelConnectorDiagnosticsRecorder as t };
@@ -1,26 +1,8 @@
1
- import { A as FunnelConnectorAdapter, B as FunnelLogger, D as FunnelConnectorListener, O as NotifyFn, k as CallInput, p as ConnectorDiagnosticLog, r as ConnectorDescriptor } from "../connector-descriptor-6SXJoszo.js";
2
- import { t as FunnelHttpClient } from "../http-client-oICicjuO.js";
3
- import { z } from "zod";
1
+ import { M as NotifyFn, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, l as FunnelHttpClient, r as ConnectorDescriptor } from "../connector-descriptor-ClEEbuW3.js";
2
+ import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-D-bOVAKt.js";
3
+ import { t as FunnelFlumeSourceListener } from "../flume-source-listener-Dim5szHG.js";
4
+ import { FlumeRuntimeDeps } from "@interactive-inc/flume";
4
5
 
5
- //#region lib/engine/connectors/discord-connector-schema.d.ts
6
- /**
7
- * Like slack, a discord connector holds either a literal `botToken` or a
8
- * `botTokenEnv` reference resolved from `process.env` at listener start. The
9
- * reference form keeps the secret out of settings.json, but is only set through
10
- * the engine API (`new Funnel(...)`); funnel.json and the `fnl` CLI produce
11
- * literals.
12
- */
13
- declare const discordConnectorSchema: z.ZodObject<{
14
- id: z.ZodString;
15
- name: z.ZodString;
16
- type: z.ZodLiteral<"discord">;
17
- botToken: z.ZodOptional<z.ZodString>;
18
- botTokenEnv: z.ZodOptional<z.ZodString>;
19
- createdAt: z.ZodOptional<z.ZodString>;
20
- updatedAt: z.ZodOptional<z.ZodString>;
21
- }, z.core.$strip>;
22
- type DiscordConnectorConfig = z.infer<typeof discordConnectorSchema>;
23
- //#endregion
24
6
  //#region lib/engine/connectors/discord-adapter.d.ts
25
7
  type Deps$1 = {
26
8
  config: DiscordConnectorConfig; /** Environment used to resolve a `botTokenEnv` reference. Defaults to process.env. */
@@ -38,6 +20,9 @@ declare class FunnelDiscordAdapter extends FunnelConnectorAdapter {
38
20
  /**
39
21
  * Discord connector descriptor. Pass `discordConnector()` to
40
22
  * `new Funnel({ connectors: [...] })` to enable the type.
23
+ *
24
+ * The listener is backed by `@interactive-inc/flume`'s `FlumeDiscordSource`
25
+ * (raw Gateway WebSocket).
41
26
  */
42
27
  declare const discordConnector: () => ConnectorDescriptor;
43
28
  //#endregion
@@ -68,28 +53,37 @@ declare class FunnelDiscordEventProcessor {
68
53
  process(message: DiscordInboundMessage): DiscordProcessed;
69
54
  }
70
55
  //#endregion
71
- //#region lib/engine/connectors/discord-listener.d.ts
56
+ //#region lib/engine/connectors/discord-flume-listener.d.ts
72
57
  type Deps = {
73
- config: DiscordConnectorConfig; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
74
- channelId?: string; /** Environment used to resolve a `botTokenEnv` reference. Defaults to process.env. */
58
+ config: DiscordConnectorConfig;
59
+ channelId?: string;
75
60
  env?: NodeJS.ProcessEnv;
76
- logger?: FunnelLogger; /** Diagnostic log of inbound events, before and after processing. No-op when absent. */
61
+ logger?: FunnelLogger;
77
62
  diagnosticLog?: ConnectorDiagnosticLog;
63
+ flumeDeps?: Partial<FlumeRuntimeDeps>; /** Shutdown signal forwarded to the underlying Flume. */
64
+ signal?: AbortSignal;
78
65
  };
79
- declare class FunnelDiscordListener extends FunnelConnectorListener {
66
+ /**
67
+ * Discord listener backed by `@interactive-inc/flume`'s `FlumeDiscordSource`
68
+ * (raw Gateway WebSocket + Zod). The processor layer
69
+ * (`FunnelDiscordEventProcessor`) is the application layer — self-skip,
70
+ * mention detection, meta shaping. Self-detection requires the bot's own
71
+ * user id, which Discord does not surface until READY; we read it from the
72
+ * READY payload on the first dispatch and build the processor then. Events
73
+ * seen before READY are impossible by protocol, so no event is lost.
74
+ */
75
+ declare class FunnelFlumeDiscordListener extends FunnelFlumeSourceListener {
80
76
  private readonly config;
81
- private readonly channelId;
82
77
  private readonly env;
83
- private readonly logger;
84
- private readonly diagnosticLog;
85
- private client;
78
+ private readonly flumeDeps;
79
+ private readonly signal;
80
+ private processor;
86
81
  constructor(deps: Deps);
87
82
  start(notify: NotifyFn): Promise<void>;
88
- stop(): Promise<void>;
89
- isAlive(): boolean;
90
- private recordRaw;
91
- private recordProcessed;
92
- private recordConnection;
83
+ protected onStop(): void;
84
+ private handleEvent;
85
+ private adoptReady;
86
+ private deliver;
93
87
  }
94
88
  //#endregion
95
- export { DiscordConnectorConfig, DiscordInboundMessage, DiscordProcessed, DiscordProcessedEmit, DiscordProcessedSkip, FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelDiscordListener, discordConnector, discordConnectorSchema };
89
+ export { DiscordConnectorConfig, DiscordInboundMessage, DiscordProcessed, DiscordProcessedEmit, DiscordProcessedSkip, FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelFlumeDiscordListener, discordConnector, discordConnectorSchema };
@@ -1,3 +1,3 @@
1
- import { t as discordConnectorSchema } from "../discord-connector-schema-B_N6IXLz.js";
2
- import { i as FunnelDiscordAdapter, n as FunnelDiscordListener, r as FunnelDiscordEventProcessor, t as discordConnector } from "../discord-connector-BL36yvbL.js";
3
- export { FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelDiscordListener, discordConnector, discordConnectorSchema };
1
+ import { t as discordConnectorSchema } from "../discord-connector-schema-B4YpWpR3.js";
2
+ import { i as FunnelDiscordAdapter, n as FunnelFlumeDiscordListener, r as FunnelDiscordEventProcessor, t as discordConnector } from "../discord-connector-DIFkYBbi.js";
3
+ export { FunnelDiscordAdapter, FunnelDiscordEventProcessor, FunnelFlumeDiscordListener, discordConnector, discordConnectorSchema };