@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.
- package/dist/account-inspect-D7AZNs8C.js +77 -0
- package/dist/account-inspect-api.js +10 -0
- package/dist/accounts-ClAPP5ry.js +139 -0
- package/dist/accounts.runtime-DDVcLJUI.js +2 -0
- package/dist/action-runtime-e2UhRsNx.js +350 -0
- package/dist/action-runtime.runtime-BFcqMbOm.js +2 -0
- package/dist/actions-CYLFK-Zy.js +292 -0
- package/dist/actions.runtime-CO3OaTLb.js +2 -0
- package/dist/allow-list-BPnnlRPL.js +82 -0
- package/dist/api.js +21 -0
- package/dist/approval-handler.runtime-CmeRr9qA.js +256 -0
- package/dist/blocks-input-CwTFVImV.js +29 -0
- package/dist/blocks-render-BIDw-Pom.js +161 -0
- package/dist/channel-DRjHBTDB.js +1020 -0
- package/dist/channel-api-B_nZwosg.js +20 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-entry.js +22 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.setup-Cayn7afd.js +73 -0
- package/dist/client-CPe4GmDR.js +103 -0
- package/dist/config-api-B_jq4NJW.js +2 -0
- package/dist/config-schema-D9B5LB_L.js +167 -0
- package/dist/configured-state.js +11 -0
- package/dist/contract-api.js +5 -0
- package/dist/directory-config-B3JiHeB7.js +54 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/directory-live-Bf16GwDh.js +133 -0
- package/dist/doctor-contract-KUjHnkQm.js +147 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/errors-BYFHR24f.js +109 -0
- package/dist/exec-approvals-7xUNgLi9.js +58 -0
- package/dist/group-policy-CyLUK6My.js +41 -0
- package/dist/http-routes-api.js +2 -0
- package/dist/inbound-contract-test-api.js +3 -0
- package/dist/index.js +33 -0
- package/dist/interactive-replies-api.js +2 -0
- package/dist/interactive-replies-qAIfuBor.js +173 -0
- package/dist/magic-string.es-BMaGRRZ1.js +1011 -0
- package/dist/media-D1XCd1uP.js +469 -0
- package/dist/message-tool-api-6lowf9zE.js +104 -0
- package/dist/message-tool-api.js +2 -0
- package/dist/monitor-a97o17G6.js +13 -0
- package/dist/mrkdwn-Cax-eSfK.js +6 -0
- package/dist/outbound-adapter-B_5sEhCg.js +174 -0
- package/dist/outbound-payload-test-api.js +2 -0
- package/dist/outbound-payload.test-harness-CVCamg1x.js +13558 -0
- package/dist/pipeline.runtime-DT0hLnq2.js +1379 -0
- package/dist/plugin-routes-DtTPmga1.js +20 -0
- package/dist/prepare-D3YqV8jB.js +1482 -0
- package/dist/prepare.test-helpers-DVcjRhfG.js +49 -0
- package/dist/probe-3eZf1FjI.js +42 -0
- package/dist/provider-D7uAN3Fq.js +3235 -0
- package/dist/registry-CeaoNfoP.js +39 -0
- package/dist/replies-Xe_jMR6o.js +139 -0
- package/dist/reply-blocks-Z5l6_R6H.js +14 -0
- package/dist/resolve-allowlist-common-Bk3clYPK.js +43 -0
- package/dist/resolve-channels-BRYqyNVJ.js +81 -0
- package/dist/resolve-users-Bd_SdP8j.js +113 -0
- package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/room-context-0vovmZPU.js +787 -0
- package/dist/runtime-Bo-KHM-F.js +8 -0
- package/dist/runtime-api-Dd1xIV5v.js +9 -0
- package/dist/runtime-api.js +14 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/scopes-CDevO8jg.js +74 -0
- package/dist/secret-contract-Bo6lbSkh.js +141 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-BtHGnD3d.js +51 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-D_A9kL-C.js +721 -0
- package/dist/send.runtime-BRE_ncCU.js +2 -0
- package/dist/send.runtime-_l76lUuL.js +2 -0
- package/dist/setup-core-B9NetDkM.js +320 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-D88QBVOW.js +128 -0
- package/dist/shared-D8U42xFL.js +208 -0
- package/dist/slash-commands.runtime-22kgyst2.js +19 -0
- package/dist/slash-dispatch.runtime-BJgT0jwV.js +32 -0
- package/dist/slash-plugin-commands.runtime-CF-n3MeP.js +2 -0
- package/dist/slash-skill-commands.runtime-BMs0VjTe.js +7 -0
- package/dist/streaming-compat-RkZgTmQ2.js +43 -0
- package/dist/target-parsing-CQmv-iSm.js +55 -0
- package/dist/targets-B1tYCAr6.js +2 -0
- package/dist/test-api.js +8 -0
- package/dist/thread-ts-C2x7c5PP.js +24 -0
- package/openclaw.plugin.json +2405 -0
- package/package.json +84 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { a as resolveSlackAccount, d as resolveSlackBotToken } from "./accounts-ClAPP5ry.js";
|
|
2
|
+
import { i as truncateSlackText, r as SLACK_TEXT_LIMIT } from "./thread-ts-C2x7c5PP.js";
|
|
3
|
+
import { a as getSlackWriteClient, r as createSlackWebClient } from "./client-CPe4GmDR.js";
|
|
4
|
+
import { r as validateSlackBlocksArray } from "./blocks-input-CwTFVImV.js";
|
|
5
|
+
import { c as buildSlackBlocksFallbackText, t as sendMessageSlack } from "./send-D_A9kL-C.js";
|
|
6
|
+
import { n as resolveSlackMedia } from "./media-D1XCd1uP.js";
|
|
7
|
+
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
8
|
+
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
9
|
+
//#region extensions/slack/src/edit-text.ts
|
|
10
|
+
function buildSlackEditTextPayload(content, blocks) {
|
|
11
|
+
const trimmedContent = content.trim();
|
|
12
|
+
if (trimmedContent) return trimmedContent;
|
|
13
|
+
if (blocks?.length) return truncateSlackText(buildSlackBlocksFallbackText(blocks), SLACK_TEXT_LIMIT);
|
|
14
|
+
return " ";
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region extensions/slack/src/actions.ts
|
|
18
|
+
function resolveToken(explicit, accountId, cfg) {
|
|
19
|
+
if (explicit?.trim()) {
|
|
20
|
+
const token = resolveSlackBotToken(explicit);
|
|
21
|
+
if (token) return token;
|
|
22
|
+
}
|
|
23
|
+
if (!cfg) throw new Error("Slack actions requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
|
|
24
|
+
const account = resolveSlackAccount({
|
|
25
|
+
cfg: requireRuntimeConfig(cfg, "Slack actions"),
|
|
26
|
+
accountId
|
|
27
|
+
});
|
|
28
|
+
const token = resolveSlackBotToken(account.botToken ?? void 0);
|
|
29
|
+
if (!token) {
|
|
30
|
+
logVerbose(`slack actions: missing bot token for account=${account.accountId} explicit=${Boolean(explicit)} source=${account.botTokenSource ?? "unknown"}`);
|
|
31
|
+
throw new Error("SLACK_BOT_TOKEN or channels.slack.botToken is required for Slack actions");
|
|
32
|
+
}
|
|
33
|
+
return token;
|
|
34
|
+
}
|
|
35
|
+
function normalizeEmoji(raw) {
|
|
36
|
+
const trimmed = raw.trim();
|
|
37
|
+
if (!trimmed) throw new Error("Emoji is required for Slack reactions");
|
|
38
|
+
return trimmed.replace(/^:+|:+$/g, "");
|
|
39
|
+
}
|
|
40
|
+
function hasSlackPlatformError(err, code) {
|
|
41
|
+
if (!err || typeof err !== "object") return false;
|
|
42
|
+
const data = err.data;
|
|
43
|
+
if (!data || typeof data !== "object") return false;
|
|
44
|
+
return data.error === code;
|
|
45
|
+
}
|
|
46
|
+
async function getClient(opts = {}, mode = "read") {
|
|
47
|
+
if (opts.client) return opts.client;
|
|
48
|
+
const token = resolveToken(opts.token, opts.accountId, opts.cfg);
|
|
49
|
+
return mode === "write" ? getSlackWriteClient(token) : createSlackWebClient(token);
|
|
50
|
+
}
|
|
51
|
+
async function resolveBotUserId(client) {
|
|
52
|
+
const auth = await client.auth.test();
|
|
53
|
+
if (!auth?.user_id) throw new Error("Failed to resolve Slack bot user id");
|
|
54
|
+
return auth.user_id;
|
|
55
|
+
}
|
|
56
|
+
async function reactSlackMessage(channelId, messageId, emoji, opts = {}) {
|
|
57
|
+
const client = await getClient(opts, "write");
|
|
58
|
+
try {
|
|
59
|
+
await client.reactions.add({
|
|
60
|
+
channel: channelId,
|
|
61
|
+
timestamp: messageId,
|
|
62
|
+
name: normalizeEmoji(emoji)
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (hasSlackPlatformError(err, "already_reacted")) return;
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function removeSlackReaction(channelId, messageId, emoji, opts = {}) {
|
|
70
|
+
const client = await getClient(opts, "write");
|
|
71
|
+
try {
|
|
72
|
+
await client.reactions.remove({
|
|
73
|
+
channel: channelId,
|
|
74
|
+
timestamp: messageId,
|
|
75
|
+
name: normalizeEmoji(emoji)
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (hasSlackPlatformError(err, "no_reaction")) return;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function removeOwnSlackReactions(channelId, messageId, opts = {}) {
|
|
83
|
+
const client = await getClient(opts, "write");
|
|
84
|
+
const userId = await resolveBotUserId(client);
|
|
85
|
+
const reactions = await listSlackReactions(channelId, messageId, { client });
|
|
86
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
87
|
+
for (const reaction of reactions ?? []) {
|
|
88
|
+
const name = reaction?.name;
|
|
89
|
+
if (!name) continue;
|
|
90
|
+
if ((reaction?.users ?? []).includes(userId)) toRemove.add(name);
|
|
91
|
+
}
|
|
92
|
+
if (toRemove.size === 0) return [];
|
|
93
|
+
await Promise.all(Array.from(toRemove, (name) => removeSlackReaction(channelId, messageId, name, {
|
|
94
|
+
...opts,
|
|
95
|
+
client
|
|
96
|
+
})));
|
|
97
|
+
return Array.from(toRemove);
|
|
98
|
+
}
|
|
99
|
+
async function listSlackReactions(channelId, messageId, opts = {}) {
|
|
100
|
+
return (await (await getClient(opts)).reactions.get({
|
|
101
|
+
channel: channelId,
|
|
102
|
+
timestamp: messageId,
|
|
103
|
+
full: true
|
|
104
|
+
})).message?.reactions ?? [];
|
|
105
|
+
}
|
|
106
|
+
async function sendSlackMessage(to, content, opts) {
|
|
107
|
+
return await sendMessageSlack(to, content, {
|
|
108
|
+
accountId: opts.accountId,
|
|
109
|
+
cfg: opts.cfg,
|
|
110
|
+
token: opts.token,
|
|
111
|
+
mediaUrl: opts.mediaUrl,
|
|
112
|
+
mediaAccess: opts.mediaAccess,
|
|
113
|
+
mediaLocalRoots: opts.mediaLocalRoots,
|
|
114
|
+
mediaReadFile: opts.mediaReadFile,
|
|
115
|
+
client: opts.client,
|
|
116
|
+
threadTs: opts.threadTs,
|
|
117
|
+
replyBroadcast: opts.replyBroadcast,
|
|
118
|
+
...opts.uploadFileName ? { uploadFileName: opts.uploadFileName } : {},
|
|
119
|
+
...opts.uploadTitle ? { uploadTitle: opts.uploadTitle } : {},
|
|
120
|
+
blocks: opts.blocks
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function editSlackMessage(channelId, messageId, content, opts = {}) {
|
|
124
|
+
const client = await getClient(opts, "write");
|
|
125
|
+
const blocks = opts.blocks == null ? void 0 : validateSlackBlocksArray(opts.blocks);
|
|
126
|
+
await client.chat.update({
|
|
127
|
+
channel: channelId,
|
|
128
|
+
ts: messageId,
|
|
129
|
+
text: buildSlackEditTextPayload(content, blocks),
|
|
130
|
+
...blocks ? { blocks } : {}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async function deleteSlackMessage(channelId, messageId, opts = {}) {
|
|
134
|
+
await (await getClient(opts, "write")).chat.delete({
|
|
135
|
+
channel: channelId,
|
|
136
|
+
ts: messageId
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async function readSlackMessages(channelId, opts = {}) {
|
|
140
|
+
const client = await getClient(opts);
|
|
141
|
+
const exactMessageId = opts.messageId?.trim();
|
|
142
|
+
const readLimit = exactMessageId ? 1 : opts.limit;
|
|
143
|
+
const exactBounds = exactMessageId ? {
|
|
144
|
+
inclusive: true,
|
|
145
|
+
latest: exactMessageId,
|
|
146
|
+
oldest: void 0
|
|
147
|
+
} : {
|
|
148
|
+
latest: opts.before,
|
|
149
|
+
oldest: opts.after
|
|
150
|
+
};
|
|
151
|
+
if (opts.threadId) {
|
|
152
|
+
const result = await client.conversations.replies({
|
|
153
|
+
channel: channelId,
|
|
154
|
+
ts: opts.threadId,
|
|
155
|
+
limit: readLimit,
|
|
156
|
+
...exactBounds
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
messages: (result.messages ?? []).filter((message) => {
|
|
160
|
+
if (exactMessageId) return message.ts === exactMessageId;
|
|
161
|
+
return message.ts !== opts.threadId;
|
|
162
|
+
}),
|
|
163
|
+
hasMore: exactMessageId ? false : Boolean(result.has_more)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const result = await client.conversations.history({
|
|
167
|
+
channel: channelId,
|
|
168
|
+
limit: readLimit,
|
|
169
|
+
...exactBounds
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
messages: (result.messages ?? []).filter((message) => !exactMessageId || message.ts === exactMessageId),
|
|
173
|
+
hasMore: exactMessageId ? false : Boolean(result.has_more)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async function getSlackMemberInfo(userId, opts = {}) {
|
|
177
|
+
return await (await getClient(opts)).users.info({ user: userId });
|
|
178
|
+
}
|
|
179
|
+
async function listSlackEmojis(opts = {}) {
|
|
180
|
+
return await (await getClient(opts)).emoji.list();
|
|
181
|
+
}
|
|
182
|
+
async function pinSlackMessage(channelId, messageId, opts = {}) {
|
|
183
|
+
await (await getClient(opts, "write")).pins.add({
|
|
184
|
+
channel: channelId,
|
|
185
|
+
timestamp: messageId
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async function unpinSlackMessage(channelId, messageId, opts = {}) {
|
|
189
|
+
await (await getClient(opts, "write")).pins.remove({
|
|
190
|
+
channel: channelId,
|
|
191
|
+
timestamp: messageId
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async function listSlackPins(channelId, opts = {}) {
|
|
195
|
+
return (await (await getClient(opts)).pins.list({ channel: channelId })).items ?? [];
|
|
196
|
+
}
|
|
197
|
+
function normalizeSlackScopeValue(value) {
|
|
198
|
+
const trimmed = value?.trim();
|
|
199
|
+
return trimmed ? trimmed : void 0;
|
|
200
|
+
}
|
|
201
|
+
function collectSlackDirectShareChannelIds(file) {
|
|
202
|
+
const ids = /* @__PURE__ */ new Set();
|
|
203
|
+
for (const group of [
|
|
204
|
+
file.channels,
|
|
205
|
+
file.groups,
|
|
206
|
+
file.ims
|
|
207
|
+
]) {
|
|
208
|
+
if (!Array.isArray(group)) continue;
|
|
209
|
+
for (const entry of group) {
|
|
210
|
+
if (typeof entry !== "string") continue;
|
|
211
|
+
const normalized = normalizeSlackScopeValue(entry);
|
|
212
|
+
if (normalized) ids.add(normalized);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return ids;
|
|
216
|
+
}
|
|
217
|
+
function collectSlackShareMaps(file) {
|
|
218
|
+
if (!file.shares || typeof file.shares !== "object" || Array.isArray(file.shares)) return [];
|
|
219
|
+
const shares = file.shares;
|
|
220
|
+
return [shares.public, shares.private].filter((value) => Boolean(value) && typeof value === "object" && !Array.isArray(value));
|
|
221
|
+
}
|
|
222
|
+
function collectSlackSharedChannelIds(file) {
|
|
223
|
+
const ids = /* @__PURE__ */ new Set();
|
|
224
|
+
for (const shareMap of collectSlackShareMaps(file)) for (const channelId of Object.keys(shareMap)) {
|
|
225
|
+
const normalized = normalizeSlackScopeValue(channelId);
|
|
226
|
+
if (normalized) ids.add(normalized);
|
|
227
|
+
}
|
|
228
|
+
return ids;
|
|
229
|
+
}
|
|
230
|
+
function collectSlackThreadShares(file, channelId) {
|
|
231
|
+
const matches = [];
|
|
232
|
+
for (const shareMap of collectSlackShareMaps(file)) {
|
|
233
|
+
const rawEntries = shareMap[channelId];
|
|
234
|
+
if (!Array.isArray(rawEntries)) continue;
|
|
235
|
+
for (const rawEntry of rawEntries) {
|
|
236
|
+
if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) continue;
|
|
237
|
+
const entry = rawEntry;
|
|
238
|
+
const ts = typeof entry.ts === "string" ? normalizeSlackScopeValue(entry.ts) : void 0;
|
|
239
|
+
const threadTs = typeof entry.thread_ts === "string" ? normalizeSlackScopeValue(entry.thread_ts) : void 0;
|
|
240
|
+
matches.push({
|
|
241
|
+
channelId,
|
|
242
|
+
ts,
|
|
243
|
+
threadTs
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return matches;
|
|
248
|
+
}
|
|
249
|
+
function hasSlackScopeMismatch(params) {
|
|
250
|
+
const channelId = normalizeSlackScopeValue(params.channelId);
|
|
251
|
+
if (!channelId) return false;
|
|
252
|
+
const threadId = normalizeSlackScopeValue(params.threadId);
|
|
253
|
+
const directIds = collectSlackDirectShareChannelIds(params.file);
|
|
254
|
+
const sharedIds = collectSlackSharedChannelIds(params.file);
|
|
255
|
+
const hasChannelEvidence = directIds.size > 0 || sharedIds.size > 0;
|
|
256
|
+
const inChannel = directIds.has(channelId) || sharedIds.has(channelId);
|
|
257
|
+
if (hasChannelEvidence && !inChannel) return true;
|
|
258
|
+
if (!threadId) return false;
|
|
259
|
+
const threadShares = collectSlackThreadShares(params.file, channelId);
|
|
260
|
+
if (threadShares.length === 0) return false;
|
|
261
|
+
const threadEvidence = threadShares.filter((entry) => entry.threadTs || entry.ts);
|
|
262
|
+
if (threadEvidence.length === 0) return false;
|
|
263
|
+
return !threadEvidence.some((entry) => entry.threadTs === threadId || entry.ts === threadId);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Downloads a Slack file by ID and saves it to the local media store.
|
|
267
|
+
* Fetches a fresh download URL via files.info to avoid using stale private URLs.
|
|
268
|
+
* Returns null when the file cannot be found or downloaded.
|
|
269
|
+
*/
|
|
270
|
+
async function downloadSlackFile(fileId, opts) {
|
|
271
|
+
const token = resolveToken(opts.token, opts.accountId, opts.cfg);
|
|
272
|
+
const file = (await (await getClient(opts)).files.info({ file: fileId })).file;
|
|
273
|
+
if (!file?.url_private_download && !file?.url_private) return null;
|
|
274
|
+
if (hasSlackScopeMismatch({
|
|
275
|
+
file,
|
|
276
|
+
channelId: opts.channelId,
|
|
277
|
+
threadId: opts.threadId
|
|
278
|
+
})) return null;
|
|
279
|
+
return (await resolveSlackMedia({
|
|
280
|
+
files: [{
|
|
281
|
+
id: file.id,
|
|
282
|
+
name: file.name,
|
|
283
|
+
mimetype: file.mimetype,
|
|
284
|
+
url_private: file.url_private,
|
|
285
|
+
url_private_download: file.url_private_download
|
|
286
|
+
}],
|
|
287
|
+
token,
|
|
288
|
+
maxBytes: opts.maxBytes
|
|
289
|
+
}))?.[0] ?? null;
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
export { listSlackEmojis as a, pinSlackMessage as c, removeOwnSlackReactions as d, removeSlackReaction as f, buildSlackEditTextPayload as h, getSlackMemberInfo as i, reactSlackMessage as l, unpinSlackMessage as m, downloadSlackFile as n, listSlackPins as o, sendSlackMessage as p, editSlackMessage as r, listSlackReactions as s, deleteSlackMessage as t, readSlackMessages as u };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, i as getSlackMemberInfo, l as reactSlackMessage, m as unpinSlackMessage, n as downloadSlackFile, o as listSlackPins, p as sendSlackMessage, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-CYLFK-Zy.js";
|
|
2
|
+
export { deleteSlackMessage, downloadSlackFile, editSlackMessage, getSlackMemberInfo, listSlackEmojis, listSlackPins, listSlackReactions, pinSlackMessage, reactSlackMessage, readSlackMessages, removeOwnSlackReactions, removeSlackReaction, sendSlackMessage, unpinSlackMessage };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { compileAllowlist, resolveCompiledAllowlistMatch } from "openclaw/plugin-sdk/allow-from";
|
|
3
|
+
import { normalizeHyphenSlug, normalizeStringEntries as normalizeStringEntries$1, normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
|
|
4
|
+
//#region extensions/slack/src/monitor/allow-list.ts
|
|
5
|
+
const SLACK_SLUG_CACHE_MAX = 512;
|
|
6
|
+
const slackSlugCache = /* @__PURE__ */ new Map();
|
|
7
|
+
function normalizeSlackSlug(raw) {
|
|
8
|
+
const key = raw ?? "";
|
|
9
|
+
const cached = slackSlugCache.get(key);
|
|
10
|
+
if (cached !== void 0) return cached;
|
|
11
|
+
const normalized = normalizeHyphenSlug(raw);
|
|
12
|
+
slackSlugCache.set(key, normalized);
|
|
13
|
+
if (slackSlugCache.size > SLACK_SLUG_CACHE_MAX) {
|
|
14
|
+
const oldest = slackSlugCache.keys().next();
|
|
15
|
+
if (!oldest.done) slackSlugCache.delete(oldest.value);
|
|
16
|
+
}
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
function normalizeAllowList(list) {
|
|
20
|
+
return normalizeStringEntries$1(list);
|
|
21
|
+
}
|
|
22
|
+
function normalizeAllowListLower(list) {
|
|
23
|
+
return normalizeStringEntriesLower(list);
|
|
24
|
+
}
|
|
25
|
+
function normalizeSlackAllowOwnerEntry(entry) {
|
|
26
|
+
const trimmed = normalizeOptionalLowercaseString(entry);
|
|
27
|
+
if (!trimmed || trimmed === "*") return;
|
|
28
|
+
const withoutPrefix = trimmed.replace(/^(slack:|user:)/, "");
|
|
29
|
+
return /^u[a-z0-9]+$/.test(withoutPrefix) ? withoutPrefix : void 0;
|
|
30
|
+
}
|
|
31
|
+
function resolveSlackAllowListMatch(params) {
|
|
32
|
+
const compiledAllowList = compileAllowlist(params.allowList);
|
|
33
|
+
const id = normalizeOptionalLowercaseString(params.id);
|
|
34
|
+
const name = normalizeOptionalLowercaseString(params.name);
|
|
35
|
+
const slug = normalizeSlackSlug(name);
|
|
36
|
+
return resolveCompiledAllowlistMatch({
|
|
37
|
+
compiledAllowlist: compiledAllowList,
|
|
38
|
+
candidates: [
|
|
39
|
+
{
|
|
40
|
+
value: id,
|
|
41
|
+
source: "id"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
value: id ? `slack:${id}` : void 0,
|
|
45
|
+
source: "prefixed-id"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: id ? `user:${id}` : void 0,
|
|
49
|
+
source: "prefixed-user"
|
|
50
|
+
},
|
|
51
|
+
...params.allowNameMatching === true ? [
|
|
52
|
+
{
|
|
53
|
+
value: name,
|
|
54
|
+
source: "name"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
value: name ? `slack:${name}` : void 0,
|
|
58
|
+
source: "prefixed-name"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
value: slug,
|
|
62
|
+
source: "slug"
|
|
63
|
+
}
|
|
64
|
+
] : []
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function allowListMatches(params) {
|
|
69
|
+
return resolveSlackAllowListMatch(params).allowed;
|
|
70
|
+
}
|
|
71
|
+
function resolveSlackUserAllowed(params) {
|
|
72
|
+
const allowList = normalizeAllowListLower(params.allowList);
|
|
73
|
+
if (allowList.length === 0) return true;
|
|
74
|
+
return allowListMatches({
|
|
75
|
+
allowList,
|
|
76
|
+
id: params.userId,
|
|
77
|
+
name: params.userName,
|
|
78
|
+
allowNameMatching: params.allowNameMatching
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { normalizeSlackSlug as a, normalizeSlackAllowOwnerEntry as i, normalizeAllowList as n, resolveSlackAllowListMatch as o, normalizeAllowListLower as r, resolveSlackUserAllowed as s, allowListMatches as t };
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, l as resolveSlackReplyToMode, n as listSlackAccountIds, r as mergeSlackAccountConfig, t as listEnabledSlackAccounts } from "./accounts-ClAPP5ry.js";
|
|
2
|
+
import { t as inspectSlackAccount } from "./account-inspect-D7AZNs8C.js";
|
|
3
|
+
import { i as resolveSlackChannelId, n as normalizeSlackMessagingTarget, r as parseSlackTarget, t as looksLikeSlackTargetId } from "./target-parsing-CQmv-iSm.js";
|
|
4
|
+
import "./targets-B1tYCAr6.js";
|
|
5
|
+
import { i as resolveSlackChannelType, n as buildSlackThreadingToolContext, o as resolveSlackAutoThreadId, r as __resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-DRjHBTDB.js";
|
|
6
|
+
import { n as buildSlackPresentationBlocks, t as buildSlackInteractiveBlocks } from "./blocks-render-BIDw-Pom.js";
|
|
7
|
+
import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-6lowf9zE.js";
|
|
8
|
+
import { n as isSlackInteractiveRepliesEnabled, r as parseSlackOptionsLine, t as compileSlackInteractiveReplies } from "./interactive-replies-qAIfuBor.js";
|
|
9
|
+
import { a as getSlackWriteClient, c as resolveSlackWebClientOptions, i as createSlackWriteClient, l as resolveSlackWriteClientOptions, n as createSlackTokenCacheKey, o as SLACK_DEFAULT_RETRY_OPTIONS, r as createSlackWebClient, s as SLACK_WRITE_RETRY_OPTIONS, t as clearSlackWriteClientCacheForTest } from "./client-CPe4GmDR.js";
|
|
10
|
+
import { a as normalizeSlackSlug, i as normalizeSlackAllowOwnerEntry, n as normalizeAllowList, o as resolveSlackAllowListMatch, r as normalizeAllowListLower, s as resolveSlackUserAllowed, t as allowListMatches } from "./allow-list-BPnnlRPL.js";
|
|
11
|
+
import { n as resolveSlackGroupToolPolicy, t as resolveSlackGroupRequireMention } from "./group-policy-CyLUK6My.js";
|
|
12
|
+
import { n as parseSlackBlocksInput, r as validateSlackBlocksArray, t as SLACK_MAX_BLOCKS } from "./blocks-input-CwTFVImV.js";
|
|
13
|
+
import { t as slackSetupPlugin } from "./channel.setup-Cayn7afd.js";
|
|
14
|
+
import { a as recordSlackThreadParticipation, n as clearSlackThreadParticipationCache, r as hasSlackThreadParticipation } from "./send-D_A9kL-C.js";
|
|
15
|
+
import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, i as getSlackMemberInfo, l as reactSlackMessage, m as unpinSlackMessage, n as downloadSlackFile, o as listSlackPins, p as sendSlackMessage, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-CYLFK-Zy.js";
|
|
16
|
+
import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-B3JiHeB7.js";
|
|
17
|
+
import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-CeaoNfoP.js";
|
|
18
|
+
import { t as probeSlack } from "./probe-3eZf1FjI.js";
|
|
19
|
+
import { t as collectSlackSecurityAuditFindings } from "./security-audit-BtHGnD3d.js";
|
|
20
|
+
import { n as resolveSlackRuntimeGroupPolicy } from "./provider-D7uAN3Fq.js";
|
|
21
|
+
export { SLACK_DEFAULT_RETRY_OPTIONS, SLACK_MAX_BLOCKS, SLACK_WRITE_RETRY_OPTIONS, __resetSlackChannelTypeCacheForTest, allowListMatches, buildSlackInteractiveBlocks, buildSlackPresentationBlocks, buildSlackThreadingToolContext, clearSlackThreadParticipationCache, clearSlackWriteClientCacheForTest, collectSlackSecurityAuditFindings, compileSlackInteractiveReplies, createSlackTokenCacheKey, createSlackWebClient, createSlackWriteClient, deleteSlackMessage, downloadSlackFile, editSlackMessage, extractSlackToolSend, getSlackMemberInfo, getSlackWriteClient, handleSlackHttpRequest, hasSlackThreadParticipation, inspectSlackAccount, isSlackInteractiveRepliesEnabled, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsFromConfig, listSlackDirectoryPeersFromConfig, listSlackEmojis, listSlackMessageActions, listSlackPins, listSlackReactions, looksLikeSlackTargetId, mergeSlackAccountConfig, normalizeAllowList, normalizeAllowListLower, normalizeSlackAllowOwnerEntry, normalizeSlackMessagingTarget, normalizeSlackSlug, normalizeSlackWebhookPath, parseSlackBlocksInput, parseSlackOptionsLine, parseSlackTarget, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, recordSlackThreadParticipation, registerSlackHttpHandler, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAllowListMatch, resolveSlackAutoThreadId, resolveSlackChannelId, resolveSlackChannelType, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackReplyToMode, resolveSlackRuntimeGroupPolicy, resolveSlackUserAllowed, resolveSlackWebClientOptions, resolveSlackWriteClientOptions, sendSlackMessage, slackPlugin, slackSetupPlugin, unpinSlackMessage, validateSlackBlocksArray };
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { a as normalizeSlackApproverId, i as isSlackExecApprovalClientEnabled, s as shouldHandleSlackExecApprovalRequest } from "./exec-approvals-7xUNgLi9.js";
|
|
2
|
+
import { i as truncateSlackText } from "./thread-ts-C2x7c5PP.js";
|
|
3
|
+
import { t as resolveSlackReplyBlocks } from "./reply-blocks-Z5l6_R6H.js";
|
|
4
|
+
import { t as sendMessageSlack } from "./send-D_A9kL-C.js";
|
|
5
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
+
import { buildChannelApprovalNativeTargetKey } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
7
|
+
import { logError } from "openclaw/plugin-sdk/logging-core";
|
|
8
|
+
import { buildApprovalInteractiveReplyFromActionDescriptors } from "openclaw/plugin-sdk/approval-reply-runtime";
|
|
9
|
+
import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
10
|
+
//#region extensions/slack/src/approval-handler.runtime.ts
|
|
11
|
+
const SLACK_CONTEXT_ELEMENTS_MAX = 10;
|
|
12
|
+
const SLACK_CHAT_UPDATE_TEXT_LIMIT = 4e3;
|
|
13
|
+
const SLACK_TEXT_OBJECT_MAX = 3e3;
|
|
14
|
+
function resolveHandlerContext(params) {
|
|
15
|
+
const context = params.context;
|
|
16
|
+
const accountId = normalizeOptionalString(params.accountId) ?? "";
|
|
17
|
+
if (!context?.app || !accountId) return null;
|
|
18
|
+
return {
|
|
19
|
+
accountId,
|
|
20
|
+
context
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function truncateSlackMrkdwn(text, maxChars) {
|
|
24
|
+
return text.length <= maxChars ? text : `${text.slice(0, maxChars - 1)}…`;
|
|
25
|
+
}
|
|
26
|
+
function buildSlackCodeBlock(text) {
|
|
27
|
+
let fence = "```";
|
|
28
|
+
while (text.includes(fence)) fence += "`";
|
|
29
|
+
return `${fence}\n${text}\n${fence}`;
|
|
30
|
+
}
|
|
31
|
+
function formatSlackApprover(resolvedBy) {
|
|
32
|
+
const normalized = resolvedBy ? normalizeSlackApproverId(resolvedBy) : void 0;
|
|
33
|
+
if (normalized) return `<@${normalized}>`;
|
|
34
|
+
const trimmed = normalizeOptionalString(resolvedBy);
|
|
35
|
+
return trimmed ? trimmed : null;
|
|
36
|
+
}
|
|
37
|
+
function formatSlackMetadataLine(label, value) {
|
|
38
|
+
return `*${label}:* ${value}`;
|
|
39
|
+
}
|
|
40
|
+
function buildSlackMetadataLines(metadata) {
|
|
41
|
+
const lines = [];
|
|
42
|
+
for (const item of metadata) lines.push(formatSlackMetadataLine(item.label, item.value));
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
45
|
+
function buildSlackMetadataContextElements(metadata) {
|
|
46
|
+
const lines = buildSlackMetadataLines(metadata);
|
|
47
|
+
const visibleLineCount = lines.length > SLACK_CONTEXT_ELEMENTS_MAX ? SLACK_CONTEXT_ELEMENTS_MAX - 1 : lines.length;
|
|
48
|
+
const elements = [];
|
|
49
|
+
for (let index = 0; index < visibleLineCount; index += 1) {
|
|
50
|
+
const line = lines[index];
|
|
51
|
+
if (line === void 0) continue;
|
|
52
|
+
elements.push({
|
|
53
|
+
type: "mrkdwn",
|
|
54
|
+
text: truncateSlackMrkdwn(line, SLACK_TEXT_OBJECT_MAX)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (lines.length > SLACK_CONTEXT_ELEMENTS_MAX) elements.push({
|
|
58
|
+
type: "mrkdwn",
|
|
59
|
+
text: `…+${lines.length - visibleLineCount} more`
|
|
60
|
+
});
|
|
61
|
+
return elements;
|
|
62
|
+
}
|
|
63
|
+
function resolveSlackApprovalDecisionLabel(decision) {
|
|
64
|
+
return decision === "allow-once" ? "Allowed once" : decision === "allow-always" ? "Allowed always" : "Denied";
|
|
65
|
+
}
|
|
66
|
+
function buildSlackPendingApprovalText(view) {
|
|
67
|
+
const metadataLines = buildSlackMetadataLines(view.metadata);
|
|
68
|
+
return [
|
|
69
|
+
"*Exec approval required*",
|
|
70
|
+
"A command needs your approval.",
|
|
71
|
+
"",
|
|
72
|
+
"*Command*",
|
|
73
|
+
buildSlackCodeBlock(view.commandText),
|
|
74
|
+
...metadataLines
|
|
75
|
+
].join("\n");
|
|
76
|
+
}
|
|
77
|
+
function buildSlackPendingApprovalBlocks(view) {
|
|
78
|
+
const metadataElements = buildSlackMetadataContextElements(view.metadata);
|
|
79
|
+
const interactiveBlocks = resolveSlackReplyBlocks({
|
|
80
|
+
text: "",
|
|
81
|
+
interactive: buildApprovalInteractiveReplyFromActionDescriptors(view.actions)
|
|
82
|
+
}) ?? [];
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
type: "section",
|
|
86
|
+
text: {
|
|
87
|
+
type: "mrkdwn",
|
|
88
|
+
text: "*Exec approval required*\nA command needs your approval."
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "section",
|
|
93
|
+
text: {
|
|
94
|
+
type: "mrkdwn",
|
|
95
|
+
text: `*Command*\n${buildSlackCodeBlock(truncateSlackMrkdwn(view.commandText, 2600))}`
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
...metadataElements.length > 0 ? [{
|
|
99
|
+
type: "context",
|
|
100
|
+
elements: metadataElements
|
|
101
|
+
}] : [],
|
|
102
|
+
...interactiveBlocks
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
function buildSlackResolvedText(view) {
|
|
106
|
+
const resolvedBy = formatSlackApprover(view.resolvedBy);
|
|
107
|
+
return [
|
|
108
|
+
`*Exec approval: ${resolveSlackApprovalDecisionLabel(view.decision)}*`,
|
|
109
|
+
resolvedBy ? `Resolved by ${resolvedBy}.` : "Resolved.",
|
|
110
|
+
"",
|
|
111
|
+
"*Command*",
|
|
112
|
+
buildSlackCodeBlock(view.commandText)
|
|
113
|
+
].join("\n");
|
|
114
|
+
}
|
|
115
|
+
function buildSlackResolvedBlocks(view) {
|
|
116
|
+
const resolvedBy = formatSlackApprover(view.resolvedBy);
|
|
117
|
+
return [{
|
|
118
|
+
type: "section",
|
|
119
|
+
text: {
|
|
120
|
+
type: "mrkdwn",
|
|
121
|
+
text: `*Exec approval: ${resolveSlackApprovalDecisionLabel(view.decision)}*\n${resolvedBy ? `Resolved by ${resolvedBy}.` : "Resolved."}`
|
|
122
|
+
}
|
|
123
|
+
}, {
|
|
124
|
+
type: "section",
|
|
125
|
+
text: {
|
|
126
|
+
type: "mrkdwn",
|
|
127
|
+
text: `*Command*\n${buildSlackCodeBlock(truncateSlackMrkdwn(view.commandText, 2600))}`
|
|
128
|
+
}
|
|
129
|
+
}];
|
|
130
|
+
}
|
|
131
|
+
function buildSlackExpiredText(view) {
|
|
132
|
+
return [
|
|
133
|
+
"*Exec approval expired*",
|
|
134
|
+
"This approval request expired before it was resolved.",
|
|
135
|
+
"",
|
|
136
|
+
"*Command*",
|
|
137
|
+
buildSlackCodeBlock(view.commandText)
|
|
138
|
+
].join("\n");
|
|
139
|
+
}
|
|
140
|
+
function buildSlackExpiredBlocks(view) {
|
|
141
|
+
return [{
|
|
142
|
+
type: "section",
|
|
143
|
+
text: {
|
|
144
|
+
type: "mrkdwn",
|
|
145
|
+
text: "*Exec approval expired*\nThis approval request expired before it was resolved."
|
|
146
|
+
}
|
|
147
|
+
}, {
|
|
148
|
+
type: "section",
|
|
149
|
+
text: {
|
|
150
|
+
type: "mrkdwn",
|
|
151
|
+
text: `*Command*\n${buildSlackCodeBlock(truncateSlackMrkdwn(view.commandText, 2600))}`
|
|
152
|
+
}
|
|
153
|
+
}];
|
|
154
|
+
}
|
|
155
|
+
async function updateMessage(params) {
|
|
156
|
+
try {
|
|
157
|
+
await params.app.client.chat.update({
|
|
158
|
+
channel: params.channelId,
|
|
159
|
+
ts: params.messageTs,
|
|
160
|
+
text: truncateSlackText(params.text, SLACK_CHAT_UPDATE_TEXT_LIMIT),
|
|
161
|
+
blocks: params.blocks
|
|
162
|
+
});
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logError(`slack exec approvals: failed to update message: ${String(err)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const slackApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
|
|
168
|
+
eventKinds: ["exec"],
|
|
169
|
+
availability: {
|
|
170
|
+
isConfigured: (params) => {
|
|
171
|
+
const resolved = resolveHandlerContext(params);
|
|
172
|
+
return resolved ? isSlackExecApprovalClientEnabled({
|
|
173
|
+
cfg: params.cfg,
|
|
174
|
+
accountId: resolved.accountId
|
|
175
|
+
}) : false;
|
|
176
|
+
},
|
|
177
|
+
shouldHandle: (params) => {
|
|
178
|
+
const resolved = resolveHandlerContext(params);
|
|
179
|
+
if (!resolved) return false;
|
|
180
|
+
return shouldHandleSlackExecApprovalRequest({
|
|
181
|
+
cfg: params.cfg,
|
|
182
|
+
accountId: resolved.accountId,
|
|
183
|
+
request: params.request
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
presentation: {
|
|
188
|
+
buildPendingPayload: ({ view }) => ({
|
|
189
|
+
text: buildSlackPendingApprovalText(view),
|
|
190
|
+
blocks: buildSlackPendingApprovalBlocks(view)
|
|
191
|
+
}),
|
|
192
|
+
buildResolvedResult: ({ view }) => ({
|
|
193
|
+
kind: "update",
|
|
194
|
+
payload: {
|
|
195
|
+
text: buildSlackResolvedText(view),
|
|
196
|
+
blocks: buildSlackResolvedBlocks(view)
|
|
197
|
+
}
|
|
198
|
+
}),
|
|
199
|
+
buildExpiredResult: ({ view }) => ({
|
|
200
|
+
kind: "update",
|
|
201
|
+
payload: {
|
|
202
|
+
text: buildSlackExpiredText(view),
|
|
203
|
+
blocks: buildSlackExpiredBlocks(view)
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
transport: {
|
|
208
|
+
prepareTarget: ({ plannedTarget }) => ({
|
|
209
|
+
dedupeKey: buildChannelApprovalNativeTargetKey(plannedTarget.target),
|
|
210
|
+
target: {
|
|
211
|
+
to: plannedTarget.target.to,
|
|
212
|
+
threadTs: plannedTarget.target.threadId != null ? String(plannedTarget.target.threadId) : void 0
|
|
213
|
+
}
|
|
214
|
+
}),
|
|
215
|
+
deliverPending: async ({ cfg, accountId, context, preparedTarget, pendingPayload }) => {
|
|
216
|
+
const resolved = resolveHandlerContext({
|
|
217
|
+
cfg,
|
|
218
|
+
accountId,
|
|
219
|
+
context
|
|
220
|
+
});
|
|
221
|
+
if (!resolved) return null;
|
|
222
|
+
const message = await sendMessageSlack(preparedTarget.to, pendingPayload.text, {
|
|
223
|
+
cfg,
|
|
224
|
+
accountId: resolved.accountId,
|
|
225
|
+
threadTs: preparedTarget.threadTs,
|
|
226
|
+
blocks: pendingPayload.blocks,
|
|
227
|
+
client: resolved.context.app.client
|
|
228
|
+
});
|
|
229
|
+
return {
|
|
230
|
+
channelId: message.channelId,
|
|
231
|
+
messageTs: message.messageId
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
updateEntry: async ({ cfg, accountId, context, entry, payload }) => {
|
|
235
|
+
const resolved = resolveHandlerContext({
|
|
236
|
+
cfg,
|
|
237
|
+
accountId,
|
|
238
|
+
context
|
|
239
|
+
});
|
|
240
|
+
if (!resolved) return;
|
|
241
|
+
const nextPayload = payload;
|
|
242
|
+
await updateMessage({
|
|
243
|
+
app: resolved.context.app,
|
|
244
|
+
channelId: entry.channelId,
|
|
245
|
+
messageTs: entry.messageTs,
|
|
246
|
+
text: nextPayload.text,
|
|
247
|
+
blocks: nextPayload.blocks
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
observe: { onDeliveryError: ({ error, request }) => {
|
|
252
|
+
logError(`slack exec approvals: failed to deliver approval ${request.id}: ${String(error)}`);
|
|
253
|
+
} }
|
|
254
|
+
});
|
|
255
|
+
//#endregion
|
|
256
|
+
export { slackApprovalNativeRuntime };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region extensions/slack/src/blocks-input.ts
|
|
2
|
+
const SLACK_MAX_BLOCKS = 50;
|
|
3
|
+
function parseBlocksJson(raw) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(raw);
|
|
6
|
+
} catch {
|
|
7
|
+
throw new Error("blocks must be valid JSON");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function assertBlocksArray(raw) {
|
|
11
|
+
if (!Array.isArray(raw)) throw new Error("blocks must be an array");
|
|
12
|
+
if (raw.length === 0) throw new Error("blocks must contain at least one block");
|
|
13
|
+
if (raw.length > 50) throw new Error(`blocks cannot exceed 50 items`);
|
|
14
|
+
for (const block of raw) {
|
|
15
|
+
if (!block || typeof block !== "object" || Array.isArray(block)) throw new Error("each block must be an object");
|
|
16
|
+
const type = block.type;
|
|
17
|
+
if (typeof type !== "string" || type.trim().length === 0) throw new Error("each block must include a non-empty string type");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function validateSlackBlocksArray(raw) {
|
|
21
|
+
assertBlocksArray(raw);
|
|
22
|
+
return raw;
|
|
23
|
+
}
|
|
24
|
+
function parseSlackBlocksInput(raw) {
|
|
25
|
+
if (raw == null) return;
|
|
26
|
+
return validateSlackBlocksArray(typeof raw === "string" ? parseBlocksJson(raw) : raw);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { parseSlackBlocksInput as n, validateSlackBlocksArray as r, SLACK_MAX_BLOCKS as t };
|