@openclaw/slack 2026.7.2-beta.7 → 2026.8.1-beta.2
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/{account-inspect-DEKpg9Ip.js → account-inspect-Drw2gAiN.js} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{accounts-CHAeNZOX.js → accounts-CK4AVI2_.js} +2 -2
- package/dist/{accounts.runtime-BHD4Ma2R.js → accounts.runtime-Cb7QwWBz.js} +1 -1
- package/dist/{action-runtime-DfuYoMjG.js → action-runtime-BkGe7ny_.js} +148 -76
- package/dist/action-runtime.runtime-D9I6uc0Q.js +2 -0
- package/dist/{actions-D3NuJvva.js → actions-BTDXwwE3.js} +85 -44
- package/dist/actions.runtime-DLFyUkvg.js +2 -0
- package/dist/api.js +15 -15
- package/dist/{approval-handler.runtime-BA05P0QT.js → approval-handler.runtime-CJaNzAq2.js} +51 -14
- package/dist/{block-text-DkHaoGem.js → block-text-YtiNkqmx.js} +1 -1
- package/dist/{channel-B9fVdW3s.js → channel-D_tovcUL.js} +151 -91
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-DXyos1d2.js → channel-type-B9MSdoGN.js} +9 -8
- package/dist/{config-adapter-CxBQH8fq.js → channel.setup-Bch9dYmA.js} +83 -25
- package/dist/{config-schema-SgZT6nXV.js → config-schema-xto5jFIx.js} +5 -11
- package/dist/{directory-config-CnCIAktf.js → directory-config-CNBz8lJa.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-DKunWh3A.js → directory-live-CFKUtW9E.js} +13 -22
- package/dist/{doctor-contract-CsGH1Dfc.js → doctor-contract-BAy7-EUQ.js} +30 -3
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-NQZti9t-.js → group-policy-c4RP9bjc.js} +248 -163
- package/dist/index.js +4 -1
- package/dist/{message-tool-api-DkhFpMoy.js → message-tool-api-BykQB4AU.js} +14 -3
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-CmKz0wtI.js → monitor-1nF1NtqT.js} +3 -3
- package/dist/{outbound-adapter-BkuMp2Vs.js → outbound-adapter-S81tsswN.js} +19 -17
- package/dist/{pipeline.runtime-DgtmuYCP.js → pipeline.runtime-BvYsM5JO.js} +1087 -682
- package/dist/{policy-PRysPn23.js → policy-TapwbeaT.js} +18 -14
- package/dist/{probe-CgDsSops.js → probe-HoEg_WxB.js} +38 -18
- package/dist/{provider-DmRizMZt.js → provider-CDNvBXUi.js} +3422 -2590
- package/dist/{relay-source-Blxj8xb4.js → relay-source-C9leF11u.js} +11 -21
- package/dist/{replies-4s_Vq_z9.js → replies-CeOfv82I.js} +52 -6
- package/dist/{resolve-channels-yEqmM-3I.js → resolve-channels-CNPrxaXY.js} +39 -12
- package/dist/{resolve-users-BOrt5K-5.js → resolve-users-Dzomuwnr.js} +28 -5
- package/dist/{runtime-api-QMb-Q5Ko.js → runtime-api-Cj9nxBNv.js} +3 -3
- package/dist/runtime-api.js +11 -11
- package/dist/{scopes-BJSrkAp6.js → scopes-cYXKkrRD.js} +1 -1
- package/dist/{send-DsLeZpST.js → send-Nor0iBwY.js} +211 -105
- package/dist/{send.runtime-gUdK6V3h.js → send.runtime-BmgNe6S9.js} +1 -1
- package/dist/setup-entry.js +1 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-BeNzrMrJ.js → setup-surface-CbX1-Knh.js} +6 -6
- package/dist/{shared-DgIN3Dcr.js → shared-zdT5AMp-.js} +36 -42
- package/dist/{slash-dispatch.runtime-zPkyl7dB.js → slash-dispatch.runtime-Bnr9GR1S.js} +1 -1
- package/dist/{streaming-compat-3_X-21wW.js → streaming-compat-B-e0mqM0.js} +3 -8
- package/dist/{target-parsing-N6lHogYZ.js → target-parsing-BnMD2ZqZ.js} +40 -2
- package/dist/{targets-KmjP2E8s.js → targets-Cx5W_n3W.js} +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/socket-mode/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +54 -9
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
- package/node_modules/undici/lib/core/request.js +12 -1
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -2
- package/node_modules/undici/lib/handler/cache-handler.js +246 -64
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +3 -3
- package/node_modules/undici/lib/handler/retry-handler.js +24 -0
- package/node_modules/undici/lib/interceptor/cache.js +159 -52
- package/node_modules/undici/lib/util/cache.js +324 -62
- package/node_modules/undici/lib/util/date.js +23 -6
- package/node_modules/undici/lib/web/cookies/util.js +79 -9
- package/node_modules/undici/package.json +1 -1
- package/openclaw.plugin.json +239 -34
- package/package.json +9 -9
- package/skills/slack/SKILL.md +10 -68
- package/dist/action-runtime.runtime-DQErZYpz.js +0 -2
- package/dist/actions.runtime-BIRU_G4M.js +0 -2
- package/dist/channel.setup-CLP5oTvh.js +0 -56
- package/dist/preflight-audio.runtime-D5Bgags0.js +0 -3
- package/dist/slash-plugin-commands.runtime-Dosfcx3X.js +0 -2
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig } from "./accounts-
|
|
2
|
-
import {
|
|
1
|
+
import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig } from "./accounts-CK4AVI2_.js";
|
|
2
|
+
import { a as parseSlackTarget, n as formatSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-BnMD2ZqZ.js";
|
|
3
3
|
import { t as isSlackPluginAccountConfigured } from "./account-configured-BwfXBo_F.js";
|
|
4
4
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
5
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringifiedOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import { normalizeMessageChannel } from "openclaw/plugin-sdk/routing";
|
|
7
7
|
import { FormatCapabilityProfile, chunkTextForOutbound, markdownToIR, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers } from "openclaw/plugin-sdk/text-chunking";
|
|
8
|
-
import {
|
|
8
|
+
import { buildChannelKeyCandidates } from "openclaw/plugin-sdk/channel-targets";
|
|
9
|
+
import { createNativeApprovalChannelRouteGates, doesApprovalRequestSelectChannelAccount, resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
9
10
|
import { createChannelApprovalAuth, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
10
11
|
import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, isChannelExecApprovalTargetRecipient, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
11
12
|
import { channelRouteTargetsMatchExact, stringifyRouteThreadId } from "openclaw/plugin-sdk/channel-route";
|
|
13
|
+
import { resolveGlobalMap } from "openclaw/plugin-sdk/global-singleton";
|
|
12
14
|
import { normalizeMessagePresentation, reduceLegacyInteractiveReply, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, resolveMessagePresentationButtonAction, resolveMessagePresentationOptionAction } from "openclaw/plugin-sdk/interactive-runtime";
|
|
13
15
|
import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
|
|
14
16
|
import { buildApprovalResolutionRef } from "openclaw/plugin-sdk/approval-reference-runtime";
|
|
@@ -17,7 +19,7 @@ import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
|
17
19
|
import { eastAsianWidthType } from "get-east-asian-width";
|
|
18
20
|
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
19
21
|
import { compileAllowlist, resolveCompiledAllowlistMatch } from "openclaw/plugin-sdk/allow-from";
|
|
20
|
-
import { normalizeHyphenSlug, normalizeStringEntries as normalizeStringEntries$1, normalizeStringEntriesLower
|
|
22
|
+
import { normalizeHyphenSlug, normalizeStringEntries as normalizeStringEntries$1, normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
|
|
21
23
|
import { resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
22
24
|
//#region extensions/slack/src/exec-approvals.ts
|
|
23
25
|
function normalizeSlackUserLikeId(value) {
|
|
@@ -60,13 +62,7 @@ const slackExecApprovalProfile = createChannelExecApprovalProfile({
|
|
|
60
62
|
resolveConfig: (params) => resolveSlackAccount(params).config.execApprovals,
|
|
61
63
|
resolveApprovers: getSlackExecApprovalApprovers,
|
|
62
64
|
normalizeSenderId: normalizeSlackApproverId,
|
|
63
|
-
isTargetRecipient: isSlackExecApprovalTargetRecipient
|
|
64
|
-
matchesRequestAccount: (params) => doesApprovalRequestMatchChannelAccount({
|
|
65
|
-
cfg: params.cfg,
|
|
66
|
-
request: params.request,
|
|
67
|
-
channel: "slack",
|
|
68
|
-
accountId: params.accountId
|
|
69
|
-
})
|
|
65
|
+
isTargetRecipient: isSlackExecApprovalTargetRecipient
|
|
70
66
|
});
|
|
71
67
|
const isSlackExecApprovalClientEnabled = slackExecApprovalProfile.isClientEnabled;
|
|
72
68
|
const isSlackExecApprovalAuthorizedSender = slackExecApprovalProfile.isAuthorizedSender;
|
|
@@ -96,6 +92,34 @@ const slackApproval = createChannelApprovalAuth({
|
|
|
96
92
|
const getSlackApprovalApprovers = slackApproval.resolveApprovers;
|
|
97
93
|
const isSlackApprovalAuthorizedSender = slackApproval.isAuthorizedSender;
|
|
98
94
|
//#endregion
|
|
95
|
+
//#region extensions/slack/src/installation-identity-state.ts
|
|
96
|
+
const slackInstallationStates = resolveGlobalMap(Symbol.for("openclaw.slack.installation-identities"), "close-and-restart");
|
|
97
|
+
function registerSlackInstallationState(accountId, kind) {
|
|
98
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
99
|
+
const owner = Symbol(`slack-installation:${normalizedAccountId}`);
|
|
100
|
+
slackInstallationStates.set(normalizedAccountId, {
|
|
101
|
+
kind,
|
|
102
|
+
owner
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
update: (nextKind) => {
|
|
106
|
+
if (slackInstallationStates.get(normalizedAccountId)?.owner === owner) slackInstallationStates.set(normalizedAccountId, {
|
|
107
|
+
kind: nextKind,
|
|
108
|
+
owner
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
release: () => {
|
|
112
|
+
if (slackInstallationStates.get(normalizedAccountId)?.owner === owner) slackInstallationStates.delete(normalizedAccountId);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function getSlackInstallationKind(accountId) {
|
|
117
|
+
return slackInstallationStates.get(normalizeAccountId(accountId))?.kind;
|
|
118
|
+
}
|
|
119
|
+
function isSlackWorkspaceInstallation(accountId) {
|
|
120
|
+
return getSlackInstallationKind(accountId) === "workspace";
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
99
123
|
//#region extensions/slack/src/approval-native-gates.ts
|
|
100
124
|
const DEFAULT_APPROVAL_FORWARDING_MODE = "session";
|
|
101
125
|
const SLACK_DM_CHANNEL_ID_RE = /^D[A-Z0-9]{8,}$/i;
|
|
@@ -106,8 +130,7 @@ function resolveSlackApprovalKind(request) {
|
|
|
106
130
|
return isExec ? "exec" : "plugin";
|
|
107
131
|
}
|
|
108
132
|
function isSlackApprovalTransportEnabled(params) {
|
|
109
|
-
|
|
110
|
-
return account.config.enterpriseOrgInstall !== true && isSlackPluginAccountConfigured(account);
|
|
133
|
+
return isSlackPluginAccountConfigured(resolveSlackAccount(params));
|
|
111
134
|
}
|
|
112
135
|
function resolveSlackNativeApprovalConfig(params) {
|
|
113
136
|
return resolveSlackAccount(params).config.execApprovals;
|
|
@@ -140,7 +163,10 @@ function resolveTurnSourceSlackOriginTarget(request) {
|
|
|
140
163
|
}) });
|
|
141
164
|
if (!parsed) return null;
|
|
142
165
|
return {
|
|
143
|
-
to:
|
|
166
|
+
to: formatSlackTarget({
|
|
167
|
+
...parsed,
|
|
168
|
+
explicitKind: true
|
|
169
|
+
}),
|
|
144
170
|
threadId: stringifyRouteThreadId(request.request.turnSourceThreadId)
|
|
145
171
|
};
|
|
146
172
|
}
|
|
@@ -160,7 +186,11 @@ function resolveSlackFallbackOriginTarget(request) {
|
|
|
160
186
|
const parsed = parseSlackTarget(sessionTarget.id, { defaultKind: "channel" });
|
|
161
187
|
if (!parsed) return null;
|
|
162
188
|
return {
|
|
163
|
-
to:
|
|
189
|
+
to: formatSlackTarget({
|
|
190
|
+
...parsed,
|
|
191
|
+
id: canonicalizeSlackApiTargetId(parsed.kind, parsed.id),
|
|
192
|
+
explicitKind: true
|
|
193
|
+
}),
|
|
164
194
|
threadId: sessionTarget.threadId
|
|
165
195
|
};
|
|
166
196
|
}
|
|
@@ -177,6 +207,7 @@ function isSlackDmChannelToUserRoutePair(a, b) {
|
|
|
177
207
|
const left = parseComparableSlackTarget(a);
|
|
178
208
|
const right = parseComparableSlackTarget(b);
|
|
179
209
|
if (!left || !right) return false;
|
|
210
|
+
if (left.teamId?.toLowerCase() !== right.teamId?.toLowerCase()) return false;
|
|
180
211
|
return left.kind === "channel" && SLACK_DM_CHANNEL_ID_RE.test(left.id) && right.kind === "user" || right.kind === "channel" && SLACK_DM_CHANNEL_ID_RE.test(right.id) && left.kind === "user";
|
|
181
212
|
}
|
|
182
213
|
function slackTargetsMatch(a, b) {
|
|
@@ -201,7 +232,10 @@ function normalizeSlackForwardTarget(target) {
|
|
|
201
232
|
const parsed = parseSlackTarget(to, { defaultKind: SLACK_USER_ID_RE.test(to) ? "user" : "channel" });
|
|
202
233
|
if (!parsed) return null;
|
|
203
234
|
return {
|
|
204
|
-
to:
|
|
235
|
+
to: formatSlackTarget({
|
|
236
|
+
...parsed,
|
|
237
|
+
explicitKind: true
|
|
238
|
+
}),
|
|
205
239
|
accountId: normalizeOptionalString(target.accountId),
|
|
206
240
|
threadId: stringifyRouteThreadId(target.threadId)
|
|
207
241
|
};
|
|
@@ -235,21 +269,18 @@ function isSlackPluginNativeApprovalClientEnabled(params) {
|
|
|
235
269
|
return isSlackPluginNativeApprovalClientConfigEnabled(params) || isSlackPluginForwardingRoutePotentiallyEnabled(params);
|
|
236
270
|
}
|
|
237
271
|
function shouldHandleSlackPluginViaNativeClientConfig(params) {
|
|
238
|
-
if (!
|
|
239
|
-
|
|
240
|
-
request: params.request,
|
|
272
|
+
if (!doesApprovalRequestSelectChannelAccount({
|
|
273
|
+
...params,
|
|
241
274
|
channel: "slack",
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
approverCount: getSlackApprovalApprovers(params).length
|
|
275
|
+
defaultAccountId: resolveDefaultSlackAccountId(params.cfg),
|
|
276
|
+
eligibleAccountIds: listSlackNativeApprovalEligibleAccountIds({
|
|
277
|
+
...params,
|
|
278
|
+
approvalKind: "plugin"
|
|
279
|
+
})
|
|
248
280
|
})) return false;
|
|
249
|
-
return
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
sessionFilter: config?.sessionFilter
|
|
281
|
+
return isSlackNativeApprovalAccountEligible({
|
|
282
|
+
...params,
|
|
283
|
+
approvalKind: "plugin"
|
|
253
284
|
});
|
|
254
285
|
}
|
|
255
286
|
function matchesSlackNativeApprovalFilters(params) {
|
|
@@ -259,6 +290,25 @@ function matchesSlackNativeApprovalFilters(params) {
|
|
|
259
290
|
sessionFilter: params.sessionFilter
|
|
260
291
|
});
|
|
261
292
|
}
|
|
293
|
+
function isSlackNativeApprovalAccountEligible(params) {
|
|
294
|
+
const config = resolveSlackNativeApprovalConfig(params);
|
|
295
|
+
const approverCount = params.approvalKind === "exec" ? getSlackExecApprovalApprovers(params).length : getSlackApprovalApprovers(params).length;
|
|
296
|
+
return isSlackApprovalTransportEnabled(params) && isChannelExecApprovalClientEnabledFromConfig({
|
|
297
|
+
enabled: config?.enabled,
|
|
298
|
+
approverCount
|
|
299
|
+
}) && matchesSlackNativeApprovalFilters({
|
|
300
|
+
request: params.request,
|
|
301
|
+
agentFilter: config?.agentFilter,
|
|
302
|
+
sessionFilter: config?.sessionFilter
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function listSlackNativeApprovalEligibleAccountIds(params) {
|
|
306
|
+
const accountId = params.accountId ?? resolveDefaultSlackAccountId(params.cfg);
|
|
307
|
+
return isSlackNativeApprovalAccountEligible({
|
|
308
|
+
...params,
|
|
309
|
+
accountId
|
|
310
|
+
}) ? [accountId] : [];
|
|
311
|
+
}
|
|
262
312
|
function isAnyForwardedSlackExplicitTargetEligible(params) {
|
|
263
313
|
return (resolvePluginApprovalForwardingConfig(params.cfg)?.targets ?? []).some((target) => isForwardedSlackExplicitTargetEligible({
|
|
264
314
|
...params,
|
|
@@ -292,24 +342,32 @@ function isSlackAnyNativeApprovalClientEnabled(params) {
|
|
|
292
342
|
});
|
|
293
343
|
}
|
|
294
344
|
function shouldHandleSlackNativeApprovalRequest(params) {
|
|
345
|
+
if (getSlackInstallationKind(resolveSlackAccount(params).accountId) === "enterprise" && !resolveEnterpriseApprovalTeamId(params.request)) return false;
|
|
295
346
|
if ((params.approvalKind ?? resolveSlackApprovalKind(params.request)) === "plugin") return shouldHandleSlackPluginViaNativeClientConfig(params) || shouldHandleSlackPluginViaForwarding(params);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
347
|
+
const turnSourceChannel = normalizeMessageChannel(params.request.request.turnSourceChannel);
|
|
348
|
+
if (turnSourceChannel && turnSourceChannel !== "slack") return false;
|
|
349
|
+
if (!doesApprovalRequestSelectChannelAccount({
|
|
350
|
+
...params,
|
|
299
351
|
channel: "slack",
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
approverCount: getSlackExecApprovalApprovers(params).length
|
|
352
|
+
defaultAccountId: resolveDefaultSlackAccountId(params.cfg),
|
|
353
|
+
eligibleAccountIds: listSlackNativeApprovalEligibleAccountIds({
|
|
354
|
+
...params,
|
|
355
|
+
approvalKind: "exec"
|
|
356
|
+
})
|
|
306
357
|
})) return false;
|
|
307
|
-
return
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
sessionFilter: config?.sessionFilter
|
|
358
|
+
return isSlackNativeApprovalAccountEligible({
|
|
359
|
+
...params,
|
|
360
|
+
approvalKind: "exec"
|
|
311
361
|
});
|
|
312
362
|
}
|
|
363
|
+
function resolveEnterpriseApprovalTeamId(request) {
|
|
364
|
+
try {
|
|
365
|
+
const target = resolveTurnSourceSlackOriginTarget(request);
|
|
366
|
+
return target ? parseSlackTarget(target.to)?.teamId : void 0;
|
|
367
|
+
} catch {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
313
371
|
//#endregion
|
|
314
372
|
//#region extensions/slack/src/blocks-input.ts
|
|
315
373
|
const SLACK_MAX_BLOCKS = 50;
|
|
@@ -500,42 +558,36 @@ function renderSlackMessagePresentationFallbackText(params) {
|
|
|
500
558
|
//#region extensions/slack/src/data-table.ts
|
|
501
559
|
const SLACK_DATA_TABLE_COLUMNS_MAX = 20;
|
|
502
560
|
const SLACK_DATA_TABLE_ROWS_MAX = 100;
|
|
503
|
-
function asRecord$3(value) {
|
|
504
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
505
|
-
}
|
|
506
|
-
function readNonEmptyString$1(value) {
|
|
507
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
508
|
-
}
|
|
509
561
|
function countCharacters(value) {
|
|
510
562
|
return Array.from(value).length;
|
|
511
563
|
}
|
|
512
564
|
function readRichTextLeaf(record) {
|
|
513
565
|
if (record.type === "text" && typeof record.text === "string") return record.text;
|
|
514
|
-
const text =
|
|
566
|
+
const text = readNonBlankString(record.text);
|
|
515
567
|
if (text) return text;
|
|
516
568
|
switch (record.type) {
|
|
517
|
-
case "link": return
|
|
569
|
+
case "link": return readNonBlankString(record.url) ?? "";
|
|
518
570
|
case "user": {
|
|
519
|
-
const userId =
|
|
571
|
+
const userId = readNonBlankString(record.user_id);
|
|
520
572
|
return userId ? `<@${userId}>` : "";
|
|
521
573
|
}
|
|
522
574
|
case "channel": {
|
|
523
|
-
const channelId =
|
|
575
|
+
const channelId = readNonBlankString(record.channel_id);
|
|
524
576
|
return channelId ? `<#${channelId}>` : "";
|
|
525
577
|
}
|
|
526
578
|
case "usergroup": {
|
|
527
|
-
const usergroupId =
|
|
579
|
+
const usergroupId = readNonBlankString(record.usergroup_id);
|
|
528
580
|
return usergroupId ? `<!subteam^${usergroupId}>` : "";
|
|
529
581
|
}
|
|
530
582
|
case "broadcast": {
|
|
531
|
-
const range =
|
|
583
|
+
const range = readNonBlankString(record.range);
|
|
532
584
|
return range ? `<!${range}>` : "";
|
|
533
585
|
}
|
|
534
586
|
case "emoji": {
|
|
535
|
-
const name =
|
|
587
|
+
const name = readNonBlankString(record.name);
|
|
536
588
|
return name ? `:${name}:` : "";
|
|
537
589
|
}
|
|
538
|
-
case "date": return
|
|
590
|
+
case "date": return readNonBlankString(record.fallback) ?? "";
|
|
539
591
|
default: return "";
|
|
540
592
|
}
|
|
541
593
|
}
|
|
@@ -543,7 +595,7 @@ function readRichTextElements(value, separator = "") {
|
|
|
543
595
|
if (!Array.isArray(value)) return "";
|
|
544
596
|
const parts = [];
|
|
545
597
|
for (const rawElement of value) {
|
|
546
|
-
const element =
|
|
598
|
+
const element = asOptionalRecord(rawElement);
|
|
547
599
|
if (!element) continue;
|
|
548
600
|
if (Array.isArray(element.elements)) {
|
|
549
601
|
const rendered = readRichTextElements(element.elements, element.type === "rich_text_list" ? "\n" : "");
|
|
@@ -556,7 +608,7 @@ function readRichTextElements(value, separator = "") {
|
|
|
556
608
|
return parts.join(separator);
|
|
557
609
|
}
|
|
558
610
|
function readSlackBasicTableCell(value) {
|
|
559
|
-
const cell =
|
|
611
|
+
const cell = asOptionalRecord(value);
|
|
560
612
|
if (!cell) return "";
|
|
561
613
|
if (cell.type === "raw_text") return typeof cell.text === "string" ? cell.text : "";
|
|
562
614
|
if (cell.type === "raw_number") {
|
|
@@ -567,7 +619,7 @@ function readSlackBasicTableCell(value) {
|
|
|
567
619
|
return cell.type === "rich_text" ? readRichTextElements(cell.elements, "\n") : "";
|
|
568
620
|
}
|
|
569
621
|
function parseSlackBasicTableRows(value) {
|
|
570
|
-
const block =
|
|
622
|
+
const block = asOptionalRecord(value);
|
|
571
623
|
if (block?.type !== "table" || !Array.isArray(block.rows)) return;
|
|
572
624
|
if (block.rows.length < 1 || block.rows.length > SLACK_DATA_TABLE_ROWS_MAX) return;
|
|
573
625
|
let characterCount = 0;
|
|
@@ -582,15 +634,15 @@ function parseSlackBasicTableRows(value) {
|
|
|
582
634
|
return rows.some((row) => row.some((cell) => cell.length > 0)) ? rows : void 0;
|
|
583
635
|
}
|
|
584
636
|
function readSlackDataTableCell(value, allowRichText) {
|
|
585
|
-
const cell =
|
|
637
|
+
const cell = asOptionalRecord(value);
|
|
586
638
|
if (!cell) return;
|
|
587
|
-
if (cell.type === "raw_text") return
|
|
588
|
-
if (cell.type === "raw_number") return typeof cell.value === "number" && Number.isFinite(cell.value) ?
|
|
589
|
-
if (allowRichText && cell.type === "rich_text") return
|
|
639
|
+
if (cell.type === "raw_text") return readNonBlankString(cell.text);
|
|
640
|
+
if (cell.type === "raw_number") return typeof cell.value === "number" && Number.isFinite(cell.value) ? readNonBlankString(cell.text) : void 0;
|
|
641
|
+
if (allowRichText && cell.type === "rich_text") return readNonBlankString(readRichTextElements(cell.elements));
|
|
590
642
|
}
|
|
591
643
|
function parseSlackDataTable(value, options = {}) {
|
|
592
|
-
const block =
|
|
593
|
-
const caption =
|
|
644
|
+
const block = asOptionalRecord(value);
|
|
645
|
+
const caption = readNonBlankString(block?.caption);
|
|
594
646
|
if (block?.type !== "data_table" || !caption || !Array.isArray(block.rows)) return;
|
|
595
647
|
if (block.rows.length < 2) return;
|
|
596
648
|
const rawHeader = block.rows[0];
|
|
@@ -614,7 +666,7 @@ function parseSlackDataTable(value, options = {}) {
|
|
|
614
666
|
}
|
|
615
667
|
/** Detect current native table blocks without depending on unreleased Slack SDK types. */
|
|
616
668
|
function hasSlackDataTableBlock(blocks) {
|
|
617
|
-
return blocks?.some((block) =>
|
|
669
|
+
return blocks?.some((block) => asOptionalRecord(block)?.type === "data_table") ?? false;
|
|
618
670
|
}
|
|
619
671
|
function countSlackDataTableCellCharacters(value) {
|
|
620
672
|
return parseSlackDataTable(value, { enforceNativeLimits: true })?.cellCharacterCount;
|
|
@@ -678,7 +730,7 @@ function buildSlackDataTableBlock(block, options = {}) {
|
|
|
678
730
|
}
|
|
679
731
|
/** Extract a deterministic accessible summary from a native Slack table block. */
|
|
680
732
|
function renderSlackDataTableFallbackText(value) {
|
|
681
|
-
const block =
|
|
733
|
+
const block = asOptionalRecord(value);
|
|
682
734
|
if (block?.type !== "data_table") return;
|
|
683
735
|
const parsed = parseSlackDataTable(block);
|
|
684
736
|
if (parsed) return renderMessagePresentationTableFallbackText({
|
|
@@ -687,7 +739,7 @@ function renderSlackDataTableFallbackText(value) {
|
|
|
687
739
|
headers: parsed.headers,
|
|
688
740
|
rows: parsed.rows
|
|
689
741
|
});
|
|
690
|
-
return
|
|
742
|
+
return readNonBlankString(block.caption)?.trim();
|
|
691
743
|
}
|
|
692
744
|
function escapeCompactFallbackCell(value) {
|
|
693
745
|
return value.replaceAll("\\", "\\\\").replaceAll(" ", "\\t").replaceAll("\r", "\\r").replaceAll("\n", "\\n");
|
|
@@ -708,10 +760,10 @@ function renderSlackTableMrkdwnFallbackText(value) {
|
|
|
708
760
|
}
|
|
709
761
|
/** Render each native table cell once for bounded, formatting-disabled delivery. */
|
|
710
762
|
function renderSlackDataTableCompactPlainTextFallback(value) {
|
|
711
|
-
const block =
|
|
763
|
+
const block = asOptionalRecord(value);
|
|
712
764
|
if (block?.type !== "data_table") return;
|
|
713
765
|
const parsed = parseSlackDataTable(block);
|
|
714
|
-
if (!parsed) return
|
|
766
|
+
if (!parsed) return readNonBlankString(block.caption)?.trim();
|
|
715
767
|
return [
|
|
716
768
|
`${escapeCompactFallbackCell(parsed.caption)} (table)`,
|
|
717
769
|
parsed.headers.map(escapeCompactFallbackCell).join(" "),
|
|
@@ -720,7 +772,7 @@ function renderSlackDataTableCompactPlainTextFallback(value) {
|
|
|
720
772
|
}
|
|
721
773
|
/** Render a native table as mrkdwn without activating raw cell control tokens. */
|
|
722
774
|
function renderSlackDataTableMrkdwnFallbackText(value) {
|
|
723
|
-
const block =
|
|
775
|
+
const block = asOptionalRecord(value);
|
|
724
776
|
if (block?.type !== "data_table") return;
|
|
725
777
|
const parsed = parseSlackDataTable(block);
|
|
726
778
|
if (parsed) return renderSlackMessagePresentationTableFallbackText({
|
|
@@ -729,7 +781,7 @@ function renderSlackDataTableMrkdwnFallbackText(value) {
|
|
|
729
781
|
headers: parsed.headers,
|
|
730
782
|
rows: parsed.rows
|
|
731
783
|
});
|
|
732
|
-
const caption =
|
|
784
|
+
const caption = readNonBlankString(block.caption)?.trim();
|
|
733
785
|
return caption ? escapeSlackMrkdwn(caption) : void 0;
|
|
734
786
|
}
|
|
735
787
|
//#endregion
|
|
@@ -739,12 +791,9 @@ const SLACK_CHART_LABEL_MAX = 20;
|
|
|
739
791
|
const SLACK_CHART_AXIS_LABEL_MAX = 50;
|
|
740
792
|
const SLACK_CHART_SERIES_MAX = 12;
|
|
741
793
|
const SLACK_CHART_DATA_POINTS_MAX = 20;
|
|
742
|
-
function asRecord$2(value) {
|
|
743
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
744
|
-
}
|
|
745
794
|
/** Detect native chart blocks without depending on unreleased Slack SDK types. */
|
|
746
795
|
function hasSlackDataVisualizationBlock(blocks) {
|
|
747
|
-
return blocks?.some((block) =>
|
|
796
|
+
return blocks?.some((block) => asOptionalRecord(block)?.type === "data_visualization") ?? false;
|
|
748
797
|
}
|
|
749
798
|
function isStringWithin(value, maxLength) {
|
|
750
799
|
return typeof value === "string" && value.trim().length > 0 && Array.from(value).length <= maxLength;
|
|
@@ -791,7 +840,7 @@ function buildSlackDataVisualizationBlock(block) {
|
|
|
791
840
|
};
|
|
792
841
|
}
|
|
793
842
|
function readSlackChartDatum(value) {
|
|
794
|
-
const record =
|
|
843
|
+
const record = asOptionalRecord(value);
|
|
795
844
|
const label = record?.label;
|
|
796
845
|
const datumValue = record?.value;
|
|
797
846
|
return typeof label === "string" && typeof datumValue === "number" ? {
|
|
@@ -800,9 +849,9 @@ function readSlackChartDatum(value) {
|
|
|
800
849
|
} : void 0;
|
|
801
850
|
}
|
|
802
851
|
function parseSlackDataVisualizationBlock(value) {
|
|
803
|
-
const block =
|
|
852
|
+
const block = asOptionalRecord(value);
|
|
804
853
|
const title = block?.title;
|
|
805
|
-
const chart =
|
|
854
|
+
const chart = asOptionalRecord(block?.chart);
|
|
806
855
|
if (block?.type !== "data_visualization" || typeof title !== "string" || !chart) return;
|
|
807
856
|
if (chart.type === "pie") {
|
|
808
857
|
if (!Array.isArray(chart.segments)) return;
|
|
@@ -817,12 +866,12 @@ function parseSlackDataVisualizationBlock(value) {
|
|
|
817
866
|
return normalizedBlock?.type === "chart" ? normalizedBlock : void 0;
|
|
818
867
|
}
|
|
819
868
|
if (chart.type !== "bar" && chart.type !== "area" && chart.type !== "line") return;
|
|
820
|
-
const axisConfig =
|
|
869
|
+
const axisConfig = asOptionalRecord(chart.axis_config);
|
|
821
870
|
const categories = axisConfig?.categories;
|
|
822
871
|
if (!Array.isArray(categories) || !categories.every((category) => typeof category === "string")) return;
|
|
823
872
|
if (!Array.isArray(chart.series)) return;
|
|
824
873
|
const series = chart.series.map((rawSeries) => {
|
|
825
|
-
const seriesRecord =
|
|
874
|
+
const seriesRecord = asOptionalRecord(rawSeries);
|
|
826
875
|
if (typeof seriesRecord?.name !== "string" || !Array.isArray(seriesRecord.data)) return;
|
|
827
876
|
const data = seriesRecord.data.map(readSlackChartDatum);
|
|
828
877
|
if (data.some((datum) => !datum) || data.length !== categories.length) return;
|
|
@@ -847,7 +896,7 @@ function parseSlackDataVisualizationBlock(value) {
|
|
|
847
896
|
}
|
|
848
897
|
/** Extract a deterministic accessible summary from a native Slack chart block. */
|
|
849
898
|
function renderSlackDataVisualizationFallbackText(value) {
|
|
850
|
-
const block =
|
|
899
|
+
const block = asOptionalRecord(value);
|
|
851
900
|
if (block?.type !== "data_visualization") return;
|
|
852
901
|
const parsed = parseSlackDataVisualizationBlock(block);
|
|
853
902
|
if (parsed) return renderMessagePresentationChartFallbackText(parsed);
|
|
@@ -855,7 +904,7 @@ function renderSlackDataVisualizationFallbackText(value) {
|
|
|
855
904
|
}
|
|
856
905
|
/** Render a native chart as mrkdwn without activating raw data control tokens. */
|
|
857
906
|
function renderSlackDataVisualizationMrkdwnFallbackText(value) {
|
|
858
|
-
const block =
|
|
907
|
+
const block = asOptionalRecord(value);
|
|
859
908
|
if (block?.type !== "data_visualization") return;
|
|
860
909
|
const parsed = parseSlackDataVisualizationBlock(block);
|
|
861
910
|
if (parsed) return renderSlackMessagePresentationChartFallbackText(parsed);
|
|
@@ -898,15 +947,24 @@ async function resolveSlackQuestionAction(params) {
|
|
|
898
947
|
//#region extensions/slack/src/reply-action-ids.ts
|
|
899
948
|
const SLACK_REPLY_BUTTON_ACTION_ID = "openclaw:reply_button";
|
|
900
949
|
const SLACK_REPLY_LINK_ACTION_ID = "openclaw:reply_link";
|
|
950
|
+
const SLACK_SESSION_LINK_ACTION_ID = "openclaw:session_link";
|
|
901
951
|
const SLACK_REPLY_SELECT_ACTION_ID = "openclaw:reply_select";
|
|
902
952
|
const SLACK_CALLBACK_BUTTON_ACTION_ID = "openclaw:callback_button";
|
|
903
953
|
const SLACK_CALLBACK_SELECT_ACTION_ID = "openclaw:callback_select";
|
|
904
954
|
const SLACK_APPROVAL_BUTTON_ACTION_ID = "openclaw:approval_button";
|
|
905
955
|
const SLACK_APPROVAL_SELECT_ACTION_ID = "openclaw:approval_select";
|
|
906
956
|
const SLACK_QUESTION_BUTTON_ACTION_ID = "openclaw:question_button";
|
|
957
|
+
const SLACK_QUESTION_FINALIZATION_BLOCKS = Symbol("slackQuestionFinalizationBlocks");
|
|
907
958
|
function isSlackQuestionActionId(actionId) {
|
|
908
959
|
return actionId === "openclaw:question_button" || actionId.startsWith(`openclaw:question_button:`);
|
|
909
960
|
}
|
|
961
|
+
/** Read only question control identities from the blocks actually sent to Slack. */
|
|
962
|
+
function resolveSlackQuestionActionIds(blocks) {
|
|
963
|
+
return (blocks ?? []).flatMap((block) => {
|
|
964
|
+
if (block.type !== "actions") return [];
|
|
965
|
+
return (block.elements ?? []).flatMap(({ action_id }) => action_id && isSlackQuestionActionId(action_id) ? [action_id] : []);
|
|
966
|
+
});
|
|
967
|
+
}
|
|
910
968
|
function isSlackApprovalActionId(actionId) {
|
|
911
969
|
return actionId === "openclaw:approval_button" || actionId === "openclaw:approval_select" || actionId.startsWith(`openclaw:approval_button:`) || actionId.startsWith(`openclaw:approval_select:`);
|
|
912
970
|
}
|
|
@@ -1351,6 +1409,7 @@ function buildSlackPresentationSelectBlock(block, selectIndex) {
|
|
|
1351
1409
|
//#endregion
|
|
1352
1410
|
//#region extensions/slack/src/limits.ts
|
|
1353
1411
|
const SLACK_TEXT_LIMIT = 8e3;
|
|
1412
|
+
const SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT = 4e3;
|
|
1354
1413
|
const SLACK_EDIT_TEXT_MAX_BYTES = 4e3;
|
|
1355
1414
|
const SLACK_MESSAGE_TEXT_HARD_LIMIT = 4e4;
|
|
1356
1415
|
//#endregion
|
|
@@ -1394,54 +1453,46 @@ const SLACK_SELECT_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
1394
1453
|
"channels_select",
|
|
1395
1454
|
"multi_channels_select"
|
|
1396
1455
|
]);
|
|
1397
|
-
function asRecord$1(value) {
|
|
1398
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1399
|
-
}
|
|
1400
|
-
function readNonEmptyString(value) {
|
|
1401
|
-
if (typeof value !== "string") return;
|
|
1402
|
-
const trimmed = value.trim();
|
|
1403
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
1404
|
-
}
|
|
1405
1456
|
function readTextObject(value, options = {}) {
|
|
1406
|
-
const record =
|
|
1457
|
+
const record = asOptionalRecord(value);
|
|
1407
1458
|
if (!record) return;
|
|
1408
|
-
const text =
|
|
1459
|
+
const text = normalizeOptionalString(record?.text);
|
|
1409
1460
|
if (!text) return;
|
|
1410
1461
|
return record.type === "plain_text" && options.nativeDataFormat !== "plain" ? escapeSlackMrkdwn(text) : text;
|
|
1411
1462
|
}
|
|
1412
1463
|
function readTextValue(value, options = {}) {
|
|
1413
|
-
return
|
|
1464
|
+
return normalizeOptionalString(value) ?? readTextObject(value, options);
|
|
1414
1465
|
}
|
|
1415
1466
|
function renderSlackRichTextLeaf(element) {
|
|
1416
1467
|
switch (element.type) {
|
|
1417
1468
|
case "text": return typeof element.text === "string" ? escapeSlackMrkdwn(element.text) : "";
|
|
1418
|
-
case "link": return escapeSlackMrkdwn(
|
|
1469
|
+
case "link": return escapeSlackMrkdwn(normalizeOptionalString(element.text) ?? normalizeOptionalString(element.url) ?? "");
|
|
1419
1470
|
case "user": {
|
|
1420
|
-
const userId =
|
|
1471
|
+
const userId = normalizeOptionalString(element.user_id);
|
|
1421
1472
|
return userId ? escapeSlackMrkdwn(`<@${userId}>`) : "";
|
|
1422
1473
|
}
|
|
1423
1474
|
case "channel": {
|
|
1424
|
-
const channelId =
|
|
1475
|
+
const channelId = normalizeOptionalString(element.channel_id);
|
|
1425
1476
|
return channelId ? escapeSlackMrkdwn(`<#${channelId}>`) : "";
|
|
1426
1477
|
}
|
|
1427
1478
|
case "usergroup": {
|
|
1428
|
-
const usergroupId =
|
|
1479
|
+
const usergroupId = normalizeOptionalString(element.usergroup_id);
|
|
1429
1480
|
return usergroupId ? escapeSlackMrkdwn(`<!subteam^${usergroupId}>`) : "";
|
|
1430
1481
|
}
|
|
1431
1482
|
case "broadcast": {
|
|
1432
|
-
const range =
|
|
1483
|
+
const range = normalizeOptionalString(element.range);
|
|
1433
1484
|
return range ? escapeSlackMrkdwn(`<!${range}>`) : "";
|
|
1434
1485
|
}
|
|
1435
1486
|
case "emoji": {
|
|
1436
|
-
const name =
|
|
1487
|
+
const name = normalizeOptionalString(element.name);
|
|
1437
1488
|
return name ? `:${name}:` : "";
|
|
1438
1489
|
}
|
|
1439
|
-
case "date": return escapeSlackMrkdwn(
|
|
1490
|
+
case "date": return escapeSlackMrkdwn(normalizeOptionalString(element.fallback) ?? "");
|
|
1440
1491
|
default: return "";
|
|
1441
1492
|
}
|
|
1442
1493
|
}
|
|
1443
1494
|
function renderSlackRichTextElement(value) {
|
|
1444
|
-
const element =
|
|
1495
|
+
const element = asOptionalRecord(value);
|
|
1445
1496
|
if (!element) return "";
|
|
1446
1497
|
switch (element.type) {
|
|
1447
1498
|
case "rich_text_section":
|
|
@@ -1456,25 +1507,25 @@ function renderSlackRichTextElements(value, separator) {
|
|
|
1456
1507
|
return value.map(renderSlackRichTextElement).filter(Boolean).join(separator);
|
|
1457
1508
|
}
|
|
1458
1509
|
function readImageText(block) {
|
|
1459
|
-
const altText =
|
|
1510
|
+
const altText = normalizeOptionalString(block.alt_text);
|
|
1460
1511
|
return (altText ? escapeSlackMrkdwn(altText) : void 0) ?? readTextObject(block.title);
|
|
1461
1512
|
}
|
|
1462
1513
|
function readVideoText(block, options = {}) {
|
|
1463
|
-
const altText =
|
|
1514
|
+
const altText = normalizeOptionalString(block.alt_text);
|
|
1464
1515
|
return readTextObject(block.title, options) ?? (altText ? escapeSlackMrkdwn(altText) : void 0);
|
|
1465
1516
|
}
|
|
1466
1517
|
function readContextText(block, options = {}) {
|
|
1467
1518
|
if (!Array.isArray(block.elements)) return;
|
|
1468
1519
|
const parts = block.elements.map((element) => {
|
|
1469
|
-
const record =
|
|
1470
|
-
const altText =
|
|
1520
|
+
const record = asOptionalRecord(element);
|
|
1521
|
+
const altText = normalizeOptionalString(record?.alt_text);
|
|
1471
1522
|
return readTextObject(record, options) ?? (altText ? escapeSlackMrkdwn(altText) : void 0);
|
|
1472
1523
|
}).filter((part) => Boolean(part));
|
|
1473
1524
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
1474
1525
|
}
|
|
1475
1526
|
function readControlElementText(value, options = {}) {
|
|
1476
|
-
const element =
|
|
1477
|
-
const type =
|
|
1527
|
+
const element = asOptionalRecord(value);
|
|
1528
|
+
const type = normalizeOptionalString(element?.type);
|
|
1478
1529
|
if (type === "button" || type === "workflow_button") return readTextValue(element?.text, options);
|
|
1479
1530
|
if (type && SLACK_SELECT_ELEMENT_TYPES.has(type)) return readTextObject(element?.placeholder, options);
|
|
1480
1531
|
}
|
|
@@ -1501,10 +1552,10 @@ function readActionsText(block, options = {}) {
|
|
|
1501
1552
|
}
|
|
1502
1553
|
/** Read only user-visible text from one Slack block. */
|
|
1503
1554
|
function renderSlackBlockFallbackText(raw, options = {}) {
|
|
1504
|
-
const block =
|
|
1555
|
+
const block = asOptionalRecord(raw);
|
|
1505
1556
|
if (!block) return;
|
|
1506
1557
|
switch (block.type) {
|
|
1507
|
-
case "rich_text": return
|
|
1558
|
+
case "rich_text": return normalizeOptionalString(renderSlackRichTextElements(block.elements, "\n"));
|
|
1508
1559
|
case "header": return readTextObject(block.text, options);
|
|
1509
1560
|
case "section": return readSectionText(block, options);
|
|
1510
1561
|
case "image": return readImageText(block) ?? "Shared an image";
|
|
@@ -1894,9 +1945,6 @@ function markdownToSlackMrkdwnChunks(markdown, limit, options = {}) {
|
|
|
1894
1945
|
const SLACK_MALFORMED_NATIVE_DATA_FALLBACK = "Slack could not render this chart or table data.";
|
|
1895
1946
|
const SLACK_RESPONSE_URL_BODY_LIMIT_BYTES = 16 * 1024;
|
|
1896
1947
|
const SLACK_RESPONSE_URL_BODY_TIMEOUT_MS = 3e4;
|
|
1897
|
-
function asRecord(value) {
|
|
1898
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1899
|
-
}
|
|
1900
1948
|
/** Detect a native Slack chart or table block. */
|
|
1901
1949
|
function hasSlackNativeDataBlock(blocks) {
|
|
1902
1950
|
return hasSlackDataVisualizationBlock(blocks) || hasSlackDataTableBlock(blocks);
|
|
@@ -1904,23 +1952,23 @@ function hasSlackNativeDataBlock(blocks) {
|
|
|
1904
1952
|
/** Keep every sibling block while removing Slack's native data blocks. */
|
|
1905
1953
|
function stripSlackNativeDataBlocks(blocks) {
|
|
1906
1954
|
return (blocks ?? []).filter((block) => {
|
|
1907
|
-
const type =
|
|
1955
|
+
const type = asOptionalRecord(block)?.type;
|
|
1908
1956
|
return type !== "data_table" && type !== "data_visualization";
|
|
1909
1957
|
});
|
|
1910
1958
|
}
|
|
1911
1959
|
/** Match Slack's Web API and response_url `invalid_blocks` error shapes. */
|
|
1912
1960
|
function isSlackInvalidBlocksError(error) {
|
|
1913
|
-
const record =
|
|
1961
|
+
const record = asOptionalRecord(error);
|
|
1914
1962
|
const rawData = record?.data;
|
|
1915
|
-
const data =
|
|
1916
|
-
const rawResponseData =
|
|
1917
|
-
const responseData =
|
|
1963
|
+
const data = asOptionalRecord(rawData);
|
|
1964
|
+
const rawResponseData = asOptionalRecord(record?.response)?.data;
|
|
1965
|
+
const responseData = asOptionalRecord(rawResponseData);
|
|
1918
1966
|
const code = data?.error ?? (typeof rawData === "string" ? rawData : void 0) ?? responseData?.error ?? (typeof rawResponseData === "string" ? rawResponseData : void 0) ?? record?.error;
|
|
1919
1967
|
return typeof code === "string" && code.trim().toLowerCase() === "invalid_blocks";
|
|
1920
1968
|
}
|
|
1921
1969
|
function isSlackResponseLike(value) {
|
|
1922
|
-
const record =
|
|
1923
|
-
const body =
|
|
1970
|
+
const record = asOptionalRecord(value);
|
|
1971
|
+
const body = asOptionalRecord(record?.body);
|
|
1924
1972
|
return typeof record?.status === "number" && (typeof record.arrayBuffer === "function" || typeof body?.getReader === "function");
|
|
1925
1973
|
}
|
|
1926
1974
|
/** Consume Bolt 5's native response_url body under strict time and byte bounds. */
|
|
@@ -1937,12 +1985,12 @@ async function isSlackInvalidBlocksResponse(response) {
|
|
|
1937
1985
|
/** Bolt 5 omits the response body from RespondError; 400 is contextual here. */
|
|
1938
1986
|
function isSlackNativeResponseUrlRejection(error) {
|
|
1939
1987
|
if (isSlackInvalidBlocksError(error)) return true;
|
|
1940
|
-
const record =
|
|
1988
|
+
const record = asOptionalRecord(error);
|
|
1941
1989
|
return record?.code === "slack_bolt_respond_error" && record.statusCode === 400;
|
|
1942
1990
|
}
|
|
1943
1991
|
/** Extract a complete accessible summary from a supported native data block. */
|
|
1944
1992
|
function renderSlackNativeDataFallbackText(value) {
|
|
1945
|
-
const type =
|
|
1993
|
+
const type = asOptionalRecord(value)?.type;
|
|
1946
1994
|
if (type === "data_visualization") return renderSlackDataVisualizationMrkdwnFallbackText(value);
|
|
1947
1995
|
if (type === "data_table") return renderSlackDataTableMrkdwnFallbackText(value);
|
|
1948
1996
|
}
|
|
@@ -1984,7 +2032,7 @@ function appendSlackNativeDataFallback(text, blocks, render) {
|
|
|
1984
2032
|
return [base, ...dataTexts].filter(Boolean).join("\n\n");
|
|
1985
2033
|
}
|
|
1986
2034
|
function renderSlackNativeDataPlainTextBlock(value) {
|
|
1987
|
-
const type =
|
|
2035
|
+
const type = asOptionalRecord(value)?.type;
|
|
1988
2036
|
if (type === "data_table") return renderSlackDataTableCompactPlainTextFallback(value);
|
|
1989
2037
|
if (type === "data_visualization") return renderSlackDataVisualizationFallbackText(value);
|
|
1990
2038
|
}
|
|
@@ -2312,8 +2360,15 @@ function resolveSlackReplyBlocks(payload) {
|
|
|
2312
2360
|
return segments.length === 1 && segments[0]?.kind === "blocks" ? segments[0].blocks : void 0;
|
|
2313
2361
|
}
|
|
2314
2362
|
//#endregion
|
|
2363
|
+
//#region extensions/slack/src/detached-target-admission.ts
|
|
2364
|
+
function assertSlackDetachedTargetAllowed(accountId, teamId) {
|
|
2365
|
+
const installationKind = getSlackInstallationKind(accountId);
|
|
2366
|
+
if (installationKind && installationKind !== "workspace" && !teamId) throw new Error("unsupported_enterprise_slack_delivery: detached Slack operations require team:<team-id>:channel:<channel-id> or team:<team-id>:user:<user-id> until a workspace install is authenticated");
|
|
2367
|
+
}
|
|
2368
|
+
//#endregion
|
|
2315
2369
|
//#region extensions/slack/src/monitor/allow-list.ts
|
|
2316
2370
|
const SLACK_SLUG_CACHE_MAX = 512;
|
|
2371
|
+
const SLACK_STABLE_USER_ID_RE = /^[ubw][a-z0-9]+$/;
|
|
2317
2372
|
const slackSlugCache = /* @__PURE__ */ new Map();
|
|
2318
2373
|
function normalizeSlackSlug(raw) {
|
|
2319
2374
|
const key = raw ?? "";
|
|
@@ -2331,49 +2386,61 @@ function normalizeAllowList(list) {
|
|
|
2331
2386
|
return normalizeStringEntries$1(list);
|
|
2332
2387
|
}
|
|
2333
2388
|
function normalizeAllowListLower(list) {
|
|
2334
|
-
return normalizeStringEntriesLower
|
|
2389
|
+
return normalizeStringEntriesLower(list);
|
|
2335
2390
|
}
|
|
2336
2391
|
function normalizeSlackAllowOwnerEntry(entry) {
|
|
2337
2392
|
const trimmed = normalizeOptionalLowercaseString(entry);
|
|
2338
2393
|
if (!trimmed || trimmed === "*") return;
|
|
2394
|
+
try {
|
|
2395
|
+
const target = parseSlackTarget(trimmed);
|
|
2396
|
+
if (target?.kind === "user" && target.teamId) return target.id.toLowerCase();
|
|
2397
|
+
} catch {
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2339
2400
|
const withoutPrefix = trimmed.replace(/^(slack:|user:)/, "");
|
|
2340
|
-
return
|
|
2401
|
+
return SLACK_STABLE_USER_ID_RE.test(withoutPrefix) ? withoutPrefix : void 0;
|
|
2341
2402
|
}
|
|
2342
2403
|
function resolveSlackAllowListMatch(params) {
|
|
2343
2404
|
const compiledAllowList = compileAllowlist(params.allowList);
|
|
2405
|
+
const teamId = normalizeOptionalLowercaseString(params.teamId);
|
|
2344
2406
|
const id = normalizeOptionalLowercaseString(params.id);
|
|
2345
2407
|
const name = normalizeOptionalLowercaseString(params.name);
|
|
2346
2408
|
const slug = normalizeSlackSlug(name);
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2409
|
+
const scopedCandidates = [{
|
|
2410
|
+
value: teamId && id ? `team:${teamId}:user:${id}` : void 0,
|
|
2411
|
+
source: "workspace-id"
|
|
2412
|
+
}];
|
|
2413
|
+
const unscopedCandidates = [
|
|
2414
|
+
{
|
|
2415
|
+
value: id,
|
|
2416
|
+
source: "id"
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
value: id ? `slack:${id}` : void 0,
|
|
2420
|
+
source: "prefixed-id"
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
value: id ? `user:${id}` : void 0,
|
|
2424
|
+
source: "prefixed-user"
|
|
2425
|
+
},
|
|
2426
|
+
...params.allowNameMatching === true ? [
|
|
2350
2427
|
{
|
|
2351
|
-
value:
|
|
2352
|
-
source: "
|
|
2428
|
+
value: name,
|
|
2429
|
+
source: "name"
|
|
2353
2430
|
},
|
|
2354
2431
|
{
|
|
2355
|
-
value:
|
|
2356
|
-
source: "prefixed-
|
|
2432
|
+
value: name ? `slack:${name}` : void 0,
|
|
2433
|
+
source: "prefixed-name"
|
|
2357
2434
|
},
|
|
2358
2435
|
{
|
|
2359
|
-
value:
|
|
2360
|
-
source: "
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
{
|
|
2368
|
-
value: name ? `slack:${name}` : void 0,
|
|
2369
|
-
source: "prefixed-name"
|
|
2370
|
-
},
|
|
2371
|
-
{
|
|
2372
|
-
value: slug,
|
|
2373
|
-
source: "slug"
|
|
2374
|
-
}
|
|
2375
|
-
] : []
|
|
2376
|
-
]
|
|
2436
|
+
value: slug,
|
|
2437
|
+
source: "slug"
|
|
2438
|
+
}
|
|
2439
|
+
] : []
|
|
2440
|
+
];
|
|
2441
|
+
return resolveCompiledAllowlistMatch({
|
|
2442
|
+
compiledAllowlist: compiledAllowList,
|
|
2443
|
+
candidates: [...scopedCandidates, ...unscopedCandidates]
|
|
2377
2444
|
});
|
|
2378
2445
|
}
|
|
2379
2446
|
function allowListMatches(params) {
|
|
@@ -2384,18 +2451,41 @@ function resolveSlackUserAllowed(params) {
|
|
|
2384
2451
|
if (allowList.length === 0) return true;
|
|
2385
2452
|
return allowListMatches({
|
|
2386
2453
|
allowList,
|
|
2454
|
+
teamId: params.teamId,
|
|
2387
2455
|
id: params.userId,
|
|
2388
2456
|
name: params.userName,
|
|
2389
2457
|
allowNameMatching: params.allowNameMatching
|
|
2390
2458
|
});
|
|
2391
2459
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2460
|
+
function resolveSlackUserAllowListForTeam(params) {
|
|
2461
|
+
const allowList = normalizeAllowListLower(params.allowList);
|
|
2462
|
+
const teamId = normalizeOptionalLowercaseString(params.teamId);
|
|
2463
|
+
return allowList.flatMap((entry) => {
|
|
2464
|
+
if (entry === "*") return [entry];
|
|
2465
|
+
if (!entry.startsWith("team:")) return [entry];
|
|
2466
|
+
try {
|
|
2467
|
+
const target = parseSlackTarget(entry);
|
|
2468
|
+
if (target?.kind === "user" && target.teamId?.toLowerCase() === teamId) return [entry];
|
|
2469
|
+
return params.preserveUnmatchedScopedEntries ? [entry] : [];
|
|
2470
|
+
} catch {
|
|
2471
|
+
return params.preserveUnmatchedScopedEntries ? [entry] : [];
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2396
2474
|
}
|
|
2397
2475
|
//#endregion
|
|
2398
2476
|
//#region extensions/slack/src/group-policy.ts
|
|
2477
|
+
function buildSlackChannelIdCandidates(channelId, teamId, options) {
|
|
2478
|
+
const trimmedId = channelId?.trim();
|
|
2479
|
+
if (!trimmedId) return [];
|
|
2480
|
+
const lowercaseId = trimmedId.toLowerCase();
|
|
2481
|
+
const uppercaseId = trimmedId.toUpperCase();
|
|
2482
|
+
const exactTeamId = teamId || void 0;
|
|
2483
|
+
const lowercaseTeamId = exactTeamId?.toLowerCase();
|
|
2484
|
+
const uppercaseTeamId = exactTeamId?.toUpperCase();
|
|
2485
|
+
const scopedCandidates = buildChannelKeyCandidates(exactTeamId ? `team:${exactTeamId}:channel:${trimmedId}` : void 0, lowercaseTeamId ? `team:${lowercaseTeamId}:channel:${lowercaseId}` : void 0, uppercaseTeamId ? `team:${uppercaseTeamId}:channel:${uppercaseId}` : void 0);
|
|
2486
|
+
if (exactTeamId && options?.allowUnscoped !== true) return scopedCandidates;
|
|
2487
|
+
return buildChannelKeyCandidates(...scopedCandidates, trimmedId, lowercaseId, uppercaseId, `channel:${trimmedId}`, `channel:${lowercaseId}`, `channel:${uppercaseId}`);
|
|
2488
|
+
}
|
|
2399
2489
|
function buildSlackChannelPolicyScope(params) {
|
|
2400
2490
|
const channels = params.channels ?? {};
|
|
2401
2491
|
const tree = { scopes: channels };
|
|
@@ -2413,16 +2503,11 @@ function buildSlackChannelPolicyScope(params) {
|
|
|
2413
2503
|
function resolveSlackGroupPolicyScope(params) {
|
|
2414
2504
|
const accountId = normalizeAccountId(params.accountId ?? resolveDefaultSlackAccountId(params.cfg));
|
|
2415
2505
|
const channels = mergeSlackAccountConfig(params.cfg, accountId).channels;
|
|
2416
|
-
const channelId = params.groupId?.trim();
|
|
2417
2506
|
const channelName = params.groupChannel?.replace(/^#/, "");
|
|
2507
|
+
const allowUnscoped = getSlackInstallationKind(accountId) !== "enterprise";
|
|
2418
2508
|
return buildSlackChannelPolicyScope({
|
|
2419
2509
|
channels,
|
|
2420
|
-
candidates:
|
|
2421
|
-
channelId,
|
|
2422
|
-
channelName ? `#${channelName}` : void 0,
|
|
2423
|
-
channelName,
|
|
2424
|
-
normalizeHyphenSlug(channelName)
|
|
2425
|
-
].filter((candidate) => Boolean(candidate))
|
|
2510
|
+
candidates: buildChannelKeyCandidates(...buildSlackChannelIdCandidates(params.groupId, params.groupSpace, { allowUnscoped }), channelName ? `#${channelName}` : void 0, channelName, normalizeHyphenSlug(channelName))
|
|
2426
2511
|
});
|
|
2427
2512
|
}
|
|
2428
2513
|
function resolveSlackGroupRequireMention(params) {
|
|
@@ -2439,4 +2524,4 @@ function resolveSlackGroupToolPolicy(params) {
|
|
|
2439
2524
|
});
|
|
2440
2525
|
}
|
|
2441
2526
|
//#endregion
|
|
2442
|
-
export {
|
|
2527
|
+
export { isSlackCallbackActionId as $, chunkSlackMrkdwnText as A, normalizeSlackApproverId as At, SLACK_TEXT_LIMIT as B, buildSlackNativeDataAccessibilityText as C, isSlackWorkspaceInstallation as Ct, isSlackInvalidBlocksResponse as D, getSlackExecApprovalApprovers as Dt, isSlackInvalidBlocksError as E, isSlackApprovalAuthorizedSender as Et, renderSlackBlockFallbackText as F, truncateSlackText as G, buildSlackPresentationBlocks as H, resolveSlackAuthoredTextPlacement as I, SLACK_REPLY_BUTTON_ACTION_ID as J, truncateSlackTextByUtf8Bytes as K, SLACK_EDIT_TEXT_MAX_BYTES as L, normalizeSlackOutboundText as M, shouldSuppressLocalSlackExecApprovalPrompt as Mt, buildSlackBlocksFallbackText as N, isSlackNativeResponseUrlRejection as O, isSlackExecApprovalAuthorizedSender as Ot, buildSlackCompleteBlocksFallbackText as P, isSlackApprovalActionId as Q, SLACK_MESSAGE_TEXT_HARD_LIMIT as R, appendSlackNativeDataPlainTextFallback as S, getSlackInstallationKind as St, hasSlackNativeDataBlock as T, getSlackApprovalApprovers as Tt, canRenderSlackPresentation as U, buildSlackInteractiveBlocks as V, countSlackTextUtf8Bytes as W, SLACK_REPLY_SELECT_ACTION_ID as X, SLACK_REPLY_LINK_ACTION_ID as Y, SLACK_SESSION_LINK_ACTION_ID as Z, resolveSlackReplyBlocks as _, resolveSlackFallbackOriginTarget as _t, allowListMatches as a, escapeSlackMrkdwn as at, SLACK_MALFORMED_NATIVE_DATA_FALLBACK as b, shouldHandleSlackPluginViaForwardingSession as bt, normalizeSlackAllowOwnerEntry as c, SLACK_MAX_BLOCKS as ct, resolveSlackUserAllowListForTeam as d, hasSlackPluginApprovers as dt, isSlackQuestionActionId as et, resolveSlackUserAllowed as f, isSlackAnyNativeApprovalClientEnabled as ft, resolveSlackReplyBlockResolution as g, resolveSessionSlackOriginTarget as gt, parseSlackReplyBlockSegments as h, resolveEnterpriseApprovalTeamId as ht, resolveSlackGroupToolPolicy as i, renderSlackMessagePresentationFallbackText as it, markdownToSlackMrkdwnChunks as j, resolveSlackExecApprovalTarget as jt, stripSlackNativeDataBlocks as k, isSlackExecApprovalClientEnabled as kt, normalizeSlackSlug as l, parseSlackBlocksInput as lt, hasSlackReplyStructuredContent as m, normalizeSlackOriginTarget as mt, buildSlackChannelPolicyScope as n, decodeSlackQuestionAction as nt, normalizeAllowList as o, decodeSlackApprovalAction as ot, assertSlackDetachedTargetAllowed as p, normalizeSlackForwardTarget as pt, SLACK_QUESTION_FINALIZATION_BLOCKS as q, resolveSlackGroupRequireMention as r, resolveSlackQuestionAction as rt, normalizeAllowListLower as s, SLACK_PRESENTATION_CAPABILITIES as st, buildSlackChannelIdCandidates as t, resolveSlackQuestionActionIds as tt, resolveSlackAllowListMatch as u, validateSlackBlocksArray as ut, resolveSlackReplyDeliveryMessages as v, resolveTurnSourceSlackOriginTarget as vt, createSlackNativeDataBaseTextConsumer as w, registerSlackInstallationState as wt, appendSlackNativeDataFallbackText as x, slackTargetsMatch as xt, resolveSlackReplyRenderPlan as y, shouldHandleSlackNativeApprovalRequest as yt, SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT as z };
|