@interactive-inc/claude-funnel 0.26.1 → 0.28.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 (27) hide show
  1. package/dist/bin.js +786 -717
  2. package/dist/connector-diagnostic-log-OPpPi9V9.d.ts +208 -0
  3. package/dist/connectors/discord.d.ts +16 -6
  4. package/dist/connectors/discord.js +1 -1
  5. package/dist/connectors/gh.d.ts +12 -5
  6. package/dist/connectors/gh.js +1 -1
  7. package/dist/connectors/schedule.d.ts +1 -1
  8. package/dist/connectors/schedule.js +1 -1
  9. package/dist/connectors/slack.d.ts +5 -4
  10. package/dist/connectors/slack.js +1 -1
  11. package/dist/{discord-connector-schema-Dww2I4zH.d.ts → discord-connector-schema-Df_McRJI.d.ts} +7 -1
  12. package/dist/{discord-connector-schema-CpuI6rmE.js → discord-connector-schema-RzDvrNE5.js} +81 -8
  13. package/dist/gateway/daemon.js +225 -225
  14. package/dist/{gh-connector-schema-CQRIvPpz.js → gh-connector-schema-eYE4g77K.js} +51 -3
  15. package/dist/index.d.ts +221 -39
  16. package/dist/index.js +781 -104
  17. package/dist/resolve-connector-token-Ch6XWMJM.js +22 -0
  18. package/dist/{schedule-connector-schema-CuCjP7z4.js → schedule-connector-schema-CM-sRkac.js} +53 -3
  19. package/dist/{schedule-listener-CBYF2bGZ.d.ts → schedule-listener-3M6WkH1Y.d.ts} +10 -3
  20. package/dist/{slack-connector-schema-BWL7dWlY.js → slack-connector-schema-CHbRJHGp.js} +140 -19
  21. package/dist/slack-listener-CLTiOEJw.d.ts +112 -0
  22. package/package.json +1 -1
  23. package/dist/logger-B3aXsVcX.d.ts +0 -33
  24. package/dist/slack-listener-DbNCPMqY.d.ts +0 -77
  25. /package/dist/{connector-adapter-CXB-q_XC.d.ts → connector-adapter-VA6undzc.d.ts} +0 -0
  26. /package/dist/{gh-connector-schema-Cmi57jvL.d.ts → gh-connector-schema-CQmEWzdV.d.ts} +0 -0
  27. /package/dist/{logger-D1A3_JXV.js → logger-Czli2OKh.js} +0 -0
@@ -0,0 +1,22 @@
1
+ //#region lib/connectors/resolve-connector-token.ts
2
+ /**
3
+ * Resolves a connector token from either a literal value or the name of an env
4
+ * var. A connector config carries one or the other per slot (see
5
+ * slack-connector-schema): literals are inlined into settings.json, references
6
+ * keep the secret in the environment (`.env.local`) and out of settings.json.
7
+ *
8
+ * Errors loudly when neither yields a value — a misconfigured connector should
9
+ * fail at listener start, not connect with an empty token and silently never
10
+ * receive events.
11
+ */
12
+ const resolveConnectorToken = (props) => {
13
+ if (props.literal !== void 0 && props.literal !== "") return props.literal;
14
+ if (props.envVar !== void 0 && props.envVar !== "") {
15
+ const fromEnv = props.env[props.envVar];
16
+ if (fromEnv !== void 0 && fromEnv !== "") return fromEnv;
17
+ throw new Error(`${props.label} references env var "${props.envVar}" but it is not set in the environment`);
18
+ }
19
+ throw new Error(`${props.label} has neither a literal token nor an env var reference`);
20
+ };
21
+ //#endregion
22
+ export { resolveConnectorToken as t };
@@ -1,4 +1,4 @@
1
- import { n as FunnelConnectorListener } from "./logger-D1A3_JXV.js";
1
+ import { n as FunnelConnectorListener } from "./logger-Czli2OKh.js";
2
2
  import { dirname } from "node:path";
3
3
  import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { z } from "zod";
@@ -157,7 +157,9 @@ const MAX_CATCHUP_MINUTES = 1440;
157
157
  var FunnelScheduleListener = class extends FunnelConnectorListener {
158
158
  config;
159
159
  lastFiredStore;
160
+ channelId;
160
161
  logger;
162
+ diagnosticLog;
161
163
  now;
162
164
  onFired;
163
165
  timer = null;
@@ -166,12 +168,15 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
166
168
  super();
167
169
  this.config = deps.config;
168
170
  this.lastFiredStore = deps.lastFiredStore;
171
+ this.channelId = deps.channelId ?? null;
169
172
  this.logger = deps.logger;
173
+ this.diagnosticLog = deps.diagnosticLog;
170
174
  this.now = deps.now ?? (() => /* @__PURE__ */ new Date());
171
175
  this.onFired = deps.onFired ?? null;
172
176
  }
173
177
  async start(notify) {
174
178
  this.stopped = false;
179
+ this.recordConnection("started", "");
175
180
  const scheduleNext = () => {
176
181
  if (this.stopped) return;
177
182
  const date = this.now();
@@ -192,6 +197,7 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
192
197
  clearTimeout(this.timer);
193
198
  this.timer = null;
194
199
  }
200
+ this.recordConnection("stopped", "");
195
201
  }
196
202
  isAlive() {
197
203
  return !this.stopped && this.timer !== null;
@@ -243,7 +249,15 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
243
249
  catchup_policy: entry.catchupPolicy
244
250
  };
245
251
  if (catchup) meta.catchup = "true";
246
- await notify(entry.prompt, meta);
252
+ const eventId = `${entry.id}@${firedAt.toISOString()}`;
253
+ this.recordRaw(eventId, entry, firedAt, catchup);
254
+ try {
255
+ await notify(entry.prompt, meta);
256
+ } catch (error) {
257
+ this.recordProcessed(eventId, entry, "emitted:delivery-failed");
258
+ throw error;
259
+ }
260
+ this.recordProcessed(eventId, entry, "emitted");
247
261
  if (this.onFired) try {
248
262
  await this.onFired(entry, firedAt);
249
263
  } catch (error) {
@@ -289,11 +303,13 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
289
303
  return matches;
290
304
  }
291
305
  logInvalidCron(entry, error) {
306
+ const message = error instanceof Error ? error.message : String(error);
307
+ this.recordConnection("error", `invalid cron "${entry.cron}" (entry ${entry.id}): ${message}`);
292
308
  this.logger?.error("invalid cron expression in schedule", {
293
309
  connector: this.config.name,
294
310
  id: entry.id,
295
311
  cron: entry.cron,
296
- error: error instanceof Error ? error.message : String(error)
312
+ error: message
297
313
  });
298
314
  }
299
315
  truncateToMinute(date) {
@@ -301,6 +317,40 @@ var FunnelScheduleListener = class extends FunnelConnectorListener {
301
317
  copy.setSeconds(0, 0);
302
318
  return copy;
303
319
  }
320
+ recordRaw(eventId, entry, firedAt, catchup) {
321
+ this.diagnosticLog?.recordRaw({
322
+ eventId,
323
+ type: "schedule",
324
+ connectorId: this.config.id,
325
+ channelId: this.channelId,
326
+ payload: JSON.stringify({
327
+ schedule_id: entry.id,
328
+ cron: entry.cron,
329
+ prompt: entry.prompt,
330
+ fired_at: firedAt.toISOString(),
331
+ catchup
332
+ })
333
+ });
334
+ }
335
+ recordProcessed(eventId, entry, outcome) {
336
+ this.diagnosticLog?.recordProcessed({
337
+ eventId,
338
+ type: "schedule",
339
+ connectorId: this.config.id,
340
+ channelId: this.channelId,
341
+ outcome,
342
+ payload: entry.prompt
343
+ });
344
+ }
345
+ recordConnection(status, detail) {
346
+ this.diagnosticLog?.recordConnection({
347
+ type: "schedule",
348
+ connectorId: this.config.id,
349
+ channelId: this.channelId,
350
+ status,
351
+ detail
352
+ });
353
+ }
304
354
  };
305
355
  //#endregion
306
356
  //#region lib/connectors/schedule-connector-schema.ts
@@ -1,4 +1,4 @@
1
- import { n as FunnelConnectorListener, r as NotifyFn, t as FunnelLogger } from "./logger-B3aXsVcX.js";
1
+ import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-OPpPi9V9.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region lib/connectors/schedule-connector-schema.d.ts
@@ -95,8 +95,10 @@ declare class ScheduleStateStore {
95
95
  type ScheduleOnFired = (entry: ScheduleEntry, firedAt: Date) => void | Promise<void>;
96
96
  type Deps = {
97
97
  config: ScheduleConnectorConfig;
98
- lastFiredStore: ScheduleStateStore;
99
- logger?: FunnelLogger;
98
+ lastFiredStore: ScheduleStateStore; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
99
+ channelId?: string;
100
+ logger?: FunnelLogger; /** Diagnostic log of fired entries and lifecycle. No-op when absent. */
101
+ diagnosticLog?: ConnectorDiagnosticLog;
100
102
  now?: () => Date;
101
103
  /**
102
104
  * Invoked after a schedule entry fires successfully. Use to remove one-shot
@@ -108,7 +110,9 @@ type Deps = {
108
110
  declare class FunnelScheduleListener extends FunnelConnectorListener {
109
111
  private readonly config;
110
112
  private readonly lastFiredStore;
113
+ private readonly channelId;
111
114
  private readonly logger;
115
+ private readonly diagnosticLog;
112
116
  private readonly now;
113
117
  private readonly onFired;
114
118
  private timer;
@@ -124,6 +128,9 @@ declare class FunnelScheduleListener extends FunnelConnectorListener {
124
128
  private findAllMatches;
125
129
  private logInvalidCron;
126
130
  private truncateToMinute;
131
+ private recordRaw;
132
+ private recordProcessed;
133
+ private recordConnection;
127
134
  }
128
135
  //#endregion
129
136
  export { FunnelFileSystem as a, ScheduleEntry as c, scheduleEntrySchema as d, FileStat as i, scheduleCatchupPolicySchema as l, ScheduleOnFired as n, ScheduleCatchupPolicy as o, ScheduleStateStore as r, ScheduleConnectorConfig as s, FunnelScheduleListener as t, scheduleConnectorSchema as u };
@@ -1,5 +1,6 @@
1
1
  import { t as FunnelConnectorAdapter } from "./connector-adapter-D5Utumgz.js";
2
- import { n as FunnelConnectorListener } from "./logger-D1A3_JXV.js";
2
+ import { t as resolveConnectorToken } from "./resolve-connector-token-Ch6XWMJM.js";
3
+ import { n as FunnelConnectorListener } from "./logger-Czli2OKh.js";
3
4
  import { z } from "zod";
4
5
  import { WebClient } from "@slack/web-api";
5
6
  import { App, LogLevel } from "@slack/bolt";
@@ -31,7 +32,13 @@ var FunnelSlackAdapter = class extends FunnelConnectorAdapter {
31
32
  client;
32
33
  constructor(deps) {
33
34
  super();
34
- this.client = deps.client ?? new WebClient(deps.config.botToken);
35
+ const botToken = resolveConnectorToken({
36
+ literal: deps.config.botToken,
37
+ envVar: deps.config.botTokenEnv,
38
+ env: deps.env ?? process.env,
39
+ label: `${deps.config.name}.botToken`
40
+ });
41
+ this.client = deps.client ?? new WebClient(botToken);
35
42
  Object.freeze(this);
36
43
  }
37
44
  async call(input) {
@@ -157,19 +164,34 @@ var FunnelSlackEventProcessor = class {
157
164
  }
158
165
  process(event) {
159
166
  const eventType = getString(event, "type");
160
- if (!eventType || !ALLOWED_EVENTS.has(eventType)) return { skip: true };
167
+ if (!eventType || !ALLOWED_EVENTS.has(eventType)) return {
168
+ skip: true,
169
+ reason: "skip:type"
170
+ };
161
171
  const subtype = getString(event, "subtype");
162
- if (!ALLOWED_SUBTYPES.has(subtype)) return { skip: true };
172
+ if (!ALLOWED_SUBTYPES.has(subtype)) return {
173
+ skip: true,
174
+ reason: "skip:subtype"
175
+ };
163
176
  const channelId = getString(event, "channel") ?? "";
164
177
  const dedupKey = `${channelId}:${getString(event, "event_ts") ?? getString(event, "ts") ?? ""}`;
165
178
  const now = this.now();
166
- if (this.dedup.has(dedupKey)) return { skip: true };
179
+ if (this.dedup.has(dedupKey)) return {
180
+ skip: true,
181
+ reason: "skip:dedup"
182
+ };
167
183
  this.dedup.set(dedupKey, now);
168
184
  for (const key of this.dedup.keys()) if ((this.dedup.get(key) ?? 0) < now - DEDUP_WINDOW) this.dedup.delete(key);
169
185
  const userId = getString(event, "user");
170
186
  const botId = getString(event, "bot_id");
171
- if (userId === this.ownBotUserId) return { skip: true };
172
- if (botId === this.ownBotId) return { skip: true };
187
+ if (userId === this.ownBotUserId) return {
188
+ skip: true,
189
+ reason: "skip:self-user"
190
+ };
191
+ if (botId === this.ownBotId) return {
192
+ skip: true,
193
+ reason: "skip:self-bot"
194
+ };
173
195
  const mentioned = (getString(event, "text") ?? "").includes(`<@${this.ownBotUserId}>`);
174
196
  const threadTs = getString(event, "thread_ts") ?? getString(event, "ts") ?? "";
175
197
  const emitted = this.minify ? minifySlackEvent(event) : event;
@@ -194,25 +216,49 @@ var FunnelSlackEventProcessor = class {
194
216
  const middlewareArgsSchema = z.object({ event: z.record(z.string(), z.unknown()).optional() });
195
217
  var FunnelSlackListener = class extends FunnelConnectorListener {
196
218
  config;
219
+ channelId;
220
+ env;
197
221
  logger;
222
+ diagnosticLog;
198
223
  onAppCreated;
199
224
  preprocessEvent;
200
225
  app = null;
201
226
  constructor(deps) {
202
227
  super();
203
228
  this.config = deps.config;
229
+ this.channelId = deps.channelId ?? null;
230
+ this.env = deps.env ?? process.env;
204
231
  this.logger = deps.logger;
232
+ this.diagnosticLog = deps.diagnosticLog;
205
233
  this.onAppCreated = deps.onAppCreated ?? null;
206
234
  this.preprocessEvent = deps.preprocessEvent ?? null;
207
235
  }
208
236
  async start(notify) {
237
+ this.recordConnection("started", "");
238
+ const botToken = resolveConnectorToken({
239
+ literal: this.config.botToken,
240
+ envVar: this.config.botTokenEnv,
241
+ env: this.env,
242
+ label: `${this.config.name}.botToken`
243
+ });
209
244
  const app = new App({
210
- token: this.config.botToken,
211
- appToken: this.config.appToken,
245
+ token: botToken,
246
+ appToken: resolveConnectorToken({
247
+ literal: this.config.appToken,
248
+ envVar: this.config.appTokenEnv,
249
+ env: this.env,
250
+ label: `${this.config.name}.appToken`
251
+ }),
212
252
  socketMode: true,
213
253
  logLevel: LogLevel.ERROR
214
254
  });
215
- const authResult = await app.client.auth.test({ token: this.config.botToken });
255
+ let authResult;
256
+ try {
257
+ authResult = await app.client.auth.test({ token: botToken });
258
+ } catch (error) {
259
+ this.recordConnection("auth-failed", messageOf(error));
260
+ throw error;
261
+ }
216
262
  const processor = new FunnelSlackEventProcessor({
217
263
  ownBotUserId: authResult.user_id ?? "",
218
264
  ownBotId: authResult.bot_id ?? "",
@@ -226,14 +272,28 @@ var FunnelSlackListener = class extends FunnelConnectorListener {
226
272
  return;
227
273
  }
228
274
  const rawEvent = parsed.data.event;
275
+ const eventId = crypto.randomUUID();
276
+ this.recordRaw(eventId, rawEvent);
229
277
  const event = preprocess ? preprocess(rawEvent) : rawEvent;
230
- if (event === null) return;
278
+ if (event === null) {
279
+ this.recordProcessed(eventId, rawEvent, "skip:preprocess", "");
280
+ return;
281
+ }
231
282
  const result = processor.process(event);
232
- if (result.skip) return;
233
- await notify(result.content, result.meta);
283
+ if (result.skip) {
284
+ this.recordProcessed(eventId, event, result.reason, "");
285
+ return;
286
+ }
287
+ try {
288
+ await notify(result.content, result.meta);
289
+ } catch (error) {
290
+ this.recordProcessed(eventId, event, "emitted:delivery-failed", result.content);
291
+ throw error;
292
+ }
293
+ this.recordProcessed(eventId, event, "emitted", result.content);
234
294
  if (result.shouldReact) try {
235
295
  await app.client.reactions.add({
236
- token: this.config.botToken,
296
+ token: botToken,
237
297
  channel: result.channel,
238
298
  timestamp: result.timestamp,
239
299
  name: "eyes"
@@ -241,34 +301,95 @@ var FunnelSlackListener = class extends FunnelConnectorListener {
241
301
  } catch {}
242
302
  });
243
303
  app.error(async (error) => {
244
- this.logger?.error("Slack error", { error: error instanceof Error ? error.message : String(error) });
304
+ const message = messageOf(error);
305
+ this.recordConnection("error", message);
306
+ this.logger?.error("Slack error", { error: message });
245
307
  });
246
308
  if (this.onAppCreated) await this.onAppCreated(app);
247
- await app.start();
309
+ try {
310
+ await app.start();
311
+ } catch (error) {
312
+ this.recordConnection("error", messageOf(error));
313
+ throw error;
314
+ }
248
315
  this.app = app;
316
+ this.recordConnection("connected", "");
249
317
  }
250
318
  async stop() {
251
319
  if (!this.app) return;
252
320
  try {
253
321
  await this.app.stop();
322
+ this.recordConnection("disconnected", "");
254
323
  } catch (error) {
255
- this.logger?.error("Slack stop error", { error: error instanceof Error ? error.message : String(error) });
324
+ this.recordConnection("error", messageOf(error));
325
+ this.logger?.error("Slack stop error", { error: messageOf(error) });
256
326
  } finally {
257
327
  this.app = null;
328
+ this.recordConnection("stopped", "");
258
329
  }
259
330
  }
260
331
  isAlive() {
261
332
  return this.app !== null;
262
333
  }
334
+ recordRaw(eventId, event) {
335
+ this.diagnosticLog?.recordRaw({
336
+ eventId,
337
+ type: "slack",
338
+ connectorId: this.config.id,
339
+ channelId: this.channelId,
340
+ payload: JSON.stringify(event)
341
+ });
342
+ }
343
+ recordProcessed(eventId, event, outcome, content) {
344
+ this.diagnosticLog?.recordProcessed({
345
+ eventId,
346
+ type: "slack",
347
+ connectorId: this.config.id,
348
+ channelId: this.channelId,
349
+ outcome,
350
+ payload: content || JSON.stringify(event)
351
+ });
352
+ }
353
+ recordConnection(status, detail) {
354
+ this.diagnosticLog?.recordConnection({
355
+ type: "slack",
356
+ connectorId: this.config.id,
357
+ channelId: this.channelId,
358
+ status,
359
+ detail
360
+ });
361
+ }
362
+ };
363
+ const messageOf = (error) => {
364
+ return error instanceof Error ? error.message : String(error);
263
365
  };
264
366
  //#endregion
265
367
  //#region lib/connectors/slack-connector-schema.ts
368
+ /**
369
+ * A slack connector resolves its tokens one of two ways, set at sync time:
370
+ *
371
+ * - literal: `botToken` / `appToken` hold the real `xoxb-`/`xapp-` secret
372
+ * (funnel.json gave a literal, or a `fnl channels` command did).
373
+ * - by reference: `botTokenEnv` / `appTokenEnv` hold the *name* of an env var
374
+ * (funnel.json used `env: { botToken: "SLACK_BOT_TOKEN" }`). The secret
375
+ * never lands in settings.json; the listener resolves it from the
376
+ * environment at start. This keeps repo-local launches' tokens in
377
+ * `.env.local` only.
378
+ *
379
+ * Both are optional at the schema level (a discriminated-union member can't
380
+ * carry a cross-field refine); the listener requires exactly one resolved
381
+ * token per slot and errors loudly otherwise.
382
+ */
266
383
  const slackConnectorSchema = z.object({
267
384
  id: z.string(),
268
385
  name: z.string(),
269
386
  type: z.literal("slack"),
270
- botToken: z.string().startsWith("xoxb-"),
271
- appToken: z.string().startsWith("xapp-"),
387
+ botToken: z.string().startsWith("xoxb-").optional(),
388
+ appToken: z.string().startsWith("xapp-").optional(),
389
+ /** Name of the env var holding the bot token, resolved at listener start. */
390
+ botTokenEnv: z.string().optional(),
391
+ /** Name of the env var holding the app token, resolved at listener start. */
392
+ appTokenEnv: z.string().optional(),
272
393
  minify: z.boolean().default(true),
273
394
  createdAt: z.string().datetime().optional(),
274
395
  updatedAt: z.string().datetime().optional()
@@ -0,0 +1,112 @@
1
+ import { S as NotifyFn, b as FunnelLogger, o as ConnectorDiagnosticLog, x as FunnelConnectorListener } from "./connector-diagnostic-log-OPpPi9V9.js";
2
+ import { z } from "zod";
3
+ import { App } from "@slack/bolt";
4
+
5
+ //#region lib/connectors/slack-connector-schema.d.ts
6
+ /**
7
+ * A slack connector resolves its tokens one of two ways, set at sync time:
8
+ *
9
+ * - literal: `botToken` / `appToken` hold the real `xoxb-`/`xapp-` secret
10
+ * (funnel.json gave a literal, or a `fnl channels` command did).
11
+ * - by reference: `botTokenEnv` / `appTokenEnv` hold the *name* of an env var
12
+ * (funnel.json used `env: { botToken: "SLACK_BOT_TOKEN" }`). The secret
13
+ * never lands in settings.json; the listener resolves it from the
14
+ * environment at start. This keeps repo-local launches' tokens in
15
+ * `.env.local` only.
16
+ *
17
+ * Both are optional at the schema level (a discriminated-union member can't
18
+ * carry a cross-field refine); the listener requires exactly one resolved
19
+ * token per slot and errors loudly otherwise.
20
+ */
21
+ declare const slackConnectorSchema: z.ZodObject<{
22
+ id: z.ZodString;
23
+ name: z.ZodString;
24
+ type: z.ZodLiteral<"slack">;
25
+ botToken: z.ZodOptional<z.ZodString>;
26
+ appToken: z.ZodOptional<z.ZodString>;
27
+ botTokenEnv: z.ZodOptional<z.ZodString>;
28
+ appTokenEnv: z.ZodOptional<z.ZodString>;
29
+ minify: z.ZodDefault<z.ZodBoolean>;
30
+ createdAt: z.ZodOptional<z.ZodString>;
31
+ updatedAt: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strip>;
33
+ type SlackConnectorConfig = z.infer<typeof slackConnectorSchema>;
34
+ //#endregion
35
+ //#region lib/connectors/slack-event-processor.d.ts
36
+ type SlackRawEvent = Record<string, unknown>;
37
+ /**
38
+ * Why the processor dropped an event. Mirrored verbatim into the diagnostic
39
+ * log's processed `outcome` column so "Slack delivered it but no notification arrived" is
40
+ * traceable to the exact gate that dropped it. The listener may additionally
41
+ * record `skip:preprocess` for events a host preprocessor dropped before the
42
+ * processor ran — that gate is outside this type.
43
+ */
44
+ type SlackSkipReason = "skip:type" | "skip:subtype" | "skip:dedup" | "skip:self-user" | "skip:self-bot";
45
+ type SlackProcessedSkip = {
46
+ skip: true;
47
+ reason: SlackSkipReason;
48
+ };
49
+ type SlackProcessedEmit = {
50
+ skip: false;
51
+ content: string;
52
+ meta: Record<string, string>;
53
+ shouldReact: boolean;
54
+ channel: string;
55
+ timestamp: string;
56
+ };
57
+ type SlackProcessed = SlackProcessedSkip | SlackProcessedEmit;
58
+ type Props = {
59
+ ownBotUserId: string;
60
+ ownBotId: string;
61
+ minify?: boolean;
62
+ now?: () => number;
63
+ };
64
+ declare class FunnelSlackEventProcessor {
65
+ private readonly ownBotUserId;
66
+ private readonly ownBotId;
67
+ private readonly minify;
68
+ private readonly now;
69
+ private readonly dedup;
70
+ constructor(props: Props);
71
+ process(event: SlackRawEvent): SlackProcessed;
72
+ }
73
+ //#endregion
74
+ //#region lib/connectors/slack-listener.d.ts
75
+ type SlackOnAppCreated = (app: App) => void | Promise<void>;
76
+ type SlackPreprocessEvent = (event: SlackRawEvent) => SlackRawEvent | null;
77
+ type Deps = {
78
+ config: SlackConnectorConfig; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
79
+ channelId?: string; /** Environment used to resolve `botTokenEnv`/`appTokenEnv` references. Defaults to process.env. */
80
+ env?: NodeJS.ProcessEnv;
81
+ logger?: FunnelLogger; /** Diagnostic log of inbound events, before and after processing. No-op when absent. */
82
+ diagnosticLog?: ConnectorDiagnosticLog;
83
+ /**
84
+ * Invoked after the Bolt App is constructed, before it starts.
85
+ * Use to attach app.action handlers, custom middleware, etc.
86
+ */
87
+ onAppCreated?: SlackOnAppCreated;
88
+ /**
89
+ * Transform or drop the raw Slack event before the built-in processor sees it.
90
+ * Return null to drop the event entirely.
91
+ */
92
+ preprocessEvent?: SlackPreprocessEvent;
93
+ };
94
+ declare class FunnelSlackListener extends FunnelConnectorListener {
95
+ private readonly config;
96
+ private readonly channelId;
97
+ private readonly env;
98
+ private readonly logger;
99
+ private readonly diagnosticLog;
100
+ private readonly onAppCreated;
101
+ private readonly preprocessEvent;
102
+ private app;
103
+ constructor(deps: Deps);
104
+ start(notify: NotifyFn): Promise<void>;
105
+ stop(): Promise<void>;
106
+ isAlive(): boolean;
107
+ private recordRaw;
108
+ private recordProcessed;
109
+ private recordConnection;
110
+ }
111
+ //#endregion
112
+ export { SlackProcessed as a, SlackRawEvent as c, slackConnectorSchema as d, FunnelSlackEventProcessor as i, SlackSkipReason as l, SlackOnAppCreated as n, SlackProcessedEmit as o, SlackPreprocessEvent as r, SlackProcessedSkip as s, FunnelSlackListener as t, SlackConnectorConfig as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.26.1",
3
+ "version": "0.28.0",
4
4
  "description": "Hub CLI that routes external events (Slack / GitHub / Discord) to Claude Code agents through subscription channels over MCP.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,33 +0,0 @@
1
- //#region lib/connectors/connector-listener.d.ts
2
- type NotifyFn = (content: string, meta?: Record<string, string>) => Promise<void>;
3
- /**
4
- * Long-lived event source for one connector.
5
- *
6
- * `start()` opens the underlying connection (Slack Socket Mode, Discord
7
- * Gateway, GH polling, schedule tick) and pushes events through `notify`.
8
- * `stop()` releases the resources so the supervisor can recreate the listener
9
- * with new config without restarting the whole gateway. `isAlive()` lets the
10
- * supervisor periodically health-check and auto-restart dead listeners; the
11
- * default optimistic implementation is fine for poll/tick-based listeners
12
- * that self-heal.
13
- */
14
- declare abstract class FunnelConnectorListener {
15
- abstract start(notify: NotifyFn): Promise<void>;
16
- abstract stop(): Promise<void>;
17
- isAlive(): boolean;
18
- }
19
- //#endregion
20
- //#region lib/engine/logger/logger.d.ts
21
- /**
22
- * Structured logger with three levels and an optional log-file path.
23
- * Defaults to NodeFunnelLogger (appends to `<os.tmpdir()>/funnel/funnel.log`);
24
- * MemoryFunnelLogger captures entries in memory and NoopFunnelLogger silences output.
25
- */
26
- declare abstract class FunnelLogger {
27
- abstract info(message: string, meta?: Record<string, unknown>): void;
28
- abstract warn(message: string, meta?: Record<string, unknown>): void;
29
- abstract error(message: string, meta?: Record<string, unknown>): void;
30
- abstract readonly file: string | null;
31
- }
32
- //#endregion
33
- export { FunnelConnectorListener as n, NotifyFn as r, FunnelLogger as t };
@@ -1,77 +0,0 @@
1
- import { n as FunnelConnectorListener, r as NotifyFn, t as FunnelLogger } from "./logger-B3aXsVcX.js";
2
- import { z } from "zod";
3
- import { App } from "@slack/bolt";
4
-
5
- //#region lib/connectors/slack-connector-schema.d.ts
6
- declare const slackConnectorSchema: z.ZodObject<{
7
- id: z.ZodString;
8
- name: z.ZodString;
9
- type: z.ZodLiteral<"slack">;
10
- botToken: z.ZodString;
11
- appToken: z.ZodString;
12
- minify: z.ZodDefault<z.ZodBoolean>;
13
- createdAt: z.ZodOptional<z.ZodString>;
14
- updatedAt: z.ZodOptional<z.ZodString>;
15
- }, z.core.$strip>;
16
- type SlackConnectorConfig = z.infer<typeof slackConnectorSchema>;
17
- //#endregion
18
- //#region lib/connectors/slack-event-processor.d.ts
19
- type SlackRawEvent = Record<string, unknown>;
20
- type SlackProcessedSkip = {
21
- skip: true;
22
- };
23
- type SlackProcessedEmit = {
24
- skip: false;
25
- content: string;
26
- meta: Record<string, string>;
27
- shouldReact: boolean;
28
- channel: string;
29
- timestamp: string;
30
- };
31
- type SlackProcessed = SlackProcessedSkip | SlackProcessedEmit;
32
- type Props = {
33
- ownBotUserId: string;
34
- ownBotId: string;
35
- minify?: boolean;
36
- now?: () => number;
37
- };
38
- declare class FunnelSlackEventProcessor {
39
- private readonly ownBotUserId;
40
- private readonly ownBotId;
41
- private readonly minify;
42
- private readonly now;
43
- private readonly dedup;
44
- constructor(props: Props);
45
- process(event: SlackRawEvent): SlackProcessed;
46
- }
47
- //#endregion
48
- //#region lib/connectors/slack-listener.d.ts
49
- type SlackOnAppCreated = (app: App) => void | Promise<void>;
50
- type SlackPreprocessEvent = (event: SlackRawEvent) => SlackRawEvent | null;
51
- type Deps = {
52
- config: SlackConnectorConfig;
53
- logger?: FunnelLogger;
54
- /**
55
- * Invoked after the Bolt App is constructed, before it starts.
56
- * Use to attach app.action handlers, custom middleware, etc.
57
- */
58
- onAppCreated?: SlackOnAppCreated;
59
- /**
60
- * Transform or drop the raw Slack event before the built-in processor sees it.
61
- * Return null to drop the event entirely.
62
- */
63
- preprocessEvent?: SlackPreprocessEvent;
64
- };
65
- declare class FunnelSlackListener extends FunnelConnectorListener {
66
- private readonly config;
67
- private readonly logger;
68
- private readonly onAppCreated;
69
- private readonly preprocessEvent;
70
- private app;
71
- constructor(deps: Deps);
72
- start(notify: NotifyFn): Promise<void>;
73
- stop(): Promise<void>;
74
- isAlive(): boolean;
75
- }
76
- //#endregion
77
- export { SlackProcessed as a, SlackRawEvent as c, FunnelSlackEventProcessor as i, SlackConnectorConfig as l, SlackOnAppCreated as n, SlackProcessedEmit as o, SlackPreprocessEvent as r, SlackProcessedSkip as s, FunnelSlackListener as t, slackConnectorSchema as u };
File without changes