@sentry/junior 0.1.0 → 0.1.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.
@@ -1,39 +1,35 @@
1
1
  import {
2
2
  GEN_AI_PROVIDER_NAME,
3
+ addReactionToMessage,
4
+ botConfig,
3
5
  buildSlackOutputMessage,
6
+ claimQueueIngressDedup,
4
7
  completeObject,
5
8
  completeText,
9
+ downloadPrivateSlackFile,
6
10
  ensureBlockSpacing,
7
11
  escapeXml,
8
12
  generateAssistantReply,
9
13
  getOAuthProviderConfig,
14
+ getSlackBotToken,
15
+ getSlackClient,
16
+ getSlackClientId,
17
+ getSlackClientSecret,
18
+ getSlackSigningSecret,
19
+ getStateAdapter,
10
20
  getUserTokenStore,
21
+ hasQueueIngressDedup,
22
+ isDmChannel,
11
23
  isExplicitChannelPostIntent,
12
24
  isPluginProvider,
13
25
  isRetryableTurnError,
26
+ listThreadReplies,
14
27
  publishAppHomeView,
28
+ removeReactionFromMessage,
15
29
  shouldEmitDevAgentTrace,
16
30
  startOAuthFlow,
17
31
  truncateStatusText
18
- } from "./chunk-7E56WM6K.js";
19
- import {
20
- claimQueueIngressDedup,
21
- getStateAdapter,
22
- hasQueueIngressDedup
23
- } from "./chunk-ZBFSIN6G.js";
24
- import {
25
- listThreadReplies
26
- } from "./chunk-MM3YNA4F.js";
27
- import {
28
- botConfig,
29
- downloadPrivateSlackFile,
30
- getSlackBotToken,
31
- getSlackClient,
32
- getSlackClientId,
33
- getSlackClientSecret,
34
- getSlackSigningSecret,
35
- isDmChannel
36
- } from "./chunk-GDNDYMGX.js";
32
+ } from "./chunk-5LLCJPTH.js";
37
33
  import {
38
34
  logError,
39
35
  logException,
@@ -229,6 +225,40 @@ function buildConversationStatePatch(conversation) {
229
225
  };
230
226
  }
231
227
 
228
+ // src/chat/queue/client.ts
229
+ import { handleCallback, send } from "@vercel/queue";
230
+ var DEFAULT_TOPIC_NAME = "junior-thread-message";
231
+ var MAX_DELIVERY_ATTEMPTS = 10;
232
+ function getThreadMessageTopic() {
233
+ return DEFAULT_TOPIC_NAME;
234
+ }
235
+ async function enqueueThreadMessage(payload, options) {
236
+ const result = await send(getThreadMessageTopic(), payload, {
237
+ ...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
238
+ });
239
+ return result.messageId ?? void 0;
240
+ }
241
+ function createQueueCallbackHandler(handler) {
242
+ return handleCallback(
243
+ async (message, metadata) => {
244
+ await handler(message, {
245
+ messageId: metadata.messageId,
246
+ deliveryCount: metadata.deliveryCount,
247
+ topicName: metadata.topicName
248
+ });
249
+ },
250
+ {
251
+ retry: (_error, metadata) => {
252
+ if (metadata.deliveryCount >= MAX_DELIVERY_ATTEMPTS) {
253
+ return { acknowledge: true };
254
+ }
255
+ const backoffSeconds = Math.min(300, Math.max(5, metadata.deliveryCount * 5));
256
+ return { afterSeconds: backoffSeconds };
257
+ }
258
+ }
259
+ );
260
+ }
261
+
232
262
  // src/chat/routing/subscribed-decision.ts
233
263
  import { z } from "zod";
234
264
  var replyDecisionSchema = z.object({
@@ -698,12 +728,9 @@ var defaultQueueRoutingDeps = {
698
728
  getIsSubscribed: (threadId) => getStateAdapter().isSubscribed(threadId),
699
729
  logInfo,
700
730
  logWarn,
701
- enqueueThreadMessage: async (payload, dedupKey) => {
702
- const { enqueueThreadMessage } = await import("./client-3GAEMIQ3.js");
703
- return await enqueueThreadMessage(payload, {
704
- idempotencyKey: dedupKey
705
- });
706
- },
731
+ enqueueThreadMessage: async (payload, dedupKey) => await enqueueThreadMessage(payload, {
732
+ idempotencyKey: dedupKey
733
+ }),
707
734
  shouldReplyInSubscribedThread: async ({ message, normalizedThreadId, thread }) => {
708
735
  const rawText = message.text;
709
736
  const text = stripLeadingBotMention(rawText, {
@@ -728,7 +755,6 @@ var defaultQueueRoutingDeps = {
728
755
  });
729
756
  },
730
757
  addProcessingReaction: async ({ channelId, timestamp }) => {
731
- const { addReactionToMessage } = await import("./channel-HJO33DGJ.js");
732
758
  await addReactionToMessage({
733
759
  channelId,
734
760
  timestamp,
@@ -736,7 +762,6 @@ var defaultQueueRoutingDeps = {
736
762
  });
737
763
  },
738
764
  removeProcessingReaction: async ({ channelId, timestamp }) => {
739
- const { removeReactionFromMessage } = await import("./channel-HJO33DGJ.js");
740
765
  await removeReactionFromMessage({
741
766
  channelId,
742
767
  timestamp,
@@ -3099,10 +3124,13 @@ registerBotHandlers({
3099
3124
  bot,
3100
3125
  appSlackRuntime
3101
3126
  });
3127
+
3102
3128
  export {
3103
- appSlackRuntime,
3104
- bot,
3105
- createNormalizingStream,
3129
+ getThreadMessageTopic,
3130
+ createQueueCallbackHandler,
3131
+ setBotDepsForTests,
3106
3132
  resetBotDepsForTests,
3107
- setBotDepsForTests
3133
+ createNormalizingStream,
3134
+ bot,
3135
+ appSlackRuntime
3108
3136
  };
@@ -12,7 +12,7 @@ import {
12
12
  import { after } from "next/server";
13
13
  import * as Sentry from "@sentry/nextjs";
14
14
  async function loadBot() {
15
- const { bot } = await import("./bot-DLML4Z7F.js");
15
+ const { bot } = await import("./bot-6KXJ366H.js");
16
16
  return bot;
17
17
  }
18
18
  async function POST(request, context) {
@@ -1,18 +1,18 @@
1
1
  import {
2
+ appSlackRuntime,
2
3
  createQueueCallbackHandler,
3
4
  getThreadMessageTopic
4
- } from "../chunk-ZA2IDPVG.js";
5
+ } from "../chunk-CJFEZLEN.js";
5
6
  import {
6
7
  acquireQueueMessageProcessingOwnership,
7
8
  completeQueueMessageProcessingOwnership,
9
+ downloadPrivateSlackFile,
8
10
  failQueueMessageProcessingOwnership,
9
11
  getQueueMessageProcessingState,
10
12
  getStateAdapter,
11
- refreshQueueMessageProcessingOwnership
12
- } from "../chunk-ZBFSIN6G.js";
13
- import {
14
- downloadPrivateSlackFile
15
- } from "../chunk-GDNDYMGX.js";
13
+ refreshQueueMessageProcessingOwnership,
14
+ removeReactionFromMessage
15
+ } from "../chunk-5LLCJPTH.js";
16
16
  import {
17
17
  createRequestContext,
18
18
  logError,
@@ -35,7 +35,6 @@ function rehydrateAttachmentFetchers(payload) {
35
35
  }
36
36
  }
37
37
  async function processThreadMessageRuntime(args) {
38
- const { appSlackRuntime } = await import("../bot-DLML4Z7F.js");
39
38
  const runtimePayload = {
40
39
  message: args.message,
41
40
  thread: args.thread
@@ -84,7 +83,6 @@ var QueueMessageOwnershipError = class extends Error {
84
83
  };
85
84
  var defaultProcessQueuedThreadMessageDeps = {
86
85
  clearProcessingReaction: async ({ channelId, timestamp }) => {
87
- const { removeReactionFromMessage } = await import("../channel-HJO33DGJ.js");
88
86
  await removeReactionFromMessage({
89
87
  channelId,
90
88
  timestamp,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  POST
3
- } from "../chunk-OD6TOSY4.js";
3
+ } from "../chunk-OVG2HBNM.js";
4
4
  import {
5
5
  GET
6
6
  } from "../chunk-4RBEYCOG.js";
@@ -8,7 +8,7 @@ import "../chunk-BBOVH5RF.js";
8
8
 
9
9
  // src/handlers/oauth-callback.ts
10
10
  async function loadOAuthRoute() {
11
- return import("../route-XLYK6CKP.js");
11
+ return import("../route-DMVINKJW.js");
12
12
  }
13
13
  async function GET2(request, context) {
14
14
  const route = await loadOAuthRoute();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  POST
3
- } from "../chunk-OD6TOSY4.js";
3
+ } from "../chunk-OVG2HBNM.js";
4
4
  import "../chunk-BBOVH5RF.js";
5
5
  export {
6
6
  POST
@@ -1,20 +1,15 @@
1
1
  import {
2
+ botConfig,
2
3
  escapeXml,
3
4
  generateAssistantReply,
4
5
  getOAuthProviderConfig,
6
+ getSlackClient,
7
+ getStateAdapter,
5
8
  getUserTokenStore,
6
9
  publishAppHomeView,
7
10
  resolveBaseUrl,
8
11
  truncateStatusText
9
- } from "./chunk-7E56WM6K.js";
10
- import {
11
- getStateAdapter
12
- } from "./chunk-ZBFSIN6G.js";
13
- import "./chunk-MM3YNA4F.js";
14
- import {
15
- botConfig,
16
- getSlackClient
17
- } from "./chunk-GDNDYMGX.js";
12
+ } from "./chunk-5LLCJPTH.js";
18
13
  import {
19
14
  logException,
20
15
  logInfo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -40,8 +40,8 @@
40
40
  "node-html-markdown": "^2.0.0",
41
41
  "yaml": "^2.8.2",
42
42
  "zod": "^4.3.6",
43
- "@sentry/junior-github": "0.1.0",
44
- "@sentry/junior-sentry": "0.1.0"
43
+ "@sentry/junior-github": "0.1.1",
44
+ "@sentry/junior-sentry": "0.1.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@sentry/nextjs": ">=10.0.0",
@@ -1,18 +0,0 @@
1
- import {
2
- addReactionToMessage,
3
- listChannelMembers,
4
- listChannelMessages,
5
- listThreadReplies,
6
- postMessageToChannel,
7
- removeReactionFromMessage
8
- } from "./chunk-MM3YNA4F.js";
9
- import "./chunk-GDNDYMGX.js";
10
- import "./chunk-BBOVH5RF.js";
11
- export {
12
- addReactionToMessage,
13
- listChannelMembers,
14
- listChannelMessages,
15
- listThreadReplies,
16
- postMessageToChannel,
17
- removeReactionFromMessage
18
- };
@@ -1,333 +0,0 @@
1
- import {
2
- logWarn
3
- } from "./chunk-BBOVH5RF.js";
4
-
5
- // src/chat/config.ts
6
- var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
7
- var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
8
- var DEFAULT_QUEUE_CALLBACK_MAX_DURATION_SECONDS = 800;
9
- var TURN_TIMEOUT_BUFFER_SECONDS = 20;
10
- function parseAgentTurnTimeoutMs(rawValue, maxTimeoutMs) {
11
- const value = Number.parseInt(rawValue ?? "", 10);
12
- if (Number.isNaN(value)) {
13
- return Math.max(MIN_AGENT_TURN_TIMEOUT_MS, Math.min(DEFAULT_AGENT_TURN_TIMEOUT_MS, maxTimeoutMs));
14
- }
15
- return Math.max(MIN_AGENT_TURN_TIMEOUT_MS, Math.min(value, maxTimeoutMs));
16
- }
17
- function resolveQueueCallbackMaxDurationSeconds() {
18
- const value = Number.parseInt(process.env.QUEUE_CALLBACK_MAX_DURATION_SECONDS ?? "", 10);
19
- if (Number.isNaN(value) || value <= 0) {
20
- return DEFAULT_QUEUE_CALLBACK_MAX_DURATION_SECONDS;
21
- }
22
- return value;
23
- }
24
- function resolveMaxTurnTimeoutMs(queueCallbackMaxDurationSeconds) {
25
- const budgetSeconds = queueCallbackMaxDurationSeconds - TURN_TIMEOUT_BUFFER_SECONDS;
26
- return Math.max(MIN_AGENT_TURN_TIMEOUT_MS, budgetSeconds * 1e3);
27
- }
28
- function buildBotConfig() {
29
- const queueCallbackMaxDurationSeconds = resolveQueueCallbackMaxDurationSeconds();
30
- const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(queueCallbackMaxDurationSeconds);
31
- return {
32
- userName: process.env.JUNIOR_BOT_NAME ?? "junior",
33
- modelId: process.env.AI_MODEL ?? "anthropic/claude-sonnet-4.6",
34
- fastModelId: process.env.AI_FAST_MODEL ?? process.env.AI_MODEL ?? "anthropic/claude-haiku-4.5",
35
- turnTimeoutMs: parseAgentTurnTimeoutMs(process.env.AGENT_TURN_TIMEOUT_MS, maxTurnTimeoutMs)
36
- };
37
- }
38
- var botConfig = buildBotConfig();
39
- function toOptionalTrimmed(value) {
40
- if (!value) {
41
- return void 0;
42
- }
43
- const trimmed = value.trim();
44
- return trimmed.length > 0 ? trimmed : void 0;
45
- }
46
- function getSlackBotToken() {
47
- return toOptionalTrimmed(process.env.SLACK_BOT_TOKEN) ?? toOptionalTrimmed(process.env.SLACK_BOT_USER_TOKEN);
48
- }
49
- function getSlackSigningSecret() {
50
- return toOptionalTrimmed(process.env.SLACK_SIGNING_SECRET);
51
- }
52
- function getSlackClientId() {
53
- return toOptionalTrimmed(process.env.SLACK_CLIENT_ID);
54
- }
55
- function getSlackClientSecret() {
56
- return toOptionalTrimmed(process.env.SLACK_CLIENT_SECRET);
57
- }
58
- function hasRedisConfig() {
59
- return Boolean(process.env.REDIS_URL);
60
- }
61
-
62
- // src/chat/slack-actions/client.ts
63
- import { WebClient } from "@slack/web-api";
64
- var SlackActionError = class extends Error {
65
- code;
66
- apiError;
67
- needed;
68
- provided;
69
- statusCode;
70
- requestId;
71
- errorData;
72
- retryAfterSeconds;
73
- detail;
74
- detailLine;
75
- detailRule;
76
- constructor(message, code, options = {}) {
77
- super(message);
78
- this.name = "SlackActionError";
79
- this.code = code;
80
- this.apiError = options.apiError;
81
- this.needed = options.needed;
82
- this.provided = options.provided;
83
- this.statusCode = options.statusCode;
84
- this.requestId = options.requestId;
85
- this.errorData = options.errorData;
86
- this.retryAfterSeconds = options.retryAfterSeconds;
87
- this.detail = options.detail;
88
- this.detailLine = options.detailLine;
89
- this.detailRule = options.detailRule;
90
- }
91
- };
92
- function serializeSlackErrorData(data) {
93
- if (!data || typeof data !== "object") {
94
- return void 0;
95
- }
96
- const filtered = Object.fromEntries(
97
- Object.entries(data).filter(([key]) => key !== "error")
98
- );
99
- if (Object.keys(filtered).length === 0) {
100
- return void 0;
101
- }
102
- try {
103
- const serialized = JSON.stringify(filtered);
104
- return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
105
- } catch {
106
- return void 0;
107
- }
108
- }
109
- function getHeaderString(headers, name) {
110
- if (!headers || typeof headers !== "object") {
111
- return void 0;
112
- }
113
- const key = name.toLowerCase();
114
- const entries = headers;
115
- for (const [entryKey, value] of Object.entries(entries)) {
116
- if (entryKey.toLowerCase() !== key) continue;
117
- if (typeof value === "string") return value;
118
- if (Array.isArray(value)) {
119
- const first = value.find((entry) => typeof entry === "string");
120
- return typeof first === "string" ? first : void 0;
121
- }
122
- }
123
- return void 0;
124
- }
125
- function parseSlackCanvasDetail(detail) {
126
- if (typeof detail !== "string") {
127
- return {};
128
- }
129
- const trimmed = detail.trim();
130
- if (!trimmed) {
131
- return {};
132
- }
133
- const parsed = {
134
- detail: trimmed
135
- };
136
- const lineMatch = trimmed.match(/line\s+(\d+):/i);
137
- if (lineMatch) {
138
- const line = Number.parseInt(lineMatch[1] ?? "", 10);
139
- if (Number.isFinite(line)) {
140
- parsed.detailLine = line;
141
- }
142
- }
143
- if (/unsupported heading depth/i.test(trimmed)) {
144
- parsed.detailRule = "unsupported_heading_depth";
145
- }
146
- return parsed;
147
- }
148
- var client = null;
149
- function normalizeSlackConversationId(channelId) {
150
- if (!channelId) return void 0;
151
- const trimmed = channelId.trim();
152
- if (!trimmed) return void 0;
153
- if (!trimmed.startsWith("slack:")) {
154
- return trimmed;
155
- }
156
- const parts = trimmed.split(":");
157
- return parts[1]?.trim() || void 0;
158
- }
159
- function getClient() {
160
- if (client) return client;
161
- const token = getSlackBotToken();
162
- if (!token) {
163
- throw new SlackActionError(
164
- "SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack canvas/list actions in this service",
165
- "missing_token"
166
- );
167
- }
168
- client = new WebClient(token);
169
- return client;
170
- }
171
- function mapSlackError(error) {
172
- if (error instanceof SlackActionError) {
173
- return error;
174
- }
175
- const candidate = error;
176
- const apiError = candidate.data?.error;
177
- const message = candidate.message ?? "Slack action failed";
178
- const baseOptions = {
179
- apiError,
180
- statusCode: candidate.statusCode,
181
- requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
182
- errorData: serializeSlackErrorData(candidate.data),
183
- ...parseSlackCanvasDetail(candidate.data?.detail)
184
- };
185
- if (apiError === "missing_scope") {
186
- return new SlackActionError(message, "missing_scope", {
187
- ...baseOptions,
188
- needed: candidate.data?.needed,
189
- provided: candidate.data?.provided
190
- });
191
- }
192
- if (apiError === "not_in_channel") {
193
- return new SlackActionError(message, "not_in_channel", baseOptions);
194
- }
195
- if (apiError === "invalid_arguments") {
196
- return new SlackActionError(message, "invalid_arguments", baseOptions);
197
- }
198
- if (apiError === "invalid_name") {
199
- return new SlackActionError(message, "invalid_arguments", baseOptions);
200
- }
201
- if (apiError === "not_found") {
202
- return new SlackActionError(message, "not_found", baseOptions);
203
- }
204
- if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
205
- return new SlackActionError(message, "feature_unavailable", baseOptions);
206
- }
207
- if (apiError === "canvas_creation_failed") {
208
- return new SlackActionError(message, "canvas_creation_failed", baseOptions);
209
- }
210
- if (apiError === "canvas_editing_failed") {
211
- return new SlackActionError(message, "canvas_editing_failed", baseOptions);
212
- }
213
- if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
214
- return new SlackActionError(message, "rate_limited", {
215
- ...baseOptions,
216
- retryAfterSeconds: candidate.retryAfter
217
- });
218
- }
219
- return new SlackActionError(message, "internal_error", baseOptions);
220
- }
221
- function sleep(ms) {
222
- return new Promise((resolve) => setTimeout(resolve, ms));
223
- }
224
- async function withSlackRetries(task, maxAttempts = 3, context = {}) {
225
- let attempt = 0;
226
- while (attempt < maxAttempts) {
227
- attempt += 1;
228
- try {
229
- return await task();
230
- } catch (error) {
231
- const mapped = mapSlackError(error);
232
- const isRetryable = mapped.code === "rate_limited";
233
- const baseLogAttributes = {
234
- "app.slack.action": context.action ?? "unknown",
235
- "app.slack.error_code": mapped.code,
236
- ...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
237
- ...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
238
- ...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
239
- ...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
240
- ...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
241
- ...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
242
- ...context.attributes ?? {}
243
- };
244
- if (!isRetryable || attempt >= maxAttempts) {
245
- logWarn(
246
- "slack_action_failed",
247
- {},
248
- {
249
- ...baseLogAttributes,
250
- ...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
251
- },
252
- "Slack action failed"
253
- );
254
- throw mapped;
255
- }
256
- logWarn(
257
- "slack_action_retrying",
258
- {},
259
- {
260
- ...baseLogAttributes,
261
- "app.slack.retry_attempt": attempt
262
- },
263
- "Retrying Slack action after transient failure"
264
- );
265
- const retryAfterMs = mapped.code === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
266
- const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
267
- await sleep(retryAfterMs ?? backoffMs);
268
- }
269
- }
270
- throw new SlackActionError("Slack action exhausted retries", "internal_error");
271
- }
272
- function getSlackClient() {
273
- return getClient();
274
- }
275
- function isDmChannel(channelId) {
276
- const normalized = normalizeSlackConversationId(channelId);
277
- return Boolean(normalized && normalized.startsWith("D"));
278
- }
279
- function isConversationScopedChannel(channelId) {
280
- const normalized = normalizeSlackConversationId(channelId);
281
- if (!normalized) return false;
282
- return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
283
- }
284
- function isConversationChannel(channelId) {
285
- const normalized = normalizeSlackConversationId(channelId);
286
- if (!normalized) return false;
287
- return normalized.startsWith("C") || normalized.startsWith("G");
288
- }
289
- async function getFilePermalink(fileId) {
290
- const client2 = getClient();
291
- const response = await withSlackRetries(
292
- () => client2.files.info({
293
- file: fileId
294
- })
295
- );
296
- return response.file?.permalink;
297
- }
298
- async function downloadPrivateSlackFile(url) {
299
- const token = getSlackBotToken();
300
- if (!token) {
301
- throw new SlackActionError(
302
- "SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack file downloads in this service",
303
- "missing_token"
304
- );
305
- }
306
- const response = await fetch(url, {
307
- headers: {
308
- Authorization: `Bearer ${token}`
309
- }
310
- });
311
- if (!response.ok) {
312
- throw new Error(`Slack file download failed: ${response.status}`);
313
- }
314
- return Buffer.from(await response.arrayBuffer());
315
- }
316
-
317
- export {
318
- botConfig,
319
- getSlackBotToken,
320
- getSlackSigningSecret,
321
- getSlackClientId,
322
- getSlackClientSecret,
323
- hasRedisConfig,
324
- SlackActionError,
325
- normalizeSlackConversationId,
326
- withSlackRetries,
327
- getSlackClient,
328
- isDmChannel,
329
- isConversationScopedChannel,
330
- isConversationChannel,
331
- getFilePermalink,
332
- downloadPrivateSlackFile
333
- };