@openclaw/slack 2026.5.12-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/account-inspect-D7AZNs8C.js +77 -0
  2. package/dist/account-inspect-api.js +10 -0
  3. package/dist/accounts-ClAPP5ry.js +139 -0
  4. package/dist/accounts.runtime-DDVcLJUI.js +2 -0
  5. package/dist/action-runtime-e2UhRsNx.js +350 -0
  6. package/dist/action-runtime.runtime-BFcqMbOm.js +2 -0
  7. package/dist/actions-CYLFK-Zy.js +292 -0
  8. package/dist/actions.runtime-CO3OaTLb.js +2 -0
  9. package/dist/allow-list-BPnnlRPL.js +82 -0
  10. package/dist/api.js +21 -0
  11. package/dist/approval-handler.runtime-CmeRr9qA.js +256 -0
  12. package/dist/blocks-input-CwTFVImV.js +29 -0
  13. package/dist/blocks-render-BIDw-Pom.js +161 -0
  14. package/dist/channel-DRjHBTDB.js +1020 -0
  15. package/dist/channel-api-B_nZwosg.js +20 -0
  16. package/dist/channel-config-api.js +2 -0
  17. package/dist/channel-entry.js +22 -0
  18. package/dist/channel-plugin-api.js +2 -0
  19. package/dist/channel.setup-Cayn7afd.js +73 -0
  20. package/dist/client-CPe4GmDR.js +103 -0
  21. package/dist/config-api-B_jq4NJW.js +2 -0
  22. package/dist/config-schema-D9B5LB_L.js +167 -0
  23. package/dist/configured-state.js +11 -0
  24. package/dist/contract-api.js +5 -0
  25. package/dist/directory-config-B3JiHeB7.js +54 -0
  26. package/dist/directory-contract-api.js +2 -0
  27. package/dist/directory-live-Bf16GwDh.js +133 -0
  28. package/dist/doctor-contract-KUjHnkQm.js +147 -0
  29. package/dist/doctor-contract-api.js +2 -0
  30. package/dist/errors-BYFHR24f.js +109 -0
  31. package/dist/exec-approvals-7xUNgLi9.js +58 -0
  32. package/dist/group-policy-CyLUK6My.js +41 -0
  33. package/dist/http-routes-api.js +2 -0
  34. package/dist/inbound-contract-test-api.js +3 -0
  35. package/dist/index.js +33 -0
  36. package/dist/interactive-replies-api.js +2 -0
  37. package/dist/interactive-replies-qAIfuBor.js +173 -0
  38. package/dist/magic-string.es-BMaGRRZ1.js +1011 -0
  39. package/dist/media-D1XCd1uP.js +469 -0
  40. package/dist/message-tool-api-6lowf9zE.js +104 -0
  41. package/dist/message-tool-api.js +2 -0
  42. package/dist/monitor-a97o17G6.js +13 -0
  43. package/dist/mrkdwn-Cax-eSfK.js +6 -0
  44. package/dist/outbound-adapter-B_5sEhCg.js +174 -0
  45. package/dist/outbound-payload-test-api.js +2 -0
  46. package/dist/outbound-payload.test-harness-CVCamg1x.js +13558 -0
  47. package/dist/pipeline.runtime-DT0hLnq2.js +1379 -0
  48. package/dist/plugin-routes-DtTPmga1.js +20 -0
  49. package/dist/prepare-D3YqV8jB.js +1482 -0
  50. package/dist/prepare.test-helpers-DVcjRhfG.js +49 -0
  51. package/dist/probe-3eZf1FjI.js +42 -0
  52. package/dist/provider-D7uAN3Fq.js +3235 -0
  53. package/dist/registry-CeaoNfoP.js +39 -0
  54. package/dist/replies-Xe_jMR6o.js +139 -0
  55. package/dist/reply-blocks-Z5l6_R6H.js +14 -0
  56. package/dist/resolve-allowlist-common-Bk3clYPK.js +43 -0
  57. package/dist/resolve-channels-BRYqyNVJ.js +81 -0
  58. package/dist/resolve-users-Bd_SdP8j.js +113 -0
  59. package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
  60. package/dist/room-context-0vovmZPU.js +787 -0
  61. package/dist/runtime-Bo-KHM-F.js +8 -0
  62. package/dist/runtime-api-Dd1xIV5v.js +9 -0
  63. package/dist/runtime-api.js +14 -0
  64. package/dist/runtime-setter-api.js +2 -0
  65. package/dist/scopes-CDevO8jg.js +74 -0
  66. package/dist/secret-contract-Bo6lbSkh.js +141 -0
  67. package/dist/secret-contract-api.js +2 -0
  68. package/dist/security-audit-BtHGnD3d.js +51 -0
  69. package/dist/security-contract-api.js +2 -0
  70. package/dist/send-D_A9kL-C.js +721 -0
  71. package/dist/send.runtime-BRE_ncCU.js +2 -0
  72. package/dist/send.runtime-_l76lUuL.js +2 -0
  73. package/dist/setup-core-B9NetDkM.js +320 -0
  74. package/dist/setup-entry.js +15 -0
  75. package/dist/setup-plugin-api.js +2 -0
  76. package/dist/setup-surface-D88QBVOW.js +128 -0
  77. package/dist/shared-D8U42xFL.js +208 -0
  78. package/dist/slash-commands.runtime-22kgyst2.js +19 -0
  79. package/dist/slash-dispatch.runtime-BJgT0jwV.js +32 -0
  80. package/dist/slash-plugin-commands.runtime-CF-n3MeP.js +2 -0
  81. package/dist/slash-skill-commands.runtime-BMs0VjTe.js +7 -0
  82. package/dist/streaming-compat-RkZgTmQ2.js +43 -0
  83. package/dist/target-parsing-CQmv-iSm.js +55 -0
  84. package/dist/targets-B1tYCAr6.js +2 -0
  85. package/dist/test-api.js +8 -0
  86. package/dist/thread-ts-C2x7c5PP.js +24 -0
  87. package/openclaw.plugin.json +2405 -0
  88. package/package.json +84 -0
@@ -0,0 +1,2 @@
1
+ import "./send-D_A9kL-C.js";
2
+ export {};
@@ -0,0 +1,2 @@
1
+ import { t as sendMessageSlack } from "./send-D_A9kL-C.js";
2
+ export { sendMessageSlack };
@@ -0,0 +1,320 @@
1
+ import { a as resolveSlackAccount } from "./accounts-ClAPP5ry.js";
2
+ import { t as inspectSlackAccount } from "./account-inspect-D7AZNs8C.js";
3
+ import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
4
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { DEFAULT_ACCOUNT_ID, createAccountScopedAllowFromSection, createAccountScopedGroupAccessSection, createAllowlistSetupWizardProxy, createEnvPatchedAccountSetupAdapter, createLegacyCompatChannelDmPolicy, createStandardChannelSetupStatus, parseMentionOrPrefixedId, patchChannelConfigForAccount, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup-runtime";
6
+ import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
7
+ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
8
+ //#region extensions/slack/src/setup-shared.ts
9
+ const SLACK_CHANNEL = "slack";
10
+ function buildSlackManifest(botName = "OpenClaw") {
11
+ const safeName = botName.trim() || "OpenClaw";
12
+ const manifest = {
13
+ display_information: {
14
+ name: safeName,
15
+ description: `${safeName} connector for OpenClaw`
16
+ },
17
+ features: {
18
+ bot_user: {
19
+ display_name: safeName,
20
+ always_online: true
21
+ },
22
+ app_home: {
23
+ home_tab_enabled: true,
24
+ messages_tab_enabled: true,
25
+ messages_tab_read_only_enabled: false
26
+ },
27
+ slash_commands: [{
28
+ command: "/openclaw",
29
+ description: "Send a message to OpenClaw",
30
+ should_escape: false
31
+ }]
32
+ },
33
+ oauth_config: { scopes: { bot: [
34
+ "app_mentions:read",
35
+ "assistant:write",
36
+ "channels:history",
37
+ "channels:read",
38
+ "chat:write",
39
+ "commands",
40
+ "emoji:read",
41
+ "files:read",
42
+ "files:write",
43
+ "groups:history",
44
+ "groups:read",
45
+ "im:history",
46
+ "im:read",
47
+ "im:write",
48
+ "mpim:history",
49
+ "mpim:read",
50
+ "mpim:write",
51
+ "pins:read",
52
+ "pins:write",
53
+ "reactions:read",
54
+ "reactions:write",
55
+ "usergroups:read",
56
+ "users:read"
57
+ ] } },
58
+ settings: {
59
+ socket_mode_enabled: true,
60
+ event_subscriptions: { bot_events: [
61
+ "app_home_opened",
62
+ "app_mention",
63
+ "channel_rename",
64
+ "member_joined_channel",
65
+ "member_left_channel",
66
+ "message.channels",
67
+ "message.groups",
68
+ "message.im",
69
+ "message.mpim",
70
+ "pin_added",
71
+ "pin_removed",
72
+ "reaction_added",
73
+ "reaction_removed"
74
+ ] }
75
+ }
76
+ };
77
+ return JSON.stringify(manifest, null, 2);
78
+ }
79
+ function buildSlackSetupLines() {
80
+ return [
81
+ "1) Slack API -> Create App -> From scratch or From manifest (with the JSON below)",
82
+ "2) Add Socket Mode + enable it to get the app-level token (xapp-...)",
83
+ "3) Install App to workspace to get the xoxb- bot token",
84
+ "4) Enable Event Subscriptions (socket) for message and App Home events",
85
+ "5) App Home -> enable the Home tab and Messages tab for DMs",
86
+ "Manifest JSON follows as plain text for copy/paste.",
87
+ "Tip: set SLACK_BOT_TOKEN + SLACK_APP_TOKEN in your env.",
88
+ `Docs: ${formatDocsLink("/slack", "slack")}`
89
+ ];
90
+ }
91
+ function setSlackChannelAllowlist(cfg, accountId, channelKeys) {
92
+ return patchChannelConfigForAccount({
93
+ cfg,
94
+ channel: SLACK_CHANNEL,
95
+ accountId,
96
+ patch: { channels: Object.fromEntries(channelKeys.map((key) => [key, { enabled: true }])) }
97
+ });
98
+ }
99
+ function isSlackSetupAccountConfigured(account) {
100
+ const hasConfiguredBotToken = Boolean(account.botToken?.trim()) || hasConfiguredSecretInput(account.config.botToken);
101
+ const hasConfiguredAppToken = Boolean(account.appToken?.trim()) || hasConfiguredSecretInput(account.config.appToken);
102
+ return hasConfiguredBotToken && hasConfiguredAppToken;
103
+ }
104
+ function describeSlackSetupAccount(account) {
105
+ return describeAccountSnapshot({
106
+ account,
107
+ configured: isSlackSetupAccountConfigured(account),
108
+ extra: {
109
+ botTokenSource: account.botTokenSource,
110
+ appTokenSource: account.appTokenSource
111
+ }
112
+ });
113
+ }
114
+ //#endregion
115
+ //#region extensions/slack/src/setup-core.ts
116
+ function enableSlackAccount(cfg, accountId) {
117
+ return patchChannelConfigForAccount({
118
+ cfg,
119
+ channel: SLACK_CHANNEL,
120
+ accountId,
121
+ patch: { enabled: true }
122
+ });
123
+ }
124
+ function hasSlackInteractiveRepliesConfig(cfg, accountId) {
125
+ const capabilities = resolveSlackAccount({
126
+ cfg,
127
+ accountId
128
+ }).config.capabilities;
129
+ if (Array.isArray(capabilities)) return capabilities.some((entry) => normalizeLowercaseStringOrEmpty(entry) === "interactivereplies");
130
+ if (!capabilities || typeof capabilities !== "object") return false;
131
+ return "interactiveReplies" in capabilities;
132
+ }
133
+ function setSlackInteractiveReplies(cfg, accountId, interactiveReplies) {
134
+ const capabilities = resolveSlackAccount({
135
+ cfg,
136
+ accountId
137
+ }).config.capabilities;
138
+ return patchChannelConfigForAccount({
139
+ cfg,
140
+ channel: SLACK_CHANNEL,
141
+ accountId,
142
+ patch: { capabilities: Array.isArray(capabilities) ? interactiveReplies ? [...new Set([...capabilities, "interactiveReplies"])] : capabilities.filter((entry) => normalizeLowercaseStringOrEmpty(entry) !== "interactivereplies") : {
143
+ ...capabilities && typeof capabilities === "object" ? capabilities : {},
144
+ interactiveReplies
145
+ } }
146
+ });
147
+ }
148
+ function createSlackTokenCredential(params) {
149
+ return {
150
+ inputKey: params.inputKey,
151
+ providerHint: params.providerHint,
152
+ credentialLabel: params.credentialLabel,
153
+ preferredEnvVar: params.preferredEnvVar,
154
+ envPrompt: `${params.preferredEnvVar} detected. Use env var?`,
155
+ keepPrompt: params.keepPrompt,
156
+ inputPrompt: params.inputPrompt,
157
+ allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID,
158
+ inspect: ({ cfg, accountId }) => {
159
+ const resolved = resolveSlackAccount({
160
+ cfg,
161
+ accountId
162
+ });
163
+ const configuredValue = params.inputKey === "botToken" ? resolved.config.botToken : resolved.config.appToken;
164
+ const resolvedValue = params.inputKey === "botToken" ? resolved.botToken : resolved.appToken;
165
+ return {
166
+ accountConfigured: Boolean(resolvedValue) || hasConfiguredSecretInput(configuredValue),
167
+ hasConfiguredValue: hasConfiguredSecretInput(configuredValue),
168
+ resolvedValue: normalizeOptionalString(resolvedValue),
169
+ envValue: accountId === DEFAULT_ACCOUNT_ID ? normalizeOptionalString(process.env[params.preferredEnvVar]) : void 0
170
+ };
171
+ },
172
+ applyUseEnv: ({ cfg, accountId }) => enableSlackAccount(cfg, accountId),
173
+ applySet: ({ cfg, accountId, value }) => patchChannelConfigForAccount({
174
+ cfg,
175
+ channel: SLACK_CHANNEL,
176
+ accountId,
177
+ patch: {
178
+ enabled: true,
179
+ [params.inputKey]: value
180
+ }
181
+ })
182
+ };
183
+ }
184
+ const slackSetupAdapter = createEnvPatchedAccountSetupAdapter({
185
+ channelKey: SLACK_CHANNEL,
186
+ defaultAccountOnlyEnvError: "Slack env tokens can only be used for the default account.",
187
+ missingCredentialError: "Slack requires --bot-token and --app-token (or --use-env).",
188
+ hasCredentials: (input) => Boolean(input.botToken && input.appToken),
189
+ buildPatch: (input) => ({
190
+ ...input.botToken ? { botToken: input.botToken } : {},
191
+ ...input.appToken ? { appToken: input.appToken } : {}
192
+ })
193
+ });
194
+ function createSlackSetupWizardBase(handlers) {
195
+ const slackDmPolicy = createLegacyCompatChannelDmPolicy({
196
+ label: "Slack",
197
+ channel: SLACK_CHANNEL,
198
+ promptAllowFrom: handlers.promptAllowFrom
199
+ });
200
+ return {
201
+ channel: SLACK_CHANNEL,
202
+ status: createStandardChannelSetupStatus({
203
+ channelLabel: "Slack",
204
+ configuredLabel: "configured",
205
+ unconfiguredLabel: "needs tokens",
206
+ configuredHint: "configured",
207
+ unconfiguredHint: "needs tokens",
208
+ configuredScore: 2,
209
+ unconfiguredScore: 1,
210
+ resolveConfigured: ({ cfg, accountId }) => inspectSlackAccount({
211
+ cfg,
212
+ accountId
213
+ }).configured
214
+ }),
215
+ introNote: {
216
+ title: "Slack socket mode tokens",
217
+ lines: buildSlackSetupLines(),
218
+ shouldShow: ({ cfg, accountId }) => !isSlackSetupAccountConfigured(resolveSlackAccount({
219
+ cfg,
220
+ accountId
221
+ }))
222
+ },
223
+ prepare: async ({ cfg, accountId, prompter }) => {
224
+ if (isSlackSetupAccountConfigured(resolveSlackAccount({
225
+ cfg,
226
+ accountId
227
+ }))) return;
228
+ const manifest = buildSlackManifest();
229
+ if (prompter.plain) await prompter.plain(manifest);
230
+ else await prompter.note(manifest, "Slack manifest JSON");
231
+ },
232
+ envShortcut: {
233
+ prompt: "SLACK_BOT_TOKEN + SLACK_APP_TOKEN detected. Use env vars?",
234
+ preferredEnvVar: "SLACK_BOT_TOKEN",
235
+ isAvailable: ({ cfg, accountId }) => accountId === DEFAULT_ACCOUNT_ID && Boolean(process.env.SLACK_BOT_TOKEN?.trim()) && Boolean(process.env.SLACK_APP_TOKEN?.trim()) && !isSlackSetupAccountConfigured(resolveSlackAccount({
236
+ cfg,
237
+ accountId
238
+ })),
239
+ apply: ({ cfg, accountId }) => enableSlackAccount(cfg, accountId)
240
+ },
241
+ credentials: [createSlackTokenCredential({
242
+ inputKey: "botToken",
243
+ providerHint: "slack-bot",
244
+ credentialLabel: "Slack bot token",
245
+ preferredEnvVar: "SLACK_BOT_TOKEN",
246
+ keepPrompt: "Slack bot token already configured. Keep it?",
247
+ inputPrompt: "Enter Slack bot token (xoxb-...)"
248
+ }), createSlackTokenCredential({
249
+ inputKey: "appToken",
250
+ providerHint: "slack-app",
251
+ credentialLabel: "Slack app token",
252
+ preferredEnvVar: "SLACK_APP_TOKEN",
253
+ keepPrompt: "Slack app token already configured. Keep it?",
254
+ inputPrompt: "Enter Slack app token (xapp-...)"
255
+ })],
256
+ dmPolicy: slackDmPolicy,
257
+ allowFrom: createAccountScopedAllowFromSection({
258
+ channel: SLACK_CHANNEL,
259
+ credentialInputKey: "botToken",
260
+ helpTitle: "Slack allowlist",
261
+ helpLines: [
262
+ "Allowlist Slack DMs by username (we resolve to user ids).",
263
+ "Examples:",
264
+ "- U12345678",
265
+ "- @alice",
266
+ "Multiple entries: comma-separated.",
267
+ `Docs: ${formatDocsLink("/slack", "slack")}`
268
+ ],
269
+ message: "Slack allowFrom (usernames or ids)",
270
+ placeholder: "@alice, U12345678",
271
+ invalidWithoutCredentialNote: "Slack token missing; use user ids (or mention form) only.",
272
+ parseId: (value) => parseMentionOrPrefixedId({
273
+ value,
274
+ mentionPattern: /^<@([A-Z0-9]+)>$/i,
275
+ prefixPattern: /^(slack:|user:)/i,
276
+ idPattern: /^[A-Z][A-Z0-9]+$/i,
277
+ normalizeId: (id) => id.toUpperCase()
278
+ }),
279
+ resolveEntries: handlers.resolveAllowFromEntries
280
+ }),
281
+ groupAccess: createAccountScopedGroupAccessSection({
282
+ channel: SLACK_CHANNEL,
283
+ label: "Slack channels",
284
+ placeholder: "#general, #private, C123",
285
+ currentPolicy: ({ cfg, accountId }) => resolveSlackAccount({
286
+ cfg,
287
+ accountId
288
+ }).config.groupPolicy ?? "allowlist",
289
+ currentEntries: ({ cfg, accountId }) => Object.entries(resolveSlackAccount({
290
+ cfg,
291
+ accountId
292
+ }).config.channels ?? {}).filter(([, value]) => value?.enabled !== false).map(([key]) => key),
293
+ updatePrompt: ({ cfg, accountId }) => Boolean(resolveSlackAccount({
294
+ cfg,
295
+ accountId
296
+ }).config.channels),
297
+ resolveAllowlist: handlers.resolveGroupAllowlist,
298
+ fallbackResolved: (entries) => entries,
299
+ applyAllowlist: ({ cfg, accountId, resolved }) => setSlackChannelAllowlist(cfg, accountId, resolved)
300
+ }),
301
+ finalize: async ({ cfg, accountId, options, prompter }) => {
302
+ if (hasSlackInteractiveRepliesConfig(cfg, accountId)) return;
303
+ if (options?.quickstartDefaults) return { cfg: setSlackInteractiveReplies(cfg, accountId, true) };
304
+ return { cfg: setSlackInteractiveReplies(cfg, accountId, await prompter.confirm({
305
+ message: "Enable Slack interactive replies (buttons/selects) for agent responses?",
306
+ initialValue: true
307
+ })) };
308
+ },
309
+ disable: (cfg) => setSetupChannelEnabled(cfg, SLACK_CHANNEL, false)
310
+ };
311
+ }
312
+ function createSlackSetupWizardProxy(loadWizard) {
313
+ return createAllowlistSetupWizardProxy({
314
+ loadWizard: async () => (await loadWizard()).slackSetupWizard,
315
+ createBase: createSlackSetupWizardBase,
316
+ fallbackResolvedGroupAllowlist: (entries) => entries
317
+ });
318
+ }
319
+ //#endregion
320
+ export { describeSlackSetupAccount as a, SLACK_CHANNEL as i, createSlackSetupWizardProxy as n, isSlackSetupAccountConfigured as o, slackSetupAdapter as r, createSlackSetupWizardBase as t };
@@ -0,0 +1,15 @@
1
+ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
2
+ //#region extensions/slack/setup-entry.ts
3
+ var setup_entry_default = defineBundledChannelSetupEntry({
4
+ importMetaUrl: import.meta.url,
5
+ plugin: {
6
+ specifier: "./setup-plugin-api.js",
7
+ exportName: "slackSetupPlugin"
8
+ },
9
+ secrets: {
10
+ specifier: "./secret-contract-api.js",
11
+ exportName: "channelSecrets"
12
+ }
13
+ });
14
+ //#endregion
15
+ export { setup_entry_default as default };
@@ -0,0 +1,2 @@
1
+ import { t as slackSetupPlugin } from "./channel.setup-Cayn7afd.js";
2
+ export { slackSetupPlugin };
@@ -0,0 +1,128 @@
1
+ import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, o as resolveSlackAccountAllowFrom } from "./accounts-ClAPP5ry.js";
2
+ import "./shared-D8U42xFL.js";
3
+ import { i as SLACK_CHANNEL, t as createSlackSetupWizardBase } from "./setup-core-B9NetDkM.js";
4
+ import { t as resolveSlackChannelAllowlist } from "./resolve-channels-BRYqyNVJ.js";
5
+ import { t as resolveSlackUserAllowlist } from "./resolve-users-Bd_SdP8j.js";
6
+ import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers";
7
+ import { noteChannelLookupFailure, noteChannelLookupSummary, parseMentionOrPrefixedId, promptLegacyChannelAllowFromForAccount, resolveEntriesWithOptionalToken } from "openclaw/plugin-sdk/setup-runtime";
8
+ import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
9
+ //#region extensions/slack/src/setup-surface.ts
10
+ async function resolveSlackAllowFromEntries(params) {
11
+ return await resolveEntriesWithOptionalToken({
12
+ token: params.token,
13
+ entries: params.entries,
14
+ buildWithoutToken: (input) => ({
15
+ input,
16
+ resolved: false,
17
+ id: null
18
+ }),
19
+ resolveEntries: async ({ token, entries }) => (await resolveSlackUserAllowlist({
20
+ token,
21
+ entries
22
+ })).map((entry) => ({
23
+ input: entry.input,
24
+ resolved: entry.resolved,
25
+ id: entry.id ?? null
26
+ }))
27
+ });
28
+ }
29
+ async function promptSlackAllowFrom(params) {
30
+ const parseId = (value) => parseMentionOrPrefixedId({
31
+ value,
32
+ mentionPattern: /^<@([A-Z0-9]+)>$/i,
33
+ prefixPattern: /^(slack:|user:)/i,
34
+ idPattern: /^[A-Z][A-Z0-9]+$/i,
35
+ normalizeId: (id) => id.toUpperCase()
36
+ });
37
+ return await promptLegacyChannelAllowFromForAccount({
38
+ cfg: params.cfg,
39
+ channel: SLACK_CHANNEL,
40
+ prompter: params.prompter,
41
+ accountId: params.accountId,
42
+ defaultAccountId: resolveDefaultSlackAccountId(params.cfg),
43
+ resolveAccount: adaptScopedAccountAccessor(resolveSlackAccount),
44
+ resolveExisting: (account, cfg) => resolveSlackAccountAllowFrom({
45
+ cfg,
46
+ accountId: account.accountId
47
+ }) ?? [],
48
+ resolveToken: (account) => account.userToken ?? account.botToken ?? "",
49
+ noteTitle: "Slack allowlist",
50
+ noteLines: [
51
+ "Allowlist Slack DMs by username (we resolve to user ids).",
52
+ "Examples:",
53
+ "- U12345678",
54
+ "- @alice",
55
+ "Multiple entries: comma-separated.",
56
+ `Docs: ${formatDocsLink("/slack", "slack")}`
57
+ ],
58
+ message: "Slack allowFrom (usernames or ids)",
59
+ placeholder: "@alice, U12345678",
60
+ parseId,
61
+ invalidWithoutTokenNote: "Slack token missing; use user ids (or mention form) only.",
62
+ resolveEntries: async ({ token, entries }) => (await resolveSlackUserAllowlist({
63
+ token,
64
+ entries
65
+ })).map((entry) => ({
66
+ input: entry.input,
67
+ resolved: entry.resolved,
68
+ id: entry.id ?? null
69
+ }))
70
+ });
71
+ }
72
+ async function resolveSlackGroupAllowlist(params) {
73
+ let keys = params.entries;
74
+ const activeBotToken = resolveSlackAccount({
75
+ cfg: params.cfg,
76
+ accountId: params.accountId
77
+ }).botToken || params.credentialValues.botToken || "";
78
+ if (params.entries.length > 0) try {
79
+ const resolved = await resolveEntriesWithOptionalToken({
80
+ token: activeBotToken,
81
+ entries: params.entries,
82
+ buildWithoutToken: (input) => ({
83
+ input,
84
+ resolved: false,
85
+ id: void 0
86
+ }),
87
+ resolveEntries: async ({ token, entries }) => await resolveSlackChannelAllowlist({
88
+ token,
89
+ entries
90
+ })
91
+ });
92
+ const resolvedKeys = resolved.filter((entry) => entry.resolved && entry.id).map((entry) => entry.id);
93
+ const unresolved = resolved.filter((entry) => !entry.resolved).map((entry) => entry.input);
94
+ keys = [...resolvedKeys, ...unresolved.map((entry) => entry.trim()).filter(Boolean)];
95
+ await noteChannelLookupSummary({
96
+ prompter: params.prompter,
97
+ label: "Slack channels",
98
+ resolvedSections: [{
99
+ title: "Resolved",
100
+ values: resolvedKeys
101
+ }],
102
+ unresolved
103
+ });
104
+ } catch (error) {
105
+ await noteChannelLookupFailure({
106
+ prompter: params.prompter,
107
+ label: "Slack channels",
108
+ error
109
+ });
110
+ }
111
+ return keys;
112
+ }
113
+ const slackSetupWizard = createSlackSetupWizardBase({
114
+ promptAllowFrom: promptSlackAllowFrom,
115
+ resolveAllowFromEntries: async ({ credentialValues, entries }) => await resolveSlackAllowFromEntries({
116
+ token: credentialValues.botToken,
117
+ entries
118
+ }),
119
+ resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries, prompter }) => await resolveSlackGroupAllowlist({
120
+ cfg,
121
+ accountId,
122
+ credentialValues,
123
+ entries,
124
+ prompter
125
+ })
126
+ });
127
+ //#endregion
128
+ export { slackSetupWizard };
@@ -0,0 +1,208 @@
1
+ import { a as resolveSlackAccount, c as resolveSlackConfigAccessorAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy } from "./accounts-ClAPP5ry.js";
2
+ import { t as inspectSlackAccount } from "./account-inspect-D7AZNs8C.js";
3
+ import { n as isSlackInteractiveRepliesEnabled } from "./interactive-replies-qAIfuBor.js";
4
+ import { r as getChatChannelMeta } from "./channel-api-B_nZwosg.js";
5
+ import { i as SLACK_CHANNEL } from "./setup-core-B9NetDkM.js";
6
+ import { t as SlackChannelConfigSchema } from "./config-schema-D9B5LB_L.js";
7
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-KUjHnkQm.js";
8
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Bo6lbSkh.js";
9
+ import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
10
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
11
+ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
12
+ import { createDangerousNameMatchingMutableAllowlistWarningCollector, createOpenProviderConfiguredRouteWarningCollector } from "openclaw/plugin-sdk/channel-policy";
13
+ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
14
+ //#region extensions/slack/src/security.ts
15
+ const resolveSlackDmPolicy = createScopedDmSecurityResolver({
16
+ channelKey: "slack",
17
+ resolvePolicy: (account) => account.config.dmPolicy,
18
+ resolveAllowFrom: (account) => account.config.allowFrom,
19
+ resolveAccess: ({ cfg, account }) => ({
20
+ dmPolicy: resolveSlackAccountDmPolicy({
21
+ cfg,
22
+ accountId: account.accountId
23
+ }),
24
+ allowFrom: resolveSlackAccountAllowFrom({
25
+ cfg,
26
+ accountId: account.accountId
27
+ })
28
+ }),
29
+ policyPathSuffix: "dmPolicy",
30
+ normalizeEntry: (raw) => raw.trim().replace(/^(slack|user):/i, "").trim()
31
+ });
32
+ const collectSlackSecurityWarnings = createOpenProviderConfiguredRouteWarningCollector({
33
+ providerConfigPresent: (cfg) => cfg.channels?.slack !== void 0,
34
+ resolveGroupPolicy: (account) => account.config.groupPolicy,
35
+ resolveRouteAllowlistConfigured: (account) => Boolean(account.config.channels) && Object.keys(account.config.channels ?? {}).length > 0,
36
+ configureRouteAllowlist: {
37
+ surface: "Slack channels",
38
+ openScope: "any channel not explicitly denied",
39
+ groupPolicyPath: "channels.slack.groupPolicy",
40
+ routeAllowlistPath: "channels.slack.channels"
41
+ },
42
+ missingRouteAllowlist: {
43
+ surface: "Slack channels",
44
+ openBehavior: "with no channel allowlist; any channel can trigger (mention-gated)",
45
+ remediation: "Set channels.slack.groupPolicy=\"allowlist\" and configure channels.slack.channels"
46
+ }
47
+ });
48
+ const loadSlackSecurityAuditModule = createLazyRuntimeModule(() => import("./security-audit-BtHGnD3d.js").then((n) => n.n));
49
+ const slackSecurityAdapter = {
50
+ resolveDmPolicy: resolveSlackDmPolicy,
51
+ collectWarnings: collectSlackSecurityWarnings,
52
+ collectAuditFindings: async (params) => {
53
+ const { collectSlackSecurityAuditFindings } = await loadSlackSecurityAuditModule();
54
+ return await collectSlackSecurityAuditFindings(params);
55
+ }
56
+ };
57
+ //#endregion
58
+ //#region extensions/slack/src/security-doctor.ts
59
+ function isSlackMutableAllowEntry(raw) {
60
+ const text = raw.trim();
61
+ if (!text || text === "*") return false;
62
+ const mentionMatch = text.match(/^<@([A-Z0-9]+)>$/i);
63
+ if (mentionMatch && /^[A-Z0-9]{8,}$/i.test(mentionMatch[1] ?? "")) return false;
64
+ const withoutPrefix = text.replace(/^(slack|user):/i, "").trim();
65
+ if (/^[UWBCGDT][A-Z0-9]{2,}$/.test(withoutPrefix)) return false;
66
+ if (/^[A-Z0-9]{8,}$/i.test(withoutPrefix)) return false;
67
+ return true;
68
+ }
69
+ //#endregion
70
+ //#region extensions/slack/src/doctor.ts
71
+ function asObjectRecord(value) {
72
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
73
+ }
74
+ const slackDoctor = {
75
+ dmAllowFromMode: "topOnly",
76
+ groupModel: "route",
77
+ groupAllowFromFallbackToAllowFrom: false,
78
+ warnOnEmptyGroupSenderAllowlist: false,
79
+ legacyConfigRules,
80
+ normalizeCompatibilityConfig,
81
+ collectMutableAllowlistWarnings: createDangerousNameMatchingMutableAllowlistWarningCollector({
82
+ channel: "slack",
83
+ detector: isSlackMutableAllowEntry,
84
+ collectLists: (scope) => {
85
+ const lists = [{
86
+ pathLabel: `${scope.prefix}.allowFrom`,
87
+ list: scope.account.allowFrom
88
+ }];
89
+ const dm = asObjectRecord(scope.account.dm);
90
+ if (dm) lists.push({
91
+ pathLabel: `${scope.prefix}.dm.allowFrom`,
92
+ list: dm.allowFrom
93
+ });
94
+ const channels = asObjectRecord(scope.account.channels);
95
+ if (channels) for (const [channelKey, channelRaw] of Object.entries(channels)) {
96
+ const channel = asObjectRecord(channelRaw);
97
+ if (!channel) continue;
98
+ lists.push({
99
+ pathLabel: `${scope.prefix}.channels.${channelKey}.users`,
100
+ list: channel.users
101
+ });
102
+ }
103
+ return lists;
104
+ }
105
+ })
106
+ };
107
+ //#endregion
108
+ //#region extensions/slack/src/shared.ts
109
+ function isSlackPluginAccountConfigured(account) {
110
+ const mode = account.config.mode ?? "socket";
111
+ if (!Boolean(account.botToken?.trim())) return false;
112
+ if (mode === "http") return Boolean(account.config.signingSecret?.trim());
113
+ return Boolean(account.appToken?.trim());
114
+ }
115
+ const slackConfigAdapter = createScopedChannelConfigAdapter({
116
+ sectionKey: SLACK_CHANNEL,
117
+ listAccountIds: listSlackAccountIds,
118
+ resolveAccount: adaptScopedAccountAccessor(resolveSlackAccount),
119
+ resolveAccessorAccount: resolveSlackConfigAccessorAccount,
120
+ inspectAccount: adaptScopedAccountAccessor(inspectSlackAccount),
121
+ defaultAccountId: resolveDefaultSlackAccountId,
122
+ clearBaseFields: [
123
+ "botToken",
124
+ "appToken",
125
+ "name"
126
+ ],
127
+ resolveAllowFrom: (account) => account.allowFrom,
128
+ formatAllowFrom: (allowFrom) => formatAllowFromLowercase({ allowFrom }),
129
+ resolveDefaultTo: (account) => account.defaultTo
130
+ });
131
+ function createSlackPluginBase(params) {
132
+ return {
133
+ id: SLACK_CHANNEL,
134
+ meta: {
135
+ ...getChatChannelMeta(SLACK_CHANNEL),
136
+ preferSessionLookupForAnnounceTarget: true
137
+ },
138
+ setupWizard: params.setupWizard,
139
+ capabilities: {
140
+ chatTypes: [
141
+ "direct",
142
+ "channel",
143
+ "thread"
144
+ ],
145
+ reactions: true,
146
+ threads: true,
147
+ media: true,
148
+ nativeCommands: true
149
+ },
150
+ commands: {
151
+ nativeCommandsAutoEnabled: false,
152
+ nativeSkillsAutoEnabled: false,
153
+ resolveNativeCommandName: ({ commandKey, defaultName }) => commandKey === "status" ? "agentstatus" : defaultName
154
+ },
155
+ doctor: slackDoctor,
156
+ agentPrompt: {
157
+ inboundFormattingHints: () => ({
158
+ text_markup: "slack_mrkdwn",
159
+ rules: [
160
+ "Use Slack mrkdwn, not standard Markdown.",
161
+ "Bold uses *single asterisks*.",
162
+ "Links use <url|label>.",
163
+ "Code blocks use triple backticks without a language identifier.",
164
+ "Do not use markdown headings or pipe tables."
165
+ ]
166
+ }),
167
+ messageToolHints: ({ cfg, accountId }) => (isSlackInteractiveRepliesEnabled({
168
+ cfg,
169
+ accountId
170
+ }) ? [
171
+ "- Prefer Slack buttons/selects for 2-5 discrete choices or parameter picks instead of asking the user to type one.",
172
+ "- Slack interactive replies: use `[[slack_buttons: Label:value, Other:other]]` to add action buttons that route clicks back as Slack interaction system events.",
173
+ "- Slack selects: use `[[slack_select: Placeholder | Label:value, Other:other]]` to add a static select menu that routes the chosen value back as a Slack interaction system event."
174
+ ] : ["- Slack interactive replies are disabled. If needed, ask to set `channels.slack.capabilities.interactiveReplies=true` (or the same under `channels.slack.accounts.<account>.capabilities`)."]).concat(["- Slack plain text sends: write standard Markdown; OpenClaw converts it to Slack mrkdwn, including `**bold**`, headings, lists, and `[label](url)` links.", "- Slack Block Kit or presentation text fields are sent as Slack mrkdwn directly; use `*bold*`, `_italic_`, `~strike~`, `<url|label>` links, and avoid Markdown headings or pipe tables there."])
175
+ },
176
+ streaming: { blockStreamingCoalesceDefaults: {
177
+ minChars: 1500,
178
+ idleMs: 1e3
179
+ } },
180
+ reload: { configPrefixes: ["channels.slack"] },
181
+ security: slackSecurityAdapter,
182
+ configSchema: SlackChannelConfigSchema,
183
+ config: {
184
+ ...slackConfigAdapter,
185
+ hasConfiguredState: ({ env }) => [
186
+ "SLACK_APP_TOKEN",
187
+ "SLACK_BOT_TOKEN",
188
+ "SLACK_USER_TOKEN"
189
+ ].some((key) => typeof env?.[key] === "string" && env[key]?.trim().length > 0),
190
+ isConfigured: (account) => isSlackPluginAccountConfigured(account),
191
+ describeAccount: (account) => describeAccountSnapshot({
192
+ account,
193
+ configured: isSlackPluginAccountConfigured(account),
194
+ extra: {
195
+ botTokenSource: account.botTokenSource,
196
+ appTokenSource: account.appTokenSource
197
+ }
198
+ })
199
+ },
200
+ secrets: {
201
+ secretTargetRegistryEntries,
202
+ collectRuntimeConfigAssignments
203
+ },
204
+ setup: params.setup
205
+ };
206
+ }
207
+ //#endregion
208
+ export { slackSecurityAdapter as i, isSlackPluginAccountConfigured as n, slackConfigAdapter as r, createSlackPluginBase as t };