@interactive-inc/claude-funnel 0.23.0 → 0.24.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.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "./disc
3
3
  import { n as FunnelConnectorListener, r as NotifyFn, t as FunnelLogger } from "./logger-B3aXsVcX.js";
4
4
  import { a as FunnelProcessRunner, c as RunResult, i as DetachOptions, n as ghConnectorSchema, o as ProcessSnapshot, r as AttachOptions, s as RunOptions, t as GhConnectorConfig } from "./gh-connector-schema-Cmi57jvL.js";
5
5
  import { a as FunnelFileSystem, c as ScheduleEntry, d as scheduleEntrySchema, i as FileStat, l as scheduleCatchupPolicySchema, n as ScheduleOnFired, o as ScheduleCatchupPolicy, s as ScheduleConnectorConfig, u as scheduleConnectorSchema } from "./schedule-listener-CBYF2bGZ.js";
6
- import { a as SlackProcessed, c as SlackRawEvent, i as FunnelSlackEventProcessor, l as SlackConnectorConfig, n as SlackOnAppCreated, o as SlackProcessedEmit, r as SlackPreprocessEvent, s as SlackProcessedSkip, u as slackConnectorSchema } from "./slack-listener-tQH7cXU7.js";
6
+ import { a as SlackProcessed, c as SlackRawEvent, i as FunnelSlackEventProcessor, l as SlackConnectorConfig, n as SlackOnAppCreated, o as SlackProcessedEmit, r as SlackPreprocessEvent, s as SlackProcessedSkip, u as slackConnectorSchema } from "./slack-listener-DbNCPMqY.js";
7
7
  import { z } from "zod";
8
8
  import * as _$hono_factory0 from "hono/factory";
9
9
  import { Hono } from "hono";
@@ -18,6 +18,7 @@ declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
18
18
  type: z.ZodLiteral<"slack">;
19
19
  botToken: z.ZodString;
20
20
  appToken: z.ZodString;
21
+ minify: z.ZodDefault<z.ZodBoolean>;
21
22
  createdAt: z.ZodOptional<z.ZodString>;
22
23
  updatedAt: z.ZodOptional<z.ZodString>;
23
24
  }, z.core.$strip>, z.ZodObject<{
@@ -159,6 +160,7 @@ declare const channelConfigSchema: z.ZodObject<{
159
160
  type: z.ZodLiteral<"slack">;
160
161
  botToken: z.ZodString;
161
162
  appToken: z.ZodString;
163
+ minify: z.ZodDefault<z.ZodBoolean>;
162
164
  createdAt: z.ZodOptional<z.ZodString>;
163
165
  updatedAt: z.ZodOptional<z.ZodString>;
164
166
  }, z.core.$strip>, z.ZodObject<{
@@ -220,6 +222,7 @@ declare const settingsSchema: z.ZodObject<{
220
222
  type: z.ZodLiteral<"slack">;
221
223
  botToken: z.ZodString;
222
224
  appToken: z.ZodString;
225
+ minify: z.ZodDefault<z.ZodBoolean>;
223
226
  createdAt: z.ZodOptional<z.ZodString>;
224
227
  updatedAt: z.ZodOptional<z.ZodString>;
225
228
  }, z.core.$strip>, z.ZodObject<{
@@ -286,6 +289,7 @@ type AddConnectorInput = {
286
289
  name: string;
287
290
  botToken: string;
288
291
  appToken: string;
292
+ minify?: boolean;
289
293
  } | {
290
294
  type: "gh";
291
295
  name: string;
@@ -532,6 +536,7 @@ declare const connectorSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
532
536
  name: z.ZodString;
533
537
  botToken: z.ZodOptional<z.ZodString>;
534
538
  appToken: z.ZodOptional<z.ZodString>;
539
+ minify: z.ZodOptional<z.ZodBoolean>;
535
540
  env: z.ZodOptional<z.ZodObject<{
536
541
  botToken: z.ZodOptional<z.ZodString>;
537
542
  appToken: z.ZodOptional<z.ZodString>;
@@ -562,6 +567,7 @@ declare const channelSpecSchema: z.ZodObject<{
562
567
  name: z.ZodString;
563
568
  botToken: z.ZodOptional<z.ZodString>;
564
569
  appToken: z.ZodOptional<z.ZodString>;
570
+ minify: z.ZodOptional<z.ZodBoolean>;
565
571
  env: z.ZodOptional<z.ZodObject<{
566
572
  botToken: z.ZodOptional<z.ZodString>;
567
573
  appToken: z.ZodOptional<z.ZodString>;
@@ -595,6 +601,7 @@ declare const localConfigSchema: z.ZodObject<{
595
601
  name: z.ZodString;
596
602
  botToken: z.ZodOptional<z.ZodString>;
597
603
  appToken: z.ZodOptional<z.ZodString>;
604
+ minify: z.ZodOptional<z.ZodBoolean>;
598
605
  env: z.ZodOptional<z.ZodObject<{
599
606
  botToken: z.ZodOptional<z.ZodString>;
600
607
  appToken: z.ZodOptional<z.ZodString>;
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { i as FunnelDiscordAdapter, n as FunnelDiscordListener, t as discordConn
2
2
  import { i as FunnelConnectorListener, n as funnelTmpDir, r as FunnelLogger, t as NodeFunnelLogger } from "./node-logger-B97ZiGwj.js";
3
3
  import { a as FunnelProcessRunner, i as NodeFunnelProcessRunner, n as FunnelGhListener, r as FunnelGhAdapter, t as ghConnectorSchema } from "./gh-connector-schema-CAC24s0r.js";
4
4
  import { a as ScheduleStateStore, i as FunnelScheduleListener, n as scheduleConnectorSchema, o as NodeFunnelFileSystem, r as scheduleEntrySchema, s as FunnelFileSystem, t as scheduleCatchupPolicySchema } from "./schedule-connector-schema-BZpH6ZmR.js";
5
- import { i as FunnelSlackAdapter, n as FunnelSlackListener, r as FunnelSlackEventProcessor, t as slackConnectorSchema } from "./slack-connector-schema-B3jr-RTH.js";
5
+ import { i as FunnelSlackAdapter, n as FunnelSlackListener, r as FunnelSlackEventProcessor, t as slackConnectorSchema } from "./slack-connector-schema-B0NyhxqQ.js";
6
6
  import { dirname, join, resolve } from "node:path";
7
7
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
8
8
  import { homedir } from "node:os";
@@ -401,6 +401,7 @@ var FunnelChannels = class {
401
401
  name: input.name,
402
402
  botToken: input.botToken,
403
403
  appToken: input.appToken,
404
+ minify: input.minify ?? true,
404
405
  createdAt,
405
406
  updatedAt
406
407
  };
@@ -800,6 +801,8 @@ const slackConnectorSpecSchema = z.object({
800
801
  name: z.string(),
801
802
  botToken: z.string().optional(),
802
803
  appToken: z.string().optional(),
804
+ /** Shrink raw Slack events before fanout. Defaults to true. */
805
+ minify: z.boolean().optional(),
803
806
  env: slackEnvSchema
804
807
  });
805
808
  const discordEnvSchema = z.object({ botToken: z.string().optional() }).optional();
@@ -1065,7 +1068,8 @@ var FunnelLocalConfigSync = class {
1065
1068
  type: "slack",
1066
1069
  name: spec.name,
1067
1070
  botToken,
1068
- appToken
1071
+ appToken,
1072
+ ...spec.minify !== void 0 ? { minify: spec.minify } : {}
1069
1073
  }).id,
1070
1074
  name: spec.name,
1071
1075
  changed: true
@@ -46,6 +46,90 @@ var FunnelSlackAdapter = class extends FunnelConnectorAdapter {
46
46
  }
47
47
  };
48
48
  //#endregion
49
+ //#region lib/connectors/minify-slack-event.ts
50
+ const TOP_LEVEL_KEYS = [
51
+ "type",
52
+ "subtype",
53
+ "user",
54
+ "bot_id",
55
+ "text",
56
+ "ts",
57
+ "thread_ts",
58
+ "channel",
59
+ "channel_type",
60
+ "files",
61
+ "attachments"
62
+ ];
63
+ const FILE_KEYS = [
64
+ "id",
65
+ "name",
66
+ "mimetype",
67
+ "filetype",
68
+ "size",
69
+ "url_private",
70
+ "permalink"
71
+ ];
72
+ const ATTACHMENT_KEYS = [
73
+ "title",
74
+ "text",
75
+ "fallback"
76
+ ];
77
+ const isRecord = (value) => {
78
+ return typeof value === "object" && value !== null && !Array.isArray(value);
79
+ };
80
+ const pickDefined = (source, keys) => {
81
+ const picked = {};
82
+ for (const key of keys) if (source[key] !== void 0) picked[key] = source[key];
83
+ return picked;
84
+ };
85
+ const hasThumbOrPreviewKey = (file) => {
86
+ return Object.keys(file).some((key) => key.startsWith("thumb") || key.startsWith("preview"));
87
+ };
88
+ const minifyFile = (file) => {
89
+ if (!isRecord(file)) return file;
90
+ const minified = pickDefined(file, FILE_KEYS);
91
+ if (hasThumbOrPreviewKey(file)) minified._funnel_omitted = ["thumb_*"];
92
+ return minified;
93
+ };
94
+ const flattenRichText = (node) => {
95
+ if (!isRecord(node)) return "";
96
+ const text = node.text;
97
+ if (typeof text === "string") return text;
98
+ const elements = node.elements;
99
+ if (!Array.isArray(elements)) return "";
100
+ return elements.map(flattenRichText).join("");
101
+ };
102
+ const flattenTableRow = (row) => {
103
+ if (!Array.isArray(row)) return "";
104
+ return row.map(flattenRichText).join(" ");
105
+ };
106
+ const flattenBlock = (block) => {
107
+ if (!isRecord(block)) return "";
108
+ if (block.type === "table" && Array.isArray(block.rows)) return block.rows.map(flattenTableRow).join("\n");
109
+ return flattenRichText(block);
110
+ };
111
+ const flattenBlocks = (blocks) => {
112
+ return blocks.map(flattenBlock).filter((line) => line.length > 0).join("\n");
113
+ };
114
+ const minifyAttachment = (attachment) => {
115
+ if (!isRecord(attachment)) return attachment;
116
+ const minified = pickDefined(attachment, ATTACHMENT_KEYS);
117
+ const blocks = attachment.blocks;
118
+ if (Array.isArray(blocks)) {
119
+ const flattened = flattenBlocks(blocks);
120
+ const existingText = typeof minified.text === "string" ? minified.text : "";
121
+ minified.text = existingText ? `${existingText}\n${flattened}` : flattened;
122
+ minified._funnel_omitted = ["blocks"];
123
+ }
124
+ return minified;
125
+ };
126
+ const minifySlackEvent = (event) => {
127
+ const minified = pickDefined(event, TOP_LEVEL_KEYS);
128
+ if (Array.isArray(minified.files)) minified.files = minified.files.map(minifyFile);
129
+ if (Array.isArray(minified.attachments)) minified.attachments = minified.attachments.map(minifyAttachment);
130
+ return minified;
131
+ };
132
+ //#endregion
49
133
  //#region lib/connectors/slack-event-processor.ts
50
134
  const ALLOWED_EVENTS = new Set(["message", "app_mention"]);
51
135
  const ALLOWED_SUBTYPES = new Set([
@@ -62,11 +146,13 @@ const getString = (event, key) => {
62
146
  var FunnelSlackEventProcessor = class {
63
147
  ownBotUserId;
64
148
  ownBotId;
149
+ minify;
65
150
  now;
66
151
  dedup = /* @__PURE__ */ new Map();
67
152
  constructor(props) {
68
153
  this.ownBotUserId = props.ownBotUserId;
69
154
  this.ownBotId = props.ownBotId;
155
+ this.minify = props.minify ?? true;
70
156
  this.now = props.now ?? (() => Date.now());
71
157
  }
72
158
  process(event) {
@@ -86,9 +172,10 @@ var FunnelSlackEventProcessor = class {
86
172
  if (botId === this.ownBotId) return { skip: true };
87
173
  const mentioned = (getString(event, "text") ?? "").includes(`<@${this.ownBotUserId}>`);
88
174
  const threadTs = getString(event, "thread_ts") ?? getString(event, "ts") ?? "";
175
+ const emitted = this.minify ? minifySlackEvent(event) : event;
89
176
  return {
90
177
  skip: false,
91
- content: JSON.stringify(event),
178
+ content: JSON.stringify(emitted),
92
179
  meta: {
93
180
  event_type: "slack",
94
181
  channel_id: channelId,
@@ -129,7 +216,8 @@ var FunnelSlackListener = class extends FunnelConnectorListener {
129
216
  const authResult = await app.client.auth.test({ token: this.config.botToken });
130
217
  const processor = new FunnelSlackEventProcessor({
131
218
  ownBotUserId: authResult.user_id ?? "",
132
- ownBotId: authResult.bot_id ?? ""
219
+ ownBotId: authResult.bot_id ?? "",
220
+ minify: this.config.minify
133
221
  });
134
222
  const preprocess = this.preprocessEvent;
135
223
  app.use(async (args) => {
@@ -140,6 +228,7 @@ var FunnelSlackListener = class extends FunnelConnectorListener {
140
228
  if (event === null) return;
141
229
  const result = processor.process(event);
142
230
  if (result.skip) return;
231
+ await notify(result.content, result.meta);
143
232
  if (result.shouldReact) try {
144
233
  await app.client.reactions.add({
145
234
  token: this.config.botToken,
@@ -148,7 +237,6 @@ var FunnelSlackListener = class extends FunnelConnectorListener {
148
237
  name: "eyes"
149
238
  });
150
239
  } catch {}
151
- await notify(result.content, result.meta);
152
240
  });
153
241
  app.error(async (error) => {
154
242
  this.logger.error("Slack error", { error: error instanceof Error ? error.message : String(error) });
@@ -179,6 +267,7 @@ const slackConnectorSchema = z.object({
179
267
  type: z.literal("slack"),
180
268
  botToken: z.string().startsWith("xoxb-"),
181
269
  appToken: z.string().startsWith("xapp-"),
270
+ minify: z.boolean().default(true),
182
271
  createdAt: z.string().datetime().optional(),
183
272
  updatedAt: z.string().datetime().optional()
184
273
  });
@@ -9,6 +9,7 @@ declare const slackConnectorSchema: z.ZodObject<{
9
9
  type: z.ZodLiteral<"slack">;
10
10
  botToken: z.ZodString;
11
11
  appToken: z.ZodString;
12
+ minify: z.ZodDefault<z.ZodBoolean>;
12
13
  createdAt: z.ZodOptional<z.ZodString>;
13
14
  updatedAt: z.ZodOptional<z.ZodString>;
14
15
  }, z.core.$strip>;
@@ -31,11 +32,13 @@ type SlackProcessed = SlackProcessedSkip | SlackProcessedEmit;
31
32
  type Props = {
32
33
  ownBotUserId: string;
33
34
  ownBotId: string;
35
+ minify?: boolean;
34
36
  now?: () => number;
35
37
  };
36
38
  declare class FunnelSlackEventProcessor {
37
39
  private readonly ownBotUserId;
38
40
  private readonly ownBotId;
41
+ private readonly minify;
39
42
  private readonly now;
40
43
  private readonly dedup;
41
44
  constructor(props: Props);
@@ -52,6 +52,9 @@
52
52
  "appToken": {
53
53
  "type": "string"
54
54
  },
55
+ "minify": {
56
+ "type": "boolean"
57
+ },
55
58
  "env": {
56
59
  "type": "object",
57
60
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.23.0",
3
+ "version": "0.24.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",