@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.
- package/dist/api.js +5 -5
- package/dist/{channel-COlXCApz.js → channel-1pldyM9r.js} +369 -18
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-Bt1Lk06Y.js → channel.runtime-BlJ_XJTS.js} +5 -6
- package/dist/contract-api.js +3 -3
- package/dist/{drive-BocW3yO6.js → drive-Av1h_Lwd.js} +1 -1
- package/dist/{monitor-BZgAq8ET.js → monitor-Dfy3D2pM.js} +2 -2
- package/dist/{monitor.account-CI4_rDnC.js → monitor.account-CT3Skl8y.js} +62 -36
- package/dist/{monitor.state-D1WkvOGG.js → monitor.state-D6ByOM5W.js} +1 -1
- package/dist/{probe-BNzzU_uR.js → probe-DpPNslkb.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/secret-contract-api.js +1 -1
- package/dist/{security-audit-DqJdocrN.js → security-audit-BzTLFO6g.js} +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/{send-DiFpqzBJ.js → send-DsCQVFOE.js} +37 -37
- package/dist/session-key-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/package.json +5 -5
- package/dist/directory.static-DqfUoZrD.js +0 -44
- package/dist/policy-iDTxfTYv.js +0 -181
- package/dist/send-result-zZZOR3qT.js +0 -140
- /package/dist/{app-registration-BNC02wLI.js → app-registration-BVQ8zzYj.js} +0 -0
- /package/dist/{audio-preflight.runtime-BPlzkO3l.js → audio-preflight.runtime-qKoiXN6J.js} +0 -0
- /package/dist/{client-DBVoQL5w.js → client-B18oTGHf.js} +0 -0
- /package/dist/{runtime-CG0DuRCy.js → runtime-Cc16UY23.js} +0 -0
- /package/dist/{secret-contract-Dm4Z_zQN.js → secret-contract-DIpADEx7.js} +0 -0
- /package/dist/{security-audit-shared-ByuMx9cJ.js → security-audit-shared-B6kEakHj.js} +0 -0
- /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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
12
|
-
import { i as waitForAbortableDelay, r as raceWithTimeoutAndAbort } from "./probe-
|
|
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-
|
|
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
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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-
|
|
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:
|
|
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-
|
|
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-
|
|
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");
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as setFeishuRuntime } from "./runtime-
|
|
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-
|
|
1
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DIpADEx7.js";
|
|
2
2
|
export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-
|
|
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 {
|
|
4
|
-
import { t as getFeishuRuntime } from "./runtime-
|
|
5
|
-
import { r as createFeishuClient } from "./client-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
...
|
|
186
|
+
...meta
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
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
|
|
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,
|
|
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 };
|
package/dist/session-key-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as resolveFeishuSessionConversation } from "./session-conversation-
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
51
|
+
"pluginApi": ">=2026.5.14-beta.2"
|
|
52
52
|
},
|
|
53
53
|
"build": {
|
|
54
|
-
"openclawVersion": "2026.5.
|
|
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 };
|
package/dist/policy-iDTxfTYv.js
DELETED
|
@@ -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 };
|