@openclaw/feishu 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 (28) hide show
  1. package/dist/api.js +5 -5
  2. package/dist/{channel-COlXCApz.js → channel-1pldyM9r.js} +369 -18
  3. package/dist/channel-plugin-api.js +1 -1
  4. package/dist/{channel.runtime-Bt1Lk06Y.js → channel.runtime-BlJ_XJTS.js} +5 -6
  5. package/dist/contract-api.js +3 -3
  6. package/dist/{drive-BocW3yO6.js → drive-Av1h_Lwd.js} +1 -1
  7. package/dist/{monitor-BZgAq8ET.js → monitor-Dfy3D2pM.js} +2 -2
  8. package/dist/{monitor.account-CI4_rDnC.js → monitor.account-CT3Skl8y.js} +62 -36
  9. package/dist/{monitor.state-D1WkvOGG.js → monitor.state-D6ByOM5W.js} +1 -1
  10. package/dist/{probe-BNzzU_uR.js → probe-DpPNslkb.js} +1 -1
  11. package/dist/runtime-api.js +1 -1
  12. package/dist/secret-contract-api.js +1 -1
  13. package/dist/{security-audit-DqJdocrN.js → security-audit-BzTLFO6g.js} +1 -1
  14. package/dist/security-contract-api.js +1 -1
  15. package/dist/{send-DiFpqzBJ.js → send-DsCQVFOE.js} +37 -37
  16. package/dist/session-key-api.js +1 -1
  17. package/dist/setup-api.js +1 -1
  18. package/package.json +5 -5
  19. package/dist/directory.static-DqfUoZrD.js +0 -44
  20. package/dist/policy-iDTxfTYv.js +0 -181
  21. package/dist/send-result-zZZOR3qT.js +0 -140
  22. /package/dist/{app-registration-BNC02wLI.js → app-registration-BVQ8zzYj.js} +0 -0
  23. /package/dist/{audio-preflight.runtime-BPlzkO3l.js → audio-preflight.runtime-qKoiXN6J.js} +0 -0
  24. /package/dist/{client-DBVoQL5w.js → client-B18oTGHf.js} +0 -0
  25. /package/dist/{runtime-CG0DuRCy.js → runtime-Cc16UY23.js} +0 -0
  26. /package/dist/{secret-contract-Dm4Z_zQN.js → secret-contract-DIpADEx7.js} +0 -0
  27. /package/dist/{security-audit-shared-ByuMx9cJ.js → security-audit-shared-B6kEakHj.js} +0 -0
  28. /package/dist/{session-conversation-BulT2OKc.js → session-conversation-DIuxgPQi.js} +0 -0
@@ -2,15 +2,14 @@ import { t as buildFeishuConversationId } from "./conversation-id-Byq1c20x.js";
2
2
  import { i as resolveReceiveIdType } from "./targets-Bb05cFr4.js";
3
3
  import { n as createFeishuThreadBindingManager } from "./thread-bindings-D5kDxq_j.js";
4
4
  import { _ as buildFeishuCommentTarget, f as isRecord$3, h as readString$2, l as encodeQuery, m as parseCommentContentElements, p as normalizeString, s as resolveFeishuRuntimeAccount, u as extractReplyText, v as normalizeCommentFileType } from "./accounts-CP4tDW-z.js";
5
- import { c as decodeFeishuCardAction, o as buildFeishuCardActionTextFallback, s as createFeishuCardInteractionEnvelope } from "./send-result-zZZOR3qT.js";
6
- import { a as resolveFeishuGroupSenderActivationIngressAccess, i as resolveFeishuGroupConversationIngressAccess, n as resolveFeishuDmIngressAccess, r as resolveFeishuGroupConfig, s as resolveFeishuReplyPolicy, t as hasExplicitFeishuGroupConfig } from "./policy-iDTxfTYv.js";
7
- import { t as getFeishuRuntime } from "./runtime-CG0DuRCy.js";
8
- import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient } from "./client-DBVoQL5w.js";
9
- import { c as getChatInfo, i as createCommentTypingReactionLifecycle, t as deliverCommentThreadText } from "./drive-BocW3yO6.js";
5
+ import { _ as buildFeishuCardActionTextFallback, d as resolveFeishuGroupConfig, f as resolveFeishuGroupConversationIngressAccess, l as hasExplicitFeishuGroupConfig, m as resolveFeishuReplyPolicy, p as resolveFeishuGroupSenderActivationIngressAccess, u as resolveFeishuDmIngressAccess, v as createFeishuCardInteractionEnvelope, y as decodeFeishuCardAction } from "./channel-1pldyM9r.js";
6
+ import { t as getFeishuRuntime } from "./runtime-Cc16UY23.js";
7
+ import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient } from "./client-B18oTGHf.js";
8
+ import { c as getChatInfo, i as createCommentTypingReactionLifecycle, t as deliverCommentThreadText } from "./drive-Av1h_Lwd.js";
10
9
  import { buildAgentMediaPayload, createReplyPrefixContext, evaluateSupplementalContextVisibility, loadSessionStore, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode, resolveSessionStoreEntry } from "./runtime-api.js";
11
- import { _ as normalizeFeishuExternalKey, a as sendCardFeishu, c as sendStructuredCardFeishu, d as isFeishuBroadcastMention, f as isMentionForwardRequest, g as shouldSuppressFeishuTextForVoiceMedia, h as sendMediaFeishu, i as resolveFeishuCardTemplate, l as parsePostContent, m as downloadMessageResourceFeishu, n as getMessageFeishu, p as isFeishuGroupChatType, r as listFeishuThreadMessages, s as sendMessageFeishu, u as extractMentionTargets } from "./send-DiFpqzBJ.js";
12
- import { i as waitForAbortableDelay, r as raceWithTimeoutAndAbort } from "./probe-BNzzU_uR.js";
13
- import { a as feishuWebhookRateLimiter, c as wsClients, i as botOpenIds, l as fetchBotIdentityForMonitor, n as FEISHU_WEBHOOK_MAX_BODY_BYTES, o as httpServers, r as botNames, s as recordWebhookStatus, t as FEISHU_WEBHOOK_BODY_TIMEOUT_MS } from "./monitor.state-D1WkvOGG.js";
10
+ import { _ as normalizeFeishuExternalKey, a as sendCardFeishu, c as sendStructuredCardFeishu, d as isFeishuBroadcastMention, f as isMentionForwardRequest, g as shouldSuppressFeishuTextForVoiceMedia, h as sendMediaFeishu, i as resolveFeishuCardTemplate, l as parsePostContent, m as saveMessageResourceFeishu, n as getMessageFeishu, p as isFeishuGroupChatType, r as listFeishuThreadMessages, s as sendMessageFeishu, u as extractMentionTargets } from "./send-DsCQVFOE.js";
11
+ import { i as waitForAbortableDelay, r as raceWithTimeoutAndAbort } from "./probe-DpPNslkb.js";
12
+ import { a as feishuWebhookRateLimiter, c as wsClients, i as botOpenIds, l as fetchBotIdentityForMonitor, n as FEISHU_WEBHOOK_MAX_BODY_BYTES, o as httpServers, r as botNames, s as recordWebhookStatus, t as FEISHU_WEBHOOK_BODY_TIMEOUT_MS } from "./monitor.state-D6ByOM5W.js";
14
13
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
15
14
  import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
16
15
  import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-message";
@@ -23,7 +22,7 @@ import path from "node:path";
23
22
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
24
23
  import * as Lark from "@larksuiteoapi/node-sdk";
25
24
  import { createPersistentDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
26
- import { applyBasicWebhookRequestGuards } from "openclaw/plugin-sdk/webhook-ingress";
25
+ import { applyBasicWebhookRequestGuards, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
27
26
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
28
27
  import { resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
29
28
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
@@ -220,6 +219,12 @@ function parseMediaKeys(content, messageType) {
220
219
  function toMessageResourceType(messageType) {
221
220
  return messageType === "image" ? "image" : "file";
222
221
  }
222
+ async function resolveSavedFeishuMedia(params) {
223
+ if ("saved" in params.result) return params.result.saved;
224
+ const core = getFeishuRuntime();
225
+ const contentType = params.result.contentType ?? await core.media.detectMime({ buffer: params.result.buffer });
226
+ return await core.channel.media.saveMediaBuffer(params.result.buffer, contentType, "inbound", params.maxBytes, params.result.fileName ?? params.originalFilename);
227
+ }
223
228
  function inferPlaceholder(messageType) {
224
229
  switch (messageType) {
225
230
  case "image": return "<media:image>";
@@ -243,22 +248,23 @@ async function resolveFeishuMediaList(params) {
243
248
  "post"
244
249
  ].includes(messageType)) return [];
245
250
  const out = [];
246
- const core = getFeishuRuntime();
247
251
  if (messageType === "post") {
248
252
  const { imageKeys, mediaKeys } = parsePostContent(content);
249
253
  if (imageKeys.length === 0 && mediaKeys.length === 0) return [];
250
254
  if (imageKeys.length > 0) log?.(`feishu: post message contains ${imageKeys.length} embedded image(s)`);
251
255
  if (mediaKeys.length > 0) log?.(`feishu: post message contains ${mediaKeys.length} embedded media file(s)`);
252
256
  for (const imageKey of imageKeys) try {
253
- const result = await downloadMessageResourceFeishu({
254
- cfg,
255
- messageId,
256
- fileKey: imageKey,
257
- type: "image",
258
- accountId
257
+ const saved = await resolveSavedFeishuMedia({
258
+ result: await saveMessageResourceFeishu({
259
+ cfg,
260
+ messageId,
261
+ fileKey: imageKey,
262
+ type: "image",
263
+ accountId,
264
+ maxBytes
265
+ }),
266
+ maxBytes
259
267
  });
260
- const contentType = result.contentType ?? await core.media.detectMime({ buffer: result.buffer });
261
- const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, "inbound", maxBytes);
262
268
  out.push({
263
269
  path: saved.path,
264
270
  contentType: saved.contentType,
@@ -269,15 +275,19 @@ async function resolveFeishuMediaList(params) {
269
275
  log?.(`feishu: failed to download embedded image ${imageKey}: ${String(err)}`);
270
276
  }
271
277
  for (const media of mediaKeys) try {
272
- const result = await downloadMessageResourceFeishu({
273
- cfg,
274
- messageId,
275
- fileKey: media.fileKey,
276
- type: "file",
277
- accountId
278
+ const saved = await resolveSavedFeishuMedia({
279
+ result: await saveMessageResourceFeishu({
280
+ cfg,
281
+ messageId,
282
+ fileKey: media.fileKey,
283
+ type: "file",
284
+ accountId,
285
+ maxBytes,
286
+ originalFilename: media.fileName
287
+ }),
288
+ maxBytes,
289
+ originalFilename: media.fileName
278
290
  });
279
- const contentType = result.contentType ?? await core.media.detectMime({ buffer: result.buffer });
280
- const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, "inbound", maxBytes);
281
291
  out.push({
282
292
  path: saved.path,
283
293
  contentType: saved.contentType,
@@ -294,15 +304,19 @@ async function resolveFeishuMediaList(params) {
294
304
  try {
295
305
  const fileKey = mediaKeys.fileKey || mediaKeys.imageKey;
296
306
  if (!fileKey) return [];
297
- const result = await downloadMessageResourceFeishu({
298
- cfg,
299
- messageId,
300
- fileKey,
301
- type: toMessageResourceType(messageType),
302
- accountId
307
+ const saved = await resolveSavedFeishuMedia({
308
+ result: await saveMessageResourceFeishu({
309
+ cfg,
310
+ messageId,
311
+ fileKey,
312
+ type: toMessageResourceType(messageType),
313
+ accountId,
314
+ maxBytes,
315
+ originalFilename: mediaKeys.fileName
316
+ }),
317
+ maxBytes,
318
+ originalFilename: mediaKeys.fileName
303
319
  });
304
- const contentType = result.contentType ?? await core.media.detectMime({ buffer: result.buffer });
305
- const saved = await core.channel.media.saveMediaBuffer(result.buffer, contentType, "inbound", maxBytes, result.fileName || mediaKeys.fileName);
306
320
  out.push({
307
321
  path: saved.path,
308
322
  contentType: saved.contentType,
@@ -1664,7 +1678,7 @@ async function resolveFeishuAudioPreflightTranscript(params) {
1664
1678
  const audioMedia = params.mediaList.filter((media) => media.contentType?.startsWith("audio/"));
1665
1679
  if (audioMedia.length === 0) return;
1666
1680
  try {
1667
- const { transcribeFirstAudio } = await import("./audio-preflight.runtime-BPlzkO3l.js");
1681
+ const { transcribeFirstAudio } = await import("./audio-preflight.runtime-qKoiXN6J.js");
1668
1682
  return await transcribeFirstAudio({
1669
1683
  ctx: {
1670
1684
  MediaPaths: audioMedia.map((media) => media.path),
@@ -4526,6 +4540,14 @@ function respondText(res, statusCode, body) {
4526
4540
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
4527
4541
  res.end(body);
4528
4542
  }
4543
+ function normalizeFeishuWebhookRateLimitClient(clientIp) {
4544
+ if (!clientIp) return "unknown";
4545
+ if (clientIp === "::1" || clientIp.startsWith("127.")) return "loopback";
4546
+ return clientIp;
4547
+ }
4548
+ function buildFeishuWebhookRateLimitKey(params) {
4549
+ return `${params.accountId}:${params.path}:${normalizeFeishuWebhookRateLimitClient(params.clientIp)}`;
4550
+ }
4529
4551
  function getFeishuWsReconnectDelayMs(attempt) {
4530
4552
  return Math.min(FEISHU_WS_RECONNECT_INITIAL_DELAY_MS * 2 ** Math.max(0, attempt - 1), FEISHU_WS_RECONNECT_MAX_DELAY_MS);
4531
4553
  }
@@ -4674,7 +4696,11 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
4674
4696
  req,
4675
4697
  res,
4676
4698
  rateLimiter: feishuWebhookRateLimiter,
4677
- rateLimitKey: `${accountId}:${path}:${req.socket.remoteAddress ?? "unknown"}`,
4699
+ rateLimitKey: buildFeishuWebhookRateLimitKey({
4700
+ accountId,
4701
+ path,
4702
+ clientIp: resolveRequestClientIp(req)
4703
+ }),
4678
4704
  nowMs: Date.now(),
4679
4705
  requireJsonContentType: true
4680
4706
  })) return;
@@ -1,4 +1,4 @@
1
- import { t as probeFeishu } from "./probe-BNzzU_uR.js";
1
+ import { t as probeFeishu } from "./probe-DpPNslkb.js";
2
2
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, createFixedWindowRateLimiter, createWebhookAnomalyTracker } from "openclaw/plugin-sdk/webhook-ingress";
4
4
  //#region extensions/feishu/src/monitor.startup.ts
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
- import { r as createFeishuClient } from "./client-DBVoQL5w.js";
2
+ import { r as createFeishuClient } from "./client-B18oTGHf.js";
3
3
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
4
  //#region extensions/feishu/src/async.ts
5
5
  const RACE_TIMEOUT = Symbol("race-timeout");
@@ -1,4 +1,4 @@
1
- import { n as setFeishuRuntime } from "./runtime-CG0DuRCy.js";
1
+ import { n as setFeishuRuntime } from "./runtime-Cc16UY23.js";
2
2
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
3
3
  import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-message";
4
4
  import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
@@ -1,2 +1,2 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-Dm4Z_zQN.js";
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DIpADEx7.js";
2
2
  export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -1,4 +1,4 @@
1
- import "./security-audit-shared-ByuMx9cJ.js";
1
+ import "./security-audit-shared-B6kEakHj.js";
2
2
  //#region extensions/feishu/src/message-action-contract.ts
3
3
  const messageActionTargetAliases = {
4
4
  read: { aliases: ["messageId"] },
@@ -1,2 +1,2 @@
1
- import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-ByuMx9cJ.js";
1
+ import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-B6kEakHj.js";
2
2
  export { collectFeishuSecurityAuditFindings };
@@ -1,8 +1,8 @@
1
1
  import { i as resolveReceiveIdType, r as normalizeFeishuTarget } from "./targets-Bb05cFr4.js";
2
2
  import { c as createFeishuApiError, f as isRecord$2, g as requestFeishuApi, s as resolveFeishuRuntimeAccount } from "./accounts-CP4tDW-z.js";
3
- import { i as toFeishuSendResult, r as resolveFeishuReceiptKind, t as assertFeishuMessageApiSuccess } from "./send-result-zZZOR3qT.js";
4
- import { t as getFeishuRuntime } from "./runtime-CG0DuRCy.js";
5
- import { r as createFeishuClient } from "./client-DBVoQL5w.js";
3
+ import { c as toFeishuSendResult, o as assertFeishuMessageApiSuccess, s as resolveFeishuReceiptKind } from "./channel-1pldyM9r.js";
4
+ import { t as getFeishuRuntime } from "./runtime-Cc16UY23.js";
5
+ import { r as createFeishuClient } from "./client-B18oTGHf.js";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
8
8
  import fs from "node:fs";
@@ -11,6 +11,8 @@ import { mediaKindFromMime } from "openclaw/plugin-sdk/media-mime";
11
11
  import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, runFfmpeg } from "openclaw/plugin-sdk/media-runtime";
12
12
  import { readRegularFile, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
13
13
  import { Readable } from "node:stream";
14
+ import { saveMediaBuffer, saveMediaStream } from "openclaw/plugin-sdk/media-store";
15
+ import "openclaw/plugin-sdk/response-limit-runtime";
14
16
  import { resolvePreferredOpenClawTmpDir, withTempDownloadPath, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
15
17
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
16
18
  //#region extensions/feishu/src/external-keys.ts
@@ -141,35 +143,29 @@ function extractFeishuDownloadMetadata(response) {
141
143
  fileName: (disposition ? decodeDispositionFileName(disposition) : void 0) ?? responseWithOptionalFields.file_name ?? responseWithOptionalFields.fileName ?? responseWithOptionalFields.data?.file_name ?? responseWithOptionalFields.data?.fileName
142
144
  };
143
145
  }
144
- async function readReadableBuffer(stream) {
145
- const chunks = [];
146
- for await (const chunk of stream) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
147
- return Buffer.concat(chunks);
146
+ function mediaLimitError(maxBytes) {
147
+ return /* @__PURE__ */ new Error(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
148
148
  }
149
- async function readFeishuResponseBuffer(params) {
150
- const { response } = params;
151
- if (Buffer.isBuffer(response)) return response;
152
- if (response instanceof ArrayBuffer) return Buffer.from(response);
149
+ async function saveFeishuResponseMedia(params) {
150
+ const { response, maxBytes, contentType, fileName } = params;
151
+ if (Buffer.isBuffer(response)) return saveMediaBuffer(response, contentType, "inbound", maxBytes, fileName);
152
+ if (response instanceof ArrayBuffer) return saveMediaBuffer(Buffer.from(response), contentType, "inbound", maxBytes, fileName);
153
153
  const responseWithOptionalFields = response;
154
154
  if (responseWithOptionalFields.code !== void 0 && responseWithOptionalFields.code !== 0) throw new Error(`${params.errorPrefix}: ${responseWithOptionalFields.msg || `code ${responseWithOptionalFields.code}`}`);
155
- if (responseWithOptionalFields.data && Buffer.isBuffer(responseWithOptionalFields.data)) return responseWithOptionalFields.data;
156
- if (responseWithOptionalFields.data instanceof ArrayBuffer) return Buffer.from(responseWithOptionalFields.data);
157
- if (typeof response.getReadableStream === "function") return readReadableBuffer(response.getReadableStream());
155
+ if (responseWithOptionalFields.data && Buffer.isBuffer(responseWithOptionalFields.data)) return saveMediaBuffer(responseWithOptionalFields.data, contentType, "inbound", maxBytes, fileName);
156
+ if (responseWithOptionalFields.data instanceof ArrayBuffer) return saveMediaBuffer(Buffer.from(responseWithOptionalFields.data), contentType, "inbound", maxBytes, fileName);
157
+ if (typeof response.getReadableStream === "function") return saveMediaStream(response.getReadableStream(), contentType, "inbound", maxBytes, fileName);
158
158
  if (typeof response.writeFile === "function") return await withTempDownloadPath({ prefix: params.tmpDirPrefix }, async (tmpPath) => {
159
159
  await response.writeFile(tmpPath);
160
- return await fs.promises.readFile(tmpPath);
160
+ if ((await fs.promises.stat(tmpPath)).size > maxBytes) throw mediaLimitError(maxBytes);
161
+ return await saveMediaStream(fs.createReadStream(tmpPath), contentType, "inbound", maxBytes, fileName);
161
162
  });
162
- if (responseWithOptionalFields[Symbol.asyncIterator]) {
163
- const asyncIterable = responseWithOptionalFields;
164
- const chunks = [];
165
- for await (const chunk of asyncIterable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
166
- return Buffer.concat(chunks);
167
- }
168
- if (response instanceof Readable) return readReadableBuffer(response);
163
+ if (responseWithOptionalFields[Symbol.asyncIterator]) return saveMediaStream(responseWithOptionalFields, contentType, "inbound", maxBytes, fileName);
164
+ if (response instanceof Readable) return saveMediaStream(response, contentType, "inbound", maxBytes, fileName);
169
165
  const keys = Object.keys(response);
170
166
  throw new Error(`${params.errorPrefix}: unexpected response format. Keys: [${keys.join(", ")}]`);
171
167
  }
172
- async function downloadMessageResourceWithType(params) {
168
+ async function saveMessageResourceWithType(params) {
173
169
  const response = await params.client.im.messageResource.get({
174
170
  path: {
175
171
  message_id: params.messageId,
@@ -177,21 +173,21 @@ async function downloadMessageResourceWithType(params) {
177
173
  },
178
174
  params: { type: params.type }
179
175
  });
176
+ const meta = extractFeishuDownloadMetadata(response);
180
177
  return {
181
- buffer: await readFeishuResponseBuffer({
178
+ saved: await saveFeishuResponseMedia({
182
179
  response,
183
180
  tmpDirPrefix: "openclaw-feishu-resource-",
184
- errorPrefix: "Feishu message resource download failed"
181
+ errorPrefix: "Feishu message resource download failed",
182
+ maxBytes: params.maxBytes,
183
+ contentType: meta.contentType,
184
+ fileName: meta.fileName ?? params.originalFilename
185
185
  }),
186
- ...extractFeishuDownloadMetadata(response)
186
+ ...meta
187
187
  };
188
188
  }
189
- /**
190
- * Download a message resource (file/image/audio/video) from Feishu.
191
- * Used for downloading files, audio, and video from messages.
192
- */
193
- async function downloadMessageResourceFeishu(params) {
194
- const { cfg, messageId, fileKey, type, accountId } = params;
189
+ async function saveMessageResourceFeishu(params) {
190
+ const { cfg, messageId, fileKey, type, accountId, maxBytes, originalFilename } = params;
195
191
  const normalizedFileKey = normalizeFeishuExternalKey(fileKey);
196
192
  if (!normalizedFileKey) throw new Error("Feishu message resource download failed: invalid file_key");
197
193
  const { client } = createConfiguredFeishuMediaClient({
@@ -199,20 +195,24 @@ async function downloadMessageResourceFeishu(params) {
199
195
  accountId
200
196
  });
201
197
  try {
202
- return await downloadMessageResourceWithType({
198
+ return await saveMessageResourceWithType({
203
199
  client,
204
200
  messageId,
205
201
  fileKey: normalizedFileKey,
206
- type
202
+ type,
203
+ maxBytes,
204
+ originalFilename
207
205
  });
208
206
  } catch (err) {
209
207
  if (type !== "file" || !isHttpStatusError(err, 502)) throw err;
210
208
  try {
211
- return await downloadMessageResourceWithType({
209
+ return await saveMessageResourceWithType({
212
210
  client,
213
211
  messageId,
214
212
  fileKey: normalizedFileKey,
215
- type: "media"
213
+ type: "media",
214
+ maxBytes,
215
+ originalFilename
216
216
  });
217
217
  } catch {
218
218
  throw err;
@@ -1207,4 +1207,4 @@ async function sendMarkdownCardFeishu(params) {
1207
1207
  });
1208
1208
  }
1209
1209
  //#endregion
1210
- export { normalizeFeishuExternalKey as _, sendCardFeishu as a, sendStructuredCardFeishu as c, isFeishuBroadcastMention as d, isMentionForwardRequest as f, shouldSuppressFeishuTextForVoiceMedia as g, sendMediaFeishu as h, resolveFeishuCardTemplate as i, parsePostContent as l, downloadMessageResourceFeishu as m, getMessageFeishu as n, sendMarkdownCardFeishu as o, isFeishuGroupChatType as p, listFeishuThreadMessages as r, sendMessageFeishu as s, editMessageFeishu as t, extractMentionTargets as u };
1210
+ export { normalizeFeishuExternalKey as _, sendCardFeishu as a, sendStructuredCardFeishu as c, isFeishuBroadcastMention as d, isMentionForwardRequest as f, shouldSuppressFeishuTextForVoiceMedia as g, sendMediaFeishu as h, resolveFeishuCardTemplate as i, parsePostContent as l, saveMessageResourceFeishu as m, getMessageFeishu as n, sendMarkdownCardFeishu as o, isFeishuGroupChatType as p, listFeishuThreadMessages as r, sendMessageFeishu as s, editMessageFeishu as t, extractMentionTargets as u };
@@ -1,2 +1,2 @@
1
- import { t as resolveFeishuSessionConversation } from "./session-conversation-BulT2OKc.js";
1
+ import { t as resolveFeishuSessionConversation } from "./session-conversation-DIuxgPQi.js";
2
2
  export { resolveFeishuSessionConversation as resolveSessionConversation };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-COlXCApz.js";
1
+ import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-1pldyM9r.js";
2
2
  export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/feishu",
3
- "version": "2026.5.12",
3
+ "version": "2026.5.14-beta.2",
4
4
  "description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@larksuiteoapi/node-sdk": "1.63.1",
11
+ "@larksuiteoapi/node-sdk": "1.64.0",
12
12
  "typebox": "1.1.38",
13
13
  "zod": "4.4.3"
14
14
  },
@@ -17,7 +17,7 @@
17
17
  "openclaw": "workspace:*"
18
18
  },
19
19
  "peerDependencies": {
20
- "openclaw": ">=2026.5.12"
20
+ "openclaw": ">=2026.5.14-beta.2"
21
21
  },
22
22
  "peerDependenciesMeta": {
23
23
  "openclaw": {
@@ -48,10 +48,10 @@
48
48
  "minHostVersion": ">=2026.4.25"
49
49
  },
50
50
  "compat": {
51
- "pluginApi": ">=2026.5.12"
51
+ "pluginApi": ">=2026.5.14-beta.2"
52
52
  },
53
53
  "build": {
54
- "openclawVersion": "2026.5.12"
54
+ "openclawVersion": "2026.5.14-beta.2"
55
55
  },
56
56
  "release": {
57
57
  "publishToClawHub": true,
@@ -1,44 +0,0 @@
1
- import { r as normalizeFeishuTarget } from "./targets-Bb05cFr4.js";
2
- import { o as resolveFeishuAccount } from "./accounts-CP4tDW-z.js";
3
- import { listDirectoryGroupEntriesFromMapKeysAndAllowFrom, listDirectoryUserEntriesFromAllowFromAndMapKeys } from "openclaw/plugin-sdk/directory-runtime";
4
- //#region extensions/feishu/src/directory.static.ts
5
- function toFeishuDirectoryPeers(ids) {
6
- return ids.map((id) => ({
7
- kind: "user",
8
- id
9
- }));
10
- }
11
- function toFeishuDirectoryGroups(ids) {
12
- return ids.map((id) => ({
13
- kind: "group",
14
- id
15
- }));
16
- }
17
- async function listFeishuDirectoryPeers(params) {
18
- const account = resolveFeishuAccount({
19
- cfg: params.cfg,
20
- accountId: params.accountId
21
- });
22
- return toFeishuDirectoryPeers(listDirectoryUserEntriesFromAllowFromAndMapKeys({
23
- allowFrom: account.config.allowFrom,
24
- map: account.config.dms,
25
- query: params.query,
26
- limit: params.limit,
27
- normalizeAllowFromId: (entry) => normalizeFeishuTarget(entry) ?? entry,
28
- normalizeMapKeyId: (entry) => normalizeFeishuTarget(entry) ?? entry
29
- }).map((entry) => entry.id));
30
- }
31
- async function listFeishuDirectoryGroups(params) {
32
- const account = resolveFeishuAccount({
33
- cfg: params.cfg,
34
- accountId: params.accountId
35
- });
36
- return toFeishuDirectoryGroups(listDirectoryGroupEntriesFromMapKeysAndAllowFrom({
37
- groups: account.config.groups,
38
- allowFrom: account.config.groupAllowFrom,
39
- query: params.query,
40
- limit: params.limit
41
- }).map((entry) => entry.id));
42
- }
43
- //#endregion
44
- export { listFeishuDirectoryPeers as n, listFeishuDirectoryGroups as t };
@@ -1,181 +0,0 @@
1
- import { t as detectIdType } from "./targets-Bb05cFr4.js";
2
- import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
- import { normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
4
- import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
5
- //#region extensions/feishu/src/policy.ts
6
- const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
7
- const FEISHU_TYPED_PREFIX_RE = /^(chat|group|channel|user|dm|open_id):/i;
8
- const FEISHU_ID_KIND = "plugin:feishu-id";
9
- const feishuIngressIdentity = defineStableChannelIngressIdentity({
10
- key: "feishu-id",
11
- kind: FEISHU_ID_KIND,
12
- normalize: normalizeFeishuAllowEntry,
13
- sensitivity: "pii",
14
- aliases: [{
15
- key: "feishu-alt-id",
16
- kind: FEISHU_ID_KIND,
17
- normalizeEntry: () => null,
18
- normalizeSubject: normalizeFeishuAllowEntry,
19
- sensitivity: "pii"
20
- }],
21
- isWildcardEntry: (entry) => normalizeFeishuAllowEntry(entry) === "*",
22
- resolveEntryId: ({ entryIndex }) => `feishu-entry-${entryIndex + 1}`
23
- });
24
- function normalizeFeishuAllowEntry(raw) {
25
- const trimmed = raw.trim();
26
- if (!trimmed) return "";
27
- if (trimmed === "*") return "*";
28
- let withoutProviderPrefix = trimmed;
29
- while (FEISHU_PROVIDER_PREFIX_RE.test(withoutProviderPrefix)) withoutProviderPrefix = withoutProviderPrefix.replace(FEISHU_PROVIDER_PREFIX_RE, "").trim();
30
- if (withoutProviderPrefix === "*") return "*";
31
- const lowered = normalizeOptionalLowercaseString(withoutProviderPrefix) ?? "";
32
- if (!lowered) return "";
33
- const prefixed = lowered.match(FEISHU_TYPED_PREFIX_RE);
34
- if (prefixed?.[1]) {
35
- const kind = [
36
- "chat",
37
- "group",
38
- "channel"
39
- ].includes(prefixed[1]) ? "chat" : "user";
40
- const value = withoutProviderPrefix.slice(prefixed[0].length).trim();
41
- return value === "*" ? "*" : value ? `${kind}:${value}` : "";
42
- }
43
- const detectedType = detectIdType(withoutProviderPrefix);
44
- if (detectedType === "chat_id") return `chat:${withoutProviderPrefix}`;
45
- if (detectedType === "open_id" || detectedType === "user_id") return `user:${withoutProviderPrefix}`;
46
- return "";
47
- }
48
- function normalizeFeishuDmPolicy(policy) {
49
- return policy === "open" || policy === "pairing" || policy === "allowlist" || policy === "disabled" ? policy : "pairing";
50
- }
51
- function normalizeFeishuGroupPolicy(policy) {
52
- return policy === "allowall" ? "open" : policy;
53
- }
54
- function createFeishuIngressSubject(params) {
55
- const ids = [params.primaryId, ...params.alternateIds ?? []].map((value) => value?.trim()).filter((value) => Boolean(value));
56
- return {
57
- stableId: ids[0],
58
- aliases: { "feishu-alt-id": ids[1] }
59
- };
60
- }
61
- function createFeishuIngressResolver(params) {
62
- return createChannelIngressResolver({
63
- channelId: "feishu",
64
- accountId: normalizeAccountId(params.accountId) ?? "default",
65
- identity: feishuIngressIdentity,
66
- cfg: params.cfg,
67
- ...params.readAllowFromStore ? { readStoreAllowFrom: params.readAllowFromStore } : {}
68
- });
69
- }
70
- async function resolveFeishuDmIngressAccess(params) {
71
- return await createFeishuIngressResolver({
72
- cfg: params.cfg,
73
- accountId: params.accountId,
74
- readAllowFromStore: params.readAllowFromStore
75
- }).message({
76
- subject: createFeishuIngressSubject({
77
- primaryId: params.senderOpenId,
78
- alternateIds: [params.senderUserId]
79
- }),
80
- conversation: {
81
- kind: "direct",
82
- id: params.conversationId
83
- },
84
- event: { mayPair: params.mayPair },
85
- dmPolicy: normalizeFeishuDmPolicy(params.dmPolicy),
86
- groupPolicy: "disabled",
87
- allowFrom: params.allowFrom ?? [],
88
- ...params.command ? { command: params.command } : {}
89
- });
90
- }
91
- async function resolveFeishuGroupConversationIngressAccess(params) {
92
- const groupPolicy = normalizeFeishuGroupPolicy(params.groupPolicy);
93
- const groupAllowFrom = groupPolicy === "allowlist" && params.groupExplicitlyConfigured ? [...params.groupAllowFrom ?? [], params.chatId] : params.groupAllowFrom ?? [];
94
- return await createFeishuIngressResolver({
95
- cfg: params.cfg,
96
- accountId: params.accountId
97
- }).message({
98
- subject: createFeishuIngressSubject({ primaryId: params.chatId }),
99
- conversation: {
100
- kind: "group",
101
- id: params.chatId
102
- },
103
- dmPolicy: "disabled",
104
- groupPolicy,
105
- groupAllowFrom
106
- });
107
- }
108
- async function resolveFeishuGroupSenderActivationIngressAccess(params) {
109
- const groupAllowFrom = params.allowFrom ?? [];
110
- return await createFeishuIngressResolver({
111
- cfg: params.cfg,
112
- accountId: params.accountId
113
- }).message({
114
- subject: createFeishuIngressSubject({
115
- primaryId: params.senderOpenId,
116
- alternateIds: [params.senderUserId]
117
- }),
118
- conversation: {
119
- kind: "group",
120
- id: params.chatId
121
- },
122
- dmPolicy: "disabled",
123
- groupPolicy: groupAllowFrom.length > 0 ? "allowlist" : "open",
124
- groupAllowFrom,
125
- mentionFacts: {
126
- canDetectMention: true,
127
- wasMentioned: params.mentionedBot
128
- },
129
- policy: { activation: {
130
- requireMention: params.requireMention,
131
- allowTextCommands: false
132
- } },
133
- ...params.command ? { command: params.command } : {}
134
- });
135
- }
136
- function resolveFeishuGroupConfig(params) {
137
- const groups = params.cfg?.groups ?? {};
138
- const wildcard = groups["*"];
139
- const groupId = params.groupId?.trim();
140
- if (!groupId) return;
141
- const direct = groups[groupId];
142
- if (direct) return direct;
143
- const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
144
- const matchKey = Object.keys(groups).find((key) => normalizeOptionalLowercaseString(key) === lowered);
145
- if (matchKey) return groups[matchKey];
146
- return wildcard;
147
- }
148
- function hasExplicitFeishuGroupConfig(params) {
149
- const groups = params.cfg?.groups ?? {};
150
- const groupId = params.groupId?.trim();
151
- if (!groupId) return false;
152
- if (Object.prototype.hasOwnProperty.call(groups, groupId) && groupId !== "*") return true;
153
- const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
154
- return Object.keys(groups).some((key) => key !== "*" && normalizeOptionalLowercaseString(key) === lowered);
155
- }
156
- function resolveFeishuGroupToolPolicy(params) {
157
- const cfg = params.cfg.channels?.feishu;
158
- if (!cfg) return;
159
- return resolveFeishuGroupConfig({
160
- cfg,
161
- groupId: params.groupId
162
- })?.tools;
163
- }
164
- function resolveFeishuReplyPolicy(params) {
165
- if (params.isDirectMessage) return { requireMention: false };
166
- const feishuCfg = params.cfg.channels?.feishu;
167
- const resolvedCfg = resolveMergedAccountConfig({
168
- channelConfig: feishuCfg,
169
- accounts: feishuCfg?.accounts,
170
- accountId: normalizeAccountId(params.accountId),
171
- normalizeAccountId,
172
- omitKeys: ["defaultAccount"]
173
- });
174
- const groupRequireMention = resolveFeishuGroupConfig({
175
- cfg: resolvedCfg,
176
- groupId: params.groupId
177
- })?.requireMention;
178
- return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
179
- }
180
- //#endregion
181
- export { resolveFeishuGroupSenderActivationIngressAccess as a, resolveFeishuGroupConversationIngressAccess as i, resolveFeishuDmIngressAccess as n, resolveFeishuGroupToolPolicy as o, resolveFeishuGroupConfig as r, resolveFeishuReplyPolicy as s, hasExplicitFeishuGroupConfig as t };