@openclaw/slack 2026.5.12 → 2026.5.14-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.
Files changed (87) hide show
  1. package/dist/{account-inspect-D7AZNs8C.js → account-inspect-BJyQLSkN.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{accounts-ClAPP5ry.js → accounts-yk5K3wQU.js} +2 -1
  4. package/dist/accounts.runtime-BhbEu1ZK.js +2 -0
  5. package/dist/{action-runtime-e2UhRsNx.js → action-runtime-CyE2jfW-.js} +4 -4
  6. package/dist/action-runtime.runtime-DLhfKw4B.js +2 -0
  7. package/dist/{media-D1XCd1uP.js → actions-BibhOXpJ.js} +339 -23
  8. package/dist/{actions.runtime-CO3OaTLb.js → actions.runtime-B9XQG6X4.js} +1 -1
  9. package/dist/api.js +18 -18
  10. package/dist/{approval-handler.runtime-CmeRr9qA.js → approval-handler.runtime-BjzVRaXN.js} +3 -4
  11. package/dist/{blocks-render-BIDw-Pom.js → blocks-render-BAVfd6r0.js} +1 -1
  12. package/dist/{channel-DRjHBTDB.js → channel-Dh07mU_K.js} +24 -52
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel.setup-Cayn7afd.js → channel.setup-CmG37S2W.js} +4 -4
  16. package/dist/{client-CPe4GmDR.js → client-C_IaJbi5.js} +20 -1
  17. package/dist/{config-schema-D9B5LB_L.js → config-schema-CNRousxw.js} +21 -1
  18. package/dist/contract-api.js +4 -4
  19. package/dist/{directory-config-B3JiHeB7.js → directory-config-CMvFiswf.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-Bf16GwDh.js → directory-live-CZPzpQZF.js} +2 -2
  22. package/dist/{doctor-contract-KUjHnkQm.js → doctor-contract-B8QIWMs1.js} +1 -1
  23. package/dist/doctor-contract-api.js +1 -1
  24. package/dist/http-routes-api.js +1 -1
  25. package/dist/inbound-contract-test-api.js +2 -2
  26. package/dist/{interactive-replies-qAIfuBor.js → interactive-replies-BSg5hXhj.js} +1 -1
  27. package/dist/interactive-replies-api.js +1 -1
  28. package/dist/{message-tool-api-6lowf9zE.js → message-tool-api-C7gc7goF.js} +2 -2
  29. package/dist/message-tool-api.js +1 -1
  30. package/dist/{monitor-a97o17G6.js → monitor-DDE5AI2O.js} +3 -3
  31. package/dist/{outbound-adapter-B_5sEhCg.js → outbound-adapter-BluPNDNi.js} +4 -5
  32. package/dist/outbound-payload-test-api.js +1 -1
  33. package/dist/{outbound-payload.test-harness-CVCamg1x.js → outbound-payload.test-harness-BNxnP6MC.js} +2 -2
  34. package/dist/{pipeline.runtime-DT0hLnq2.js → pipeline.runtime-Dft2-QU4.js} +48 -22
  35. package/dist/{plugin-routes-DtTPmga1.js → plugin-routes-CRnfsTTX.js} +2 -2
  36. package/dist/{prepare-D3YqV8jB.js → prepare-D0tMg4dt.js} +9 -10
  37. package/dist/{prepare.test-helpers-DVcjRhfG.js → prepare.test-helpers-CU1qB54Q.js} +1 -1
  38. package/dist/{probe-3eZf1FjI.js → probe-FL4sUJsH.js} +2 -2
  39. package/dist/{provider-D7uAN3Fq.js → provider-C6WxaFFf.js} +69 -30
  40. package/dist/{replies-Xe_jMR6o.js → replies-D0QXXSPP.js} +4 -4
  41. package/dist/reply-blocks-BFaJ_ejG.js +134 -0
  42. package/dist/{resolve-channels-BRYqyNVJ.js → resolve-channels-B_eKaOkE.js} +2 -2
  43. package/dist/{resolve-users-Bd_SdP8j.js → resolve-users-BzBAJwvq.js} +2 -2
  44. package/dist/{room-context-0vovmZPU.js → room-context-Cd8jFpS-.js} +4 -2
  45. package/dist/{runtime-api-Dd1xIV5v.js → runtime-api-B5HGOzX3.js} +2 -2
  46. package/dist/runtime-api.js +13 -13
  47. package/dist/runtime-setter-api.js +1 -1
  48. package/dist/{scopes-CDevO8jg.js → scopes-Bvg_ZzqZ.js} +2 -2
  49. package/dist/secret-contract-api.js +1 -1
  50. package/dist/security-contract-api.js +1 -1
  51. package/dist/{send-D_A9kL-C.js → send-Dg9zcyYT.js} +5 -6
  52. package/dist/send.runtime-CjjQ9StM.js +2 -0
  53. package/dist/send.runtime-E47jGN-2.js +2 -0
  54. package/dist/{setup-core-B9NetDkM.js → setup-core-WWQl-cE9.js} +2 -2
  55. package/dist/setup-plugin-api.js +1 -1
  56. package/dist/{setup-surface-D88QBVOW.js → setup-surface-BLoTgna4.js} +5 -5
  57. package/dist/{shared-D8U42xFL.js → shared-GoB-OuUq.js} +9 -9
  58. package/dist/{slash-dispatch.runtime-BJgT0jwV.js → slash-dispatch.runtime-Bz_OkRcR.js} +1 -1
  59. package/dist/test-api.js +7 -7
  60. package/dist/thread-ts-As_dcNbD.js +52 -0
  61. package/openclaw.plugin.json +116 -0
  62. package/package.json +9 -7
  63. package/dist/accounts.runtime-DDVcLJUI.js +0 -2
  64. package/dist/action-runtime.runtime-BFcqMbOm.js +0 -2
  65. package/dist/actions-CYLFK-Zy.js +0 -292
  66. package/dist/blocks-input-CwTFVImV.js +0 -29
  67. package/dist/channel-api-B_nZwosg.js +0 -20
  68. package/dist/exec-approvals-7xUNgLi9.js +0 -58
  69. package/dist/group-policy-CyLUK6My.js +0 -41
  70. package/dist/mrkdwn-Cax-eSfK.js +0 -6
  71. package/dist/reply-blocks-Z5l6_R6H.js +0 -14
  72. package/dist/send.runtime-BRE_ncCU.js +0 -2
  73. package/dist/send.runtime-_l76lUuL.js +0 -2
  74. package/dist/thread-ts-C2x7c5PP.js +0 -24
  75. /package/dist/{allow-list-BPnnlRPL.js → allow-list-nwXs_eCP.js} +0 -0
  76. /package/dist/{config-api-B_jq4NJW.js → config-api-CmgE_ORg.js} +0 -0
  77. /package/dist/{errors-BYFHR24f.js → errors-C_sW0Zgl.js} +0 -0
  78. /package/dist/{magic-string.es-BMaGRRZ1.js → magic-string.es-9lw4MGoF.js} +0 -0
  79. /package/dist/{registry-CeaoNfoP.js → registry-D2cWOLZV.js} +0 -0
  80. /package/dist/{resolve-allowlist-common-Bk3clYPK.js → resolve-allowlist-common-DLub2I2i.js} +0 -0
  81. /package/dist/{runtime-Bo-KHM-F.js → runtime-DQxkf7k2.js} +0 -0
  82. /package/dist/{secret-contract-Bo6lbSkh.js → secret-contract-0TL3L5Kb.js} +0 -0
  83. /package/dist/{security-audit-BtHGnD3d.js → security-audit-BJzADAw3.js} +0 -0
  84. /package/dist/{slash-commands.runtime-22kgyst2.js → slash-commands.runtime-bcDwsGnu.js} +0 -0
  85. /package/dist/{slash-plugin-commands.runtime-CF-n3MeP.js → slash-plugin-commands.runtime-DBHGUSj2.js} +0 -0
  86. /package/dist/{slash-skill-commands.runtime-BMs0VjTe.js → slash-skill-commands.runtime-rrY2hXvN.js} +0 -0
  87. /package/dist/{streaming-compat-RkZgTmQ2.js → streaming-compat-eu5Rj5gj.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { i as resolveDefaultSlackAccountId, r as mergeSlackAccountConfig } from "./accounts-ClAPP5ry.js";
1
+ import { i as resolveDefaultSlackAccountId, r as mergeSlackAccountConfig } from "./accounts-yk5K3wQU.js";
2
2
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
3
3
  import { hasConfiguredSecretInput, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
4
4
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -1,4 +1,4 @@
1
- import { t as inspectSlackAccount } from "./account-inspect-D7AZNs8C.js";
1
+ import { t as inspectSlackAccount } from "./account-inspect-BJyQLSkN.js";
2
2
  //#region extensions/slack/account-inspect-api.ts
3
3
  function inspectSlackReadOnlyAccount(cfg, accountId) {
4
4
  return inspectSlackAccount({
@@ -48,7 +48,8 @@ function mergeSlackAccountConfig(cfg, accountId) {
48
48
  return resolveMergedAccountConfig({
49
49
  channelConfig: cfg.channels?.slack,
50
50
  accounts: cfg.channels?.slack?.accounts,
51
- accountId
51
+ accountId,
52
+ nestedObjectKeys: ["botLoopProtection"]
52
53
  });
53
54
  }
54
55
  function resolveSlackAccountAllowFrom(params) {
@@ -0,0 +1,2 @@
1
+ import { a as resolveSlackAccount } from "./accounts-yk5K3wQU.js";
2
+ export { resolveSlackAccount };
@@ -1,7 +1,7 @@
1
1
  import { i as resolveSlackChannelId, r as parseSlackTarget } from "./target-parsing-CQmv-iSm.js";
2
2
  import "./targets-B1tYCAr6.js";
3
- import { n as parseSlackBlocksInput } from "./blocks-input-CwTFVImV.js";
4
- import { a as readNumberParam, c as withNormalizedTimestamp, n as imageResultFromFile, o as readReactionParams, r as jsonResult, s as readStringParam, t as createActionGate } from "./runtime-api-Dd1xIV5v.js";
3
+ import { i as parseSlackBlocksInput } from "./thread-ts-As_dcNbD.js";
4
+ import { a as readNumberParam, c as withNormalizedTimestamp, n as imageResultFromFile, o as readReactionParams, r as jsonResult, s as readStringParam, t as createActionGate } from "./runtime-api-B5HGOzX3.js";
5
5
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { isSingleUseReplyToMode } from "openclaw/plugin-sdk/reply-reference";
7
7
  import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param";
@@ -28,11 +28,11 @@ function sameSlackChannelTarget(targetChannel, currentChannelId) {
28
28
  let slackActionsRuntimePromise;
29
29
  let slackAccountsRuntimePromise;
30
30
  function loadSlackActionsRuntime() {
31
- slackActionsRuntimePromise ??= import("./actions.runtime-CO3OaTLb.js");
31
+ slackActionsRuntimePromise ??= import("./actions.runtime-B9XQG6X4.js");
32
32
  return slackActionsRuntimePromise;
33
33
  }
34
34
  function loadSlackAccountsRuntime() {
35
- slackAccountsRuntimePromise ??= import("./accounts.runtime-DDVcLJUI.js");
35
+ slackAccountsRuntimePromise ??= import("./accounts.runtime-BhbEu1ZK.js");
36
36
  return slackAccountsRuntimePromise;
37
37
  }
38
38
  function createLazySlackAction(key) {
@@ -0,0 +1,2 @@
1
+ import { t as handleSlackAction } from "./action-runtime-CyE2jfW-.js";
2
+ export { handleSlackAction };
@@ -1,12 +1,27 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
+ import { a as resolveSlackAccount, d as resolveSlackBotToken } from "./accounts-yk5K3wQU.js";
3
+ import { a as validateSlackBlocksArray, o as SLACK_TEXT_LIMIT, s as truncateSlackText } from "./thread-ts-As_dcNbD.js";
4
+ import { a as getSlackWriteClient, r as createSlackWebClient } from "./client-C_IaJbi5.js";
5
+ import { c as buildSlackBlocksFallbackText, t as sendMessageSlack } from "./send-Dg9zcyYT.js";
2
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
- import { logVerbose as logVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
7
+ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
8
+ import { logVerbose, logVerbose as logVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
9
+ import { z } from "zod";
10
+ import fs from "node:fs/promises";
4
11
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
12
  import { normalizeHostname } from "openclaw/plugin-sdk/host-runtime";
6
13
  import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
7
14
  import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
8
- import { fetchRemoteMedia, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
15
+ import { saveRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
9
16
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
17
+ //#region extensions/slack/src/edit-text.ts
18
+ function buildSlackEditTextPayload(content, blocks) {
19
+ const trimmedContent = content.trim();
20
+ if (trimmedContent) return trimmedContent;
21
+ if (blocks?.length) return truncateSlackText(buildSlackBlocksFallbackText(blocks), SLACK_TEXT_LIMIT);
22
+ return " ";
23
+ }
24
+ //#endregion
10
25
  //#region extensions/slack/src/file-reference.ts
11
26
  function formatSlackFileReference(file) {
12
27
  const name = normalizeOptionalString(file?.name) ?? "file";
@@ -234,7 +249,7 @@ function mergeAbortSignals(signals) {
234
249
  for (const signal of activeSignals) signal.addEventListener("abort", abort, { once: true });
235
250
  return controller.signal;
236
251
  }
237
- async function fetchSlackMedia(params) {
252
+ async function saveSlackMedia(params) {
238
253
  const timeoutAbortController = params.totalTimeoutMs ? new AbortController() : void 0;
239
254
  const signal = mergeAbortSignals([
240
255
  params.abortSignal,
@@ -243,7 +258,7 @@ async function fetchSlackMedia(params) {
243
258
  ]);
244
259
  let timedOut = false;
245
260
  let timeoutHandle = null;
246
- const fetchPromise = fetchRemoteMedia({
261
+ const savePromise = saveRemoteMedia({
247
262
  ...params.options,
248
263
  readIdleTimeoutMs: params.readIdleTimeoutMs ?? 6e4,
249
264
  ...signal ? { requestInit: {
@@ -255,7 +270,7 @@ async function fetchSlackMedia(params) {
255
270
  throw error;
256
271
  });
257
272
  try {
258
- if (!params.totalTimeoutMs) return await fetchPromise;
273
+ if (!params.totalTimeoutMs) return await savePromise;
259
274
  const timeoutPromise = new Promise((_, reject) => {
260
275
  timeoutHandle = setTimeout(() => {
261
276
  timedOut = true;
@@ -264,7 +279,7 @@ async function fetchSlackMedia(params) {
264
279
  }, params.totalTimeoutMs);
265
280
  timeoutHandle.unref?.();
266
281
  });
267
- return await Promise.race([fetchPromise, timeoutPromise]);
282
+ return await Promise.race([savePromise, timeoutPromise]);
268
283
  } finally {
269
284
  if (timeoutHandle) clearTimeout(timeoutHandle);
270
285
  }
@@ -284,6 +299,17 @@ function looksLikeHtmlBuffer(buffer) {
284
299
  const head = normalizeLowercaseStringOrEmpty(buffer.subarray(0, 512).toString("utf-8").replace(/^\s+/, ""));
285
300
  return head.startsWith("<!doctype html") || head.startsWith("<html");
286
301
  }
302
+ async function looksLikeHtmlFile(filePath) {
303
+ const handle = await fs.open(filePath, "r").catch(() => null);
304
+ if (!handle) return false;
305
+ try {
306
+ const buffer = Buffer.alloc(512);
307
+ const { bytesRead } = await handle.read(buffer, 0, buffer.byteLength, 0);
308
+ return looksLikeHtmlBuffer(buffer.subarray(0, bytesRead));
309
+ } finally {
310
+ await handle.close().catch(() => void 0);
311
+ }
312
+ }
287
313
  const MAX_SLACK_MEDIA_CONCURRENCY = 3;
288
314
  const MAX_SLACK_FORWARDED_ATTACHMENTS = 8;
289
315
  async function fetchFreshSlackFileUrl(params) {
@@ -304,12 +330,13 @@ async function fetchFreshSlackFileUrl(params) {
304
330
  }
305
331
  async function downloadSlackMediaFile(params) {
306
332
  const { url: slackUrl, requestInit } = createSlackMediaRequest(params.url, params.token);
307
- const fetched = await fetchSlackMedia({
333
+ const saved = await saveSlackMedia({
308
334
  options: {
309
335
  url: slackUrl,
310
336
  fetchImpl: createSlackMediaFetch(),
311
337
  requestInit,
312
338
  filePathHint: params.file.name,
339
+ fallbackContentType: resolveSlackMediaMimetype(params.file, params.file.mimetype),
313
340
  maxBytes: params.maxBytes,
314
341
  ssrfPolicy: SLACK_MEDIA_SSRF_POLICY
315
342
  },
@@ -317,15 +344,16 @@ async function downloadSlackMediaFile(params) {
317
344
  totalTimeoutMs: params.totalTimeoutMs ?? 12e4,
318
345
  abortSignal: params.abortSignal
319
346
  });
320
- if (fetched.buffer.byteLength > params.maxBytes) return null;
321
347
  const fileMime = normalizeOptionalLowercaseString(params.file.mimetype);
322
348
  const fileName = normalizeLowercaseStringOrEmpty(params.file.name);
323
349
  if (!(fileMime === "text/html" || fileName.endsWith(".html") || fileName.endsWith(".htm"))) {
324
- if (normalizeOptionalLowercaseString(fetched.contentType?.split(";")[0]) === "text/html" || looksLikeHtmlBuffer(fetched.buffer)) return null;
350
+ if (normalizeOptionalLowercaseString(saved.contentType?.split(";")[0]) === "text/html" || await looksLikeHtmlFile(saved.path)) {
351
+ await fs.rm(saved.path, { force: true }).catch(() => void 0);
352
+ return null;
353
+ }
325
354
  }
326
- const effectiveMime = resolveSlackMediaMimetype(params.file, fetched.contentType);
327
- const saved = await saveMediaBuffer(fetched.buffer, effectiveMime, "inbound", params.maxBytes);
328
- const label = fetched.fileName ?? params.file.name;
355
+ const effectiveMime = resolveSlackMediaMimetype(params.file, saved.contentType);
356
+ const label = saved.fileName ?? params.file.name;
329
357
  const contentType = effectiveMime ?? saved.contentType;
330
358
  return {
331
359
  path: saved.path,
@@ -423,7 +451,7 @@ async function resolveSlackAttachmentContent(params) {
423
451
  const imageUrl = resolveForwardedAttachmentImageUrl(att);
424
452
  if (imageUrl) try {
425
453
  const { url: slackUrl, requestInit } = createSlackMediaRequest(imageUrl, params.token);
426
- const fetched = await fetchSlackMedia({
454
+ const saved = await saveSlackMedia({
427
455
  options: {
428
456
  url: slackUrl,
429
457
  fetchImpl: createSlackMediaFetch(),
@@ -435,15 +463,12 @@ async function resolveSlackAttachmentContent(params) {
435
463
  totalTimeoutMs: params.totalTimeoutMs ?? 12e4,
436
464
  abortSignal: params.abortSignal
437
465
  });
438
- if (fetched.buffer.byteLength <= params.maxBytes) {
439
- const saved = await saveMediaBuffer(fetched.buffer, fetched.contentType, "inbound", params.maxBytes);
440
- const label = fetched.fileName ?? "forwarded image";
441
- allMedia.push({
442
- path: saved.path,
443
- contentType: fetched.contentType ?? saved.contentType,
444
- placeholder: `[Forwarded image: ${label}]`
445
- });
446
- }
466
+ const label = saved.fileName ?? "forwarded image";
467
+ allMedia.push({
468
+ path: saved.path,
469
+ contentType: saved.contentType,
470
+ placeholder: `[Forwarded image: ${label}]`
471
+ });
447
472
  } catch {}
448
473
  if (att.files && att.files.length > 0) {
449
474
  const fileMedia = await resolveSlackMedia({
@@ -466,4 +491,295 @@ async function resolveSlackAttachmentContent(params) {
466
491
  };
467
492
  }
468
493
  //#endregion
469
- export { MAX_SLACK_MEDIA_FILES as a, resolveSlackThreadStarter as i, resolveSlackMedia as n, formatSlackFileReference as o, resolveSlackThreadHistory as r, media_exports as t };
494
+ //#region extensions/slack/src/actions.ts
495
+ function resolveToken(explicit, accountId, cfg) {
496
+ if (explicit?.trim()) {
497
+ const token = resolveSlackBotToken(explicit);
498
+ if (token) return token;
499
+ }
500
+ 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.");
501
+ const account = resolveSlackAccount({
502
+ cfg: requireRuntimeConfig(cfg, "Slack actions"),
503
+ accountId
504
+ });
505
+ const token = resolveSlackBotToken(account.botToken ?? void 0);
506
+ if (!token) {
507
+ logVerbose(`slack actions: missing bot token for account=${account.accountId} explicit=${Boolean(explicit)} source=${account.botTokenSource ?? "unknown"}`);
508
+ throw new Error("SLACK_BOT_TOKEN or channels.slack.botToken is required for Slack actions");
509
+ }
510
+ return token;
511
+ }
512
+ function normalizeEmoji(raw) {
513
+ const trimmed = raw.trim();
514
+ if (!trimmed) throw new Error("Emoji is required for Slack reactions");
515
+ return trimmed.replace(/^:+|:+$/g, "");
516
+ }
517
+ const SLACK_TIMESTAMP_RE = /^\d+(?:\.\d+)?$/;
518
+ const ISO_8601_TIMESTAMP_SCHEMA = z.iso.datetime({ offset: true });
519
+ function formatEpochSeconds(milliseconds) {
520
+ const seconds = milliseconds / 1e3;
521
+ if (Number.isInteger(seconds)) return String(seconds);
522
+ return seconds.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
523
+ }
524
+ function normalizeSlackReadTimestamp(raw, field) {
525
+ const trimmed = raw?.trim();
526
+ if (!trimmed) return;
527
+ if (SLACK_TIMESTAMP_RE.test(trimmed)) return trimmed;
528
+ if (!ISO_8601_TIMESTAMP_SCHEMA.safeParse(trimmed).success) throw new Error(`Invalid Slack read ${field} timestamp "${trimmed}": expected a Slack timestamp or ISO-8601 date string`);
529
+ const parsed = Date.parse(trimmed);
530
+ if (!Number.isFinite(parsed)) throw new Error(`Invalid Slack read ${field} timestamp "${trimmed}": expected a Slack timestamp or ISO-8601 date string`);
531
+ return formatEpochSeconds(parsed);
532
+ }
533
+ function hasSlackPlatformError(err, code) {
534
+ if (!err || typeof err !== "object") return false;
535
+ const data = err.data;
536
+ if (!data || typeof data !== "object") return false;
537
+ return data.error === code;
538
+ }
539
+ async function getClient(opts = {}, mode = "read") {
540
+ if (opts.client) return opts.client;
541
+ const token = resolveToken(opts.token, opts.accountId, opts.cfg);
542
+ return mode === "write" ? getSlackWriteClient(token) : createSlackWebClient(token);
543
+ }
544
+ async function resolveBotUserId(client) {
545
+ const auth = await client.auth.test();
546
+ if (!auth?.user_id) throw new Error("Failed to resolve Slack bot user id");
547
+ return auth.user_id;
548
+ }
549
+ async function reactSlackMessage(channelId, messageId, emoji, opts = {}) {
550
+ const client = await getClient(opts, "write");
551
+ try {
552
+ await client.reactions.add({
553
+ channel: channelId,
554
+ timestamp: messageId,
555
+ name: normalizeEmoji(emoji)
556
+ });
557
+ } catch (err) {
558
+ if (hasSlackPlatformError(err, "already_reacted")) return;
559
+ throw err;
560
+ }
561
+ }
562
+ async function removeSlackReaction(channelId, messageId, emoji, opts = {}) {
563
+ const client = await getClient(opts, "write");
564
+ try {
565
+ await client.reactions.remove({
566
+ channel: channelId,
567
+ timestamp: messageId,
568
+ name: normalizeEmoji(emoji)
569
+ });
570
+ } catch (err) {
571
+ if (hasSlackPlatformError(err, "no_reaction")) return;
572
+ throw err;
573
+ }
574
+ }
575
+ async function removeOwnSlackReactions(channelId, messageId, opts = {}) {
576
+ const client = await getClient(opts, "write");
577
+ const userId = await resolveBotUserId(client);
578
+ const reactions = await listSlackReactions(channelId, messageId, { client });
579
+ const toRemove = /* @__PURE__ */ new Set();
580
+ for (const reaction of reactions ?? []) {
581
+ const name = reaction?.name;
582
+ if (!name) continue;
583
+ if ((reaction?.users ?? []).includes(userId)) toRemove.add(name);
584
+ }
585
+ if (toRemove.size === 0) return [];
586
+ await Promise.all(Array.from(toRemove, (name) => removeSlackReaction(channelId, messageId, name, {
587
+ ...opts,
588
+ client
589
+ })));
590
+ return Array.from(toRemove);
591
+ }
592
+ async function listSlackReactions(channelId, messageId, opts = {}) {
593
+ return (await (await getClient(opts)).reactions.get({
594
+ channel: channelId,
595
+ timestamp: messageId,
596
+ full: true
597
+ })).message?.reactions ?? [];
598
+ }
599
+ async function sendSlackMessage(to, content, opts) {
600
+ return await sendMessageSlack(to, content, {
601
+ accountId: opts.accountId,
602
+ cfg: opts.cfg,
603
+ token: opts.token,
604
+ mediaUrl: opts.mediaUrl,
605
+ mediaAccess: opts.mediaAccess,
606
+ mediaLocalRoots: opts.mediaLocalRoots,
607
+ mediaReadFile: opts.mediaReadFile,
608
+ client: opts.client,
609
+ threadTs: opts.threadTs,
610
+ replyBroadcast: opts.replyBroadcast,
611
+ ...opts.uploadFileName ? { uploadFileName: opts.uploadFileName } : {},
612
+ ...opts.uploadTitle ? { uploadTitle: opts.uploadTitle } : {},
613
+ blocks: opts.blocks
614
+ });
615
+ }
616
+ async function editSlackMessage(channelId, messageId, content, opts = {}) {
617
+ const client = await getClient(opts, "write");
618
+ const blocks = opts.blocks == null ? void 0 : validateSlackBlocksArray(opts.blocks);
619
+ await client.chat.update({
620
+ channel: channelId,
621
+ ts: messageId,
622
+ text: buildSlackEditTextPayload(content, blocks),
623
+ ...blocks ? { blocks } : {}
624
+ });
625
+ }
626
+ async function deleteSlackMessage(channelId, messageId, opts = {}) {
627
+ await (await getClient(opts, "write")).chat.delete({
628
+ channel: channelId,
629
+ ts: messageId
630
+ });
631
+ }
632
+ async function readSlackMessages(channelId, opts = {}) {
633
+ const exactMessageId = opts.messageId?.trim();
634
+ const readLimit = exactMessageId ? 1 : opts.limit;
635
+ const exactBounds = exactMessageId ? {
636
+ inclusive: true,
637
+ latest: exactMessageId,
638
+ oldest: void 0
639
+ } : {
640
+ latest: normalizeSlackReadTimestamp(opts.before, "before"),
641
+ oldest: normalizeSlackReadTimestamp(opts.after, "after")
642
+ };
643
+ const client = await getClient(opts);
644
+ if (opts.threadId) {
645
+ const result = await client.conversations.replies({
646
+ channel: channelId,
647
+ ts: opts.threadId,
648
+ limit: readLimit,
649
+ ...exactBounds
650
+ });
651
+ return {
652
+ messages: (result.messages ?? []).filter((message) => {
653
+ if (exactMessageId) return message.ts === exactMessageId;
654
+ return message.ts !== opts.threadId;
655
+ }),
656
+ hasMore: exactMessageId ? false : Boolean(result.has_more)
657
+ };
658
+ }
659
+ const result = await client.conversations.history({
660
+ channel: channelId,
661
+ limit: readLimit,
662
+ ...exactBounds
663
+ });
664
+ return {
665
+ messages: (result.messages ?? []).filter((message) => !exactMessageId || message.ts === exactMessageId),
666
+ hasMore: exactMessageId ? false : Boolean(result.has_more)
667
+ };
668
+ }
669
+ async function getSlackMemberInfo(userId, opts = {}) {
670
+ return await (await getClient(opts)).users.info({ user: userId });
671
+ }
672
+ async function listSlackEmojis(opts = {}) {
673
+ return await (await getClient(opts)).emoji.list();
674
+ }
675
+ async function pinSlackMessage(channelId, messageId, opts = {}) {
676
+ await (await getClient(opts, "write")).pins.add({
677
+ channel: channelId,
678
+ timestamp: messageId
679
+ });
680
+ }
681
+ async function unpinSlackMessage(channelId, messageId, opts = {}) {
682
+ await (await getClient(opts, "write")).pins.remove({
683
+ channel: channelId,
684
+ timestamp: messageId
685
+ });
686
+ }
687
+ async function listSlackPins(channelId, opts = {}) {
688
+ return (await (await getClient(opts)).pins.list({ channel: channelId })).items ?? [];
689
+ }
690
+ function normalizeSlackScopeValue(value) {
691
+ const trimmed = value?.trim();
692
+ return trimmed ? trimmed : void 0;
693
+ }
694
+ function collectSlackDirectShareChannelIds(file) {
695
+ const ids = /* @__PURE__ */ new Set();
696
+ for (const group of [
697
+ file.channels,
698
+ file.groups,
699
+ file.ims
700
+ ]) {
701
+ if (!Array.isArray(group)) continue;
702
+ for (const entry of group) {
703
+ if (typeof entry !== "string") continue;
704
+ const normalized = normalizeSlackScopeValue(entry);
705
+ if (normalized) ids.add(normalized);
706
+ }
707
+ }
708
+ return ids;
709
+ }
710
+ function collectSlackShareMaps(file) {
711
+ if (!file.shares || typeof file.shares !== "object" || Array.isArray(file.shares)) return [];
712
+ const shares = file.shares;
713
+ return [shares.public, shares.private].filter((value) => Boolean(value) && typeof value === "object" && !Array.isArray(value));
714
+ }
715
+ function collectSlackSharedChannelIds(file) {
716
+ const ids = /* @__PURE__ */ new Set();
717
+ for (const shareMap of collectSlackShareMaps(file)) for (const channelId of Object.keys(shareMap)) {
718
+ const normalized = normalizeSlackScopeValue(channelId);
719
+ if (normalized) ids.add(normalized);
720
+ }
721
+ return ids;
722
+ }
723
+ function collectSlackThreadShares(file, channelId) {
724
+ const matches = [];
725
+ for (const shareMap of collectSlackShareMaps(file)) {
726
+ const rawEntries = shareMap[channelId];
727
+ if (!Array.isArray(rawEntries)) continue;
728
+ for (const rawEntry of rawEntries) {
729
+ if (!rawEntry || typeof rawEntry !== "object" || Array.isArray(rawEntry)) continue;
730
+ const entry = rawEntry;
731
+ const ts = typeof entry.ts === "string" ? normalizeSlackScopeValue(entry.ts) : void 0;
732
+ const threadTs = typeof entry.thread_ts === "string" ? normalizeSlackScopeValue(entry.thread_ts) : void 0;
733
+ matches.push({
734
+ channelId,
735
+ ts,
736
+ threadTs
737
+ });
738
+ }
739
+ }
740
+ return matches;
741
+ }
742
+ function hasSlackScopeMismatch(params) {
743
+ const channelId = normalizeSlackScopeValue(params.channelId);
744
+ if (!channelId) return false;
745
+ const threadId = normalizeSlackScopeValue(params.threadId);
746
+ const directIds = collectSlackDirectShareChannelIds(params.file);
747
+ const sharedIds = collectSlackSharedChannelIds(params.file);
748
+ const hasChannelEvidence = directIds.size > 0 || sharedIds.size > 0;
749
+ const inChannel = directIds.has(channelId) || sharedIds.has(channelId);
750
+ if (hasChannelEvidence && !inChannel) return true;
751
+ if (!threadId) return false;
752
+ const threadShares = collectSlackThreadShares(params.file, channelId);
753
+ if (threadShares.length === 0) return false;
754
+ const threadEvidence = threadShares.filter((entry) => entry.threadTs || entry.ts);
755
+ if (threadEvidence.length === 0) return false;
756
+ return !threadEvidence.some((entry) => entry.threadTs === threadId || entry.ts === threadId);
757
+ }
758
+ /**
759
+ * Downloads a Slack file by ID and saves it to the local media store.
760
+ * Fetches a fresh download URL via files.info to avoid using stale private URLs.
761
+ * Returns null when the file cannot be found or downloaded.
762
+ */
763
+ async function downloadSlackFile(fileId, opts) {
764
+ const token = resolveToken(opts.token, opts.accountId, opts.cfg);
765
+ const file = (await (await getClient(opts)).files.info({ file: fileId })).file;
766
+ if (!file?.url_private_download && !file?.url_private) return null;
767
+ if (hasSlackScopeMismatch({
768
+ file,
769
+ channelId: opts.channelId,
770
+ threadId: opts.threadId
771
+ })) return null;
772
+ return (await resolveSlackMedia({
773
+ files: [{
774
+ id: file.id,
775
+ name: file.name,
776
+ mimetype: file.mimetype,
777
+ url_private: file.url_private,
778
+ url_private_download: file.url_private_download
779
+ }],
780
+ token,
781
+ maxBytes: opts.maxBytes
782
+ }))?.[0] ?? null;
783
+ }
784
+ //#endregion
785
+ export { resolveSlackThreadStarter as _, listSlackEmojis as a, buildSlackEditTextPayload as b, pinSlackMessage as c, removeOwnSlackReactions as d, removeSlackReaction as f, resolveSlackThreadHistory as g, media_exports 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, MAX_SLACK_MEDIA_FILES as v, formatSlackFileReference as y };
@@ -1,2 +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";
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-BibhOXpJ.js";
2
2
  export { deleteSlackMessage, downloadSlackFile, editSlackMessage, getSlackMemberInfo, listSlackEmojis, listSlackPins, listSlackReactions, pinSlackMessage, reactSlackMessage, readSlackMessages, removeOwnSlackReactions, removeSlackReaction, sendSlackMessage, unpinSlackMessage };
package/dist/api.js CHANGED
@@ -1,21 +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";
1
+ import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, l as resolveSlackReplyToMode, n as listSlackAccountIds, r as mergeSlackAccountConfig, t as listEnabledSlackAccounts } from "./accounts-yk5K3wQU.js";
2
+ import { t as inspectSlackAccount } from "./account-inspect-BJyQLSkN.js";
3
3
  import { i as resolveSlackChannelId, n as normalizeSlackMessagingTarget, r as parseSlackTarget, t as looksLikeSlackTargetId } from "./target-parsing-CQmv-iSm.js";
4
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";
5
+ import { i as resolveSlackChannelType, n as buildSlackThreadingToolContext, o as resolveSlackAutoThreadId, r as __resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-Dh07mU_K.js";
6
+ import { n as resolveSlackGroupRequireMention, r as resolveSlackGroupToolPolicy } from "./reply-blocks-BFaJ_ejG.js";
7
+ import { n as buildSlackPresentationBlocks, t as buildSlackInteractiveBlocks } from "./blocks-render-BAVfd6r0.js";
8
+ import { a as validateSlackBlocksArray, i as parseSlackBlocksInput, r as SLACK_MAX_BLOCKS } from "./thread-ts-As_dcNbD.js";
9
+ import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-C7gc7goF.js";
10
+ import { n as isSlackInteractiveRepliesEnabled, r as parseSlackOptionsLine, t as compileSlackInteractiveReplies } from "./interactive-replies-BSg5hXhj.js";
11
+ 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-C_IaJbi5.js";
12
+ 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-nwXs_eCP.js";
13
+ import { t as slackSetupPlugin } from "./channel.setup-CmG37S2W.js";
14
+ import { a as recordSlackThreadParticipation, n as clearSlackThreadParticipationCache, r as hasSlackThreadParticipation } from "./send-Dg9zcyYT.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-BibhOXpJ.js";
16
+ import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-CMvFiswf.js";
17
+ import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-D2cWOLZV.js";
18
+ import { t as probeSlack } from "./probe-FL4sUJsH.js";
19
+ import { t as collectSlackSecurityAuditFindings } from "./security-audit-BJzADAw3.js";
20
+ import { n as resolveSlackRuntimeGroupPolicy } from "./provider-C6WxaFFf.js";
21
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 };
@@ -1,7 +1,6 @@
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";
1
+ import { c as normalizeSlackApproverId, s as isSlackExecApprovalClientEnabled, t as resolveSlackReplyBlocks, u as shouldHandleSlackExecApprovalRequest } from "./reply-blocks-BFaJ_ejG.js";
2
+ import { s as truncateSlackText } from "./thread-ts-As_dcNbD.js";
3
+ import { t as sendMessageSlack } from "./send-Dg9zcyYT.js";
5
4
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
5
  import { buildChannelApprovalNativeTargetKey } from "openclaw/plugin-sdk/approval-native-runtime";
7
6
  import { logError } from "openclaw/plugin-sdk/logging-core";
@@ -1,4 +1,4 @@
1
- import { i as truncateSlackText } from "./thread-ts-C2x7c5PP.js";
1
+ import { s as truncateSlackText } from "./thread-ts-As_dcNbD.js";
2
2
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { presentationToInteractiveControlsReply, reduceInteractiveReply } from "openclaw/plugin-sdk/interactive-runtime";
4
4
  //#region extensions/slack/src/reply-action-ids.ts