@openclaw/feishu 2026.9.1-beta.1 → 2026.9.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/{accounts-RwSyseKr.js → accounts-cCMNFBKg.js} +1 -1
- package/dist/api.js +222 -284
- package/dist/{app-registration-BpfUzS9m.js → app-registration-BI99e_JI.js} +12 -9
- package/dist/{channel-Dpgf8wLD.js → channel-VytDDz_B.js} +244 -582
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BIEERwaT.js → channel.runtime-CAD74kBO.js} +15 -19
- package/dist/{chat-C1nuW5UY.js → chat-DDhIcPQy.js} +13 -13
- package/dist/{client-DalLmpMf.js → client-DMbIL3UH.js} +3 -6
- package/dist/contract-api.js +2 -2
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-w6FpMypv.js +465 -0
- package/dist/index.js +11 -42
- package/dist/{json-response-CheljwGr.js → json-response-DXajw_FU.js} +1 -1
- package/dist/{monitor.startup-D-gL82OO.js → monitor-rPJ_Mz4M.js} +62 -2
- package/dist/{monitor.account-DhhotI_Q.js → monitor.account-v0XIDyfs.js} +270 -299
- package/dist/{probe-CfhhR_0a.js → probe-D_SatdZl.js} +3 -3
- package/dist/{presentation-card-C8Z7Tgru.js → reply-delivery-result-CYaKkbUN.js} +334 -240
- package/dist/{security-audit-D6Fz2h6p.js → security-audit-DKfR4Cv3.js} +1 -1
- package/dist/{security-audit-shared-BgpY7AiJ.js → security-audit-shared-CK5rVR-1.js} +2 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{subagent-hooks-KUi-7vUy.js → subagent-hooks-Buadpie_.js} +3 -6
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{targets-BUjQ1TcA.js → targets-BTQCYRZQ.js} +4 -4
- package/dist/{thread-bindings-CptM_YU6.js → thread-bindings-BiL1wGqK.js} +20 -9
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/package.json +1 -1
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
- package/node_modules/typebox/build/system/memory/create.mjs +3 -4
- package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
- package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
- package/node_modules/typebox/build/system/memory/update.mjs +3 -2
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +45 -238
- package/package.json +6 -5
- package/dist/doctor-contract-bEQXIXyP.js +0 -158
- package/dist/monitor-BRg3sHH7.js +0 -62
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
2
|
-
import { i as resolveReceiveIdType, r as normalizeFeishuTarget, t as detectIdType } from "./targets-
|
|
3
|
-
import {
|
|
1
|
+
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
|
+
import { i as resolveReceiveIdType, r as normalizeFeishuTarget, t as detectIdType } from "./targets-BTQCYRZQ.js";
|
|
3
|
+
import { o as normalizeFeishuExternalKey } from "./doctor-contract-w6FpMypv.js";
|
|
4
|
+
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
4
5
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
5
6
|
import { normalizeAccountId, normalizeOptionalAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
6
7
|
import { compileAllowlist, resolveAllowlistMatchByCandidates } from "openclaw/plugin-sdk/allow-from";
|
|
7
8
|
import { ToolAuthorizationError } from "openclaw/plugin-sdk/channel-actions";
|
|
8
9
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
10
|
import { PlatformMessageNotDispatchedError, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
10
|
-
import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText } from "openclaw/plugin-sdk/interactive-runtime";
|
|
11
|
+
import { legacyInteractiveReplyToPresentation, normalizeLegacyInteractiveReply, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, renderPresentationForDelivery, resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
|
|
11
12
|
import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
12
13
|
import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
14
|
import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
|
|
@@ -152,7 +153,7 @@ const feishuIngressIdentity = defineStableChannelIngressIdentity({
|
|
|
152
153
|
aliases: [{
|
|
153
154
|
key: "feishu-alt-id",
|
|
154
155
|
kind: FEISHU_ID_KIND,
|
|
155
|
-
normalizeEntry:
|
|
156
|
+
normalizeEntry: normalizeFeishuAllowEntry,
|
|
156
157
|
normalizeSubject: normalizeFeishuAllowEntry,
|
|
157
158
|
sensitivity: "pii"
|
|
158
159
|
}],
|
|
@@ -460,20 +461,8 @@ function canEnumerateAllFeishuPeers(account) {
|
|
|
460
461
|
return isDmUniversallyAllowed(account);
|
|
461
462
|
}
|
|
462
463
|
//#endregion
|
|
463
|
-
//#region extensions/feishu/src/external-keys.ts
|
|
464
|
-
const CONTROL_CHARS_RE = /\p{Cc}/u;
|
|
465
|
-
const MAX_EXTERNAL_KEY_LENGTH = 512;
|
|
466
|
-
function normalizeFeishuExternalKey(value) {
|
|
467
|
-
if (typeof value !== "string") return;
|
|
468
|
-
const normalized = value.trim();
|
|
469
|
-
if (!normalized || normalized.length > MAX_EXTERNAL_KEY_LENGTH) return;
|
|
470
|
-
if (CONTROL_CHARS_RE.test(normalized)) return;
|
|
471
|
-
if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("..")) return;
|
|
472
|
-
return normalized;
|
|
473
|
-
}
|
|
474
|
-
//#endregion
|
|
475
464
|
//#region extensions/feishu/src/markdown.ts
|
|
476
|
-
const FEISHU_POST_MAX_BYTES =
|
|
465
|
+
const FEISHU_POST_MAX_BYTES = 30720;
|
|
477
466
|
/** One parser contract for Feishu message and document Markdown decisions. */
|
|
478
467
|
function parseFeishuMarkdown(text) {
|
|
479
468
|
return fromMarkdown(text, {
|
|
@@ -566,22 +555,21 @@ function postContentBytes(messageText, mentions) {
|
|
|
566
555
|
mentions
|
|
567
556
|
}), "utf8");
|
|
568
557
|
}
|
|
569
|
-
/**
|
|
570
|
-
* Honor both configured character chunking and Feishu's serialized post envelope.
|
|
571
|
-
* Markdown wrappers and first-chunk mentions count toward the byte budget.
|
|
572
|
-
*/
|
|
573
558
|
function chunkFeishuPostMarkdown(params) {
|
|
574
|
-
|
|
559
|
+
return chunkFeishuMarkdownByEnvelope({
|
|
560
|
+
...params,
|
|
561
|
+
contentBytes: (text, isFirst) => postContentBytes(text, [...params.chunkMentions ?? [], ...isFirst ? params.firstChunkMentions ?? [] : []])
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
/** Measure the actual transport envelope, including UTF-8, JSON escapes and fence wrappers. */
|
|
565
|
+
function chunkFeishuMarkdownByEnvelope(params) {
|
|
566
|
+
const { text } = params;
|
|
575
567
|
if (!text) return [];
|
|
576
568
|
const requestedLimit = Number.isFinite(params.limit) && params.limit > 0 ? Math.floor(params.limit) : text.length;
|
|
577
569
|
const initialChunks = params.initialChunks ?? chunkFeishuMarkdownWithMode(text, requestedLimit, params.mode ?? "length");
|
|
578
570
|
const output = [];
|
|
579
|
-
const resolveMentions = (isFirst) => {
|
|
580
|
-
const mentions = [...chunkMentions ?? [], ...isFirst ? firstChunkMentions ?? [] : []];
|
|
581
|
-
return mentions.length > 0 ? mentions : void 0;
|
|
582
|
-
};
|
|
583
571
|
for (const initialChunk of initialChunks) {
|
|
584
|
-
if (
|
|
572
|
+
if (params.contentBytes(initialChunk, output.length === 0) <= FEISHU_POST_MAX_BYTES) {
|
|
585
573
|
output.push(initialChunk);
|
|
586
574
|
continue;
|
|
587
575
|
}
|
|
@@ -590,27 +578,16 @@ function chunkFeishuPostMarkdown(params) {
|
|
|
590
578
|
const chunks = chunkFeishuMarkdownWithMode(initialChunk, adaptiveLimit, params.mode ?? "length");
|
|
591
579
|
let largestContentBytes = 0;
|
|
592
580
|
let oversizedChunk;
|
|
593
|
-
let oversizedMentions;
|
|
594
581
|
for (const [index, chunk] of chunks.entries()) {
|
|
595
|
-
const
|
|
596
|
-
const contentBytes = postContentBytes(chunk, mentionsForChunk);
|
|
582
|
+
const contentBytes = params.contentBytes(chunk, output.length === 0 && index === 0);
|
|
597
583
|
largestContentBytes = Math.max(largestContentBytes, contentBytes);
|
|
598
|
-
if (contentBytes > FEISHU_POST_MAX_BYTES && oversizedChunk === void 0)
|
|
599
|
-
oversizedChunk = chunk;
|
|
600
|
-
oversizedMentions = mentionsForChunk;
|
|
601
|
-
}
|
|
584
|
+
if (contentBytes > FEISHU_POST_MAX_BYTES && oversizedChunk === void 0) oversizedChunk = chunk;
|
|
602
585
|
}
|
|
603
586
|
if (oversizedChunk === void 0) {
|
|
604
587
|
output.push(...chunks);
|
|
605
588
|
break;
|
|
606
589
|
}
|
|
607
|
-
if (adaptiveLimit === 1)
|
|
608
|
-
assertFeishuPostWithinEnvelope(buildFeishuPostMessageContent({
|
|
609
|
-
messageText: oversizedChunk,
|
|
610
|
-
mentions: oversizedMentions
|
|
611
|
-
}), "Feishu post chunk");
|
|
612
|
-
return [...output, ...chunks];
|
|
613
|
-
}
|
|
590
|
+
if (adaptiveLimit === 1) throw new Error("Feishu Markdown chunk exceeds the 30 KB API limit");
|
|
614
591
|
adaptiveLimit = Math.max(1, Math.min(adaptiveLimit - 1, Math.floor(adaptiveLimit * FEISHU_POST_MAX_BYTES / largestContentBytes) - 1));
|
|
615
592
|
}
|
|
616
593
|
}
|
|
@@ -638,7 +615,7 @@ function resolveFeishuCardTemplate(template) {
|
|
|
638
615
|
if (!normalized || !FEISHU_CARD_TEMPLATES.has(normalized)) return;
|
|
639
616
|
return normalized;
|
|
640
617
|
}
|
|
641
|
-
function escapeFeishuCardMarkdownText
|
|
618
|
+
function escapeFeishuCardMarkdownText(text) {
|
|
642
619
|
return text.replace(/[&<>]/g, (char) => {
|
|
643
620
|
switch (char) {
|
|
644
621
|
case "&": return "&";
|
|
@@ -649,9 +626,9 @@ function escapeFeishuCardMarkdownText$1(text) {
|
|
|
649
626
|
});
|
|
650
627
|
}
|
|
651
628
|
function escapeFeishuCardPlainText(text) {
|
|
652
|
-
return escapeFeishuCardMarkdownText
|
|
629
|
+
return escapeFeishuCardMarkdownText(text).replace(/([\\`*_{}[\]()#+\-!|>~])/g, "\\$1");
|
|
653
630
|
}
|
|
654
|
-
function resolveSafeFeishuButtonUrl
|
|
631
|
+
function resolveSafeFeishuButtonUrl(url) {
|
|
655
632
|
const trimmed = typeof url === "string" ? url.trim() : "";
|
|
656
633
|
if (!trimmed) return;
|
|
657
634
|
try {
|
|
@@ -664,7 +641,7 @@ function resolveSafeFeishuButtonUrl$1(url) {
|
|
|
664
641
|
function sanitizeNativeFeishuButtonBehavior(behavior) {
|
|
665
642
|
if (!isRecord(behavior)) return;
|
|
666
643
|
if (behavior.type === "open_url") {
|
|
667
|
-
const safeUrl = resolveSafeFeishuButtonUrl
|
|
644
|
+
const safeUrl = resolveSafeFeishuButtonUrl(behavior.default_url) ?? resolveSafeFeishuButtonUrl(behavior.url);
|
|
668
645
|
return safeUrl ? {
|
|
669
646
|
type: "open_url",
|
|
670
647
|
default_url: safeUrl
|
|
@@ -681,7 +658,7 @@ function sanitizeNativeFeishuCardButton(button) {
|
|
|
681
658
|
if (!text?.trim()) return;
|
|
682
659
|
const style = button.type === "danger" ? "danger" : button.type === "primary" || button.type === "success" ? "primary" : void 0;
|
|
683
660
|
const behaviors = Array.isArray(button.behaviors) ? button.behaviors.map((behavior) => sanitizeNativeFeishuButtonBehavior(behavior)).filter((behavior) => Boolean(behavior)) : [];
|
|
684
|
-
const rootSafeUrl = resolveSafeFeishuButtonUrl
|
|
661
|
+
const rootSafeUrl = resolveSafeFeishuButtonUrl(button.url);
|
|
685
662
|
if (rootSafeUrl) behaviors.push({
|
|
686
663
|
type: "open_url",
|
|
687
664
|
default_url: rootSafeUrl
|
|
@@ -706,13 +683,13 @@ function sanitizeNativeFeishuCardElements(element) {
|
|
|
706
683
|
if (element.tag === "hr") return [{ tag: "hr" }];
|
|
707
684
|
if (element.tag === "markdown" && typeof element.content === "string") return [{
|
|
708
685
|
tag: "markdown",
|
|
709
|
-
content: escapeFeishuCardMarkdownText
|
|
686
|
+
content: escapeFeishuCardMarkdownText(element.content)
|
|
710
687
|
}];
|
|
711
688
|
if (element.tag === "div" && isRecord(element.text)) {
|
|
712
689
|
const text = element.text;
|
|
713
690
|
if (text.tag === "lark_md" && typeof text.content === "string") return [{
|
|
714
691
|
tag: "markdown",
|
|
715
|
-
content: escapeFeishuCardMarkdownText
|
|
692
|
+
content: escapeFeishuCardMarkdownText(text.content)
|
|
716
693
|
}];
|
|
717
694
|
if (text.tag === "plain_text" && typeof text.content === "string") return [{
|
|
718
695
|
tag: "markdown",
|
|
@@ -857,7 +834,7 @@ async function requestFeishuApi(request, errorPrefix, options = {}) {
|
|
|
857
834
|
return result;
|
|
858
835
|
}, {
|
|
859
836
|
attempts: 3,
|
|
860
|
-
minDelayMs:
|
|
837
|
+
minDelayMs: FEISHU_SEND_RETRY_BASE_MS,
|
|
861
838
|
shouldRetry: (error) => getFeishuSendRateLimitCode(error) !== void 0
|
|
862
839
|
});
|
|
863
840
|
} catch (error) {
|
|
@@ -870,7 +847,8 @@ function readDocsLinkUrl(element) {
|
|
|
870
847
|
}
|
|
871
848
|
function readMentionUserId(element) {
|
|
872
849
|
const mention = isRecord(element.mention) ? element.mention : void 0;
|
|
873
|
-
|
|
850
|
+
const person = isRecord(element.person) ? element.person : void 0;
|
|
851
|
+
return normalizeOptionalString(person?.user_id) || normalizeOptionalString(mention?.user_id) || normalizeOptionalString(mention?.open_id) || normalizeOptionalString(element.mention_user) || normalizeOptionalString(element.user_id) || void 0;
|
|
874
852
|
}
|
|
875
853
|
function readMentionDisplayText(element, userId) {
|
|
876
854
|
const mention = isRecord(element.mention) ? element.mention : void 0;
|
|
@@ -1332,7 +1310,8 @@ function resolveFeishuToolAccount(params) {
|
|
|
1332
1310
|
function createFeishuToolClient(params) {
|
|
1333
1311
|
return createFeishuClient(resolveFeishuToolAccount(params));
|
|
1334
1312
|
}
|
|
1335
|
-
function resolveAnyEnabledFeishuToolsConfig(
|
|
1313
|
+
function resolveAnyEnabledFeishuToolsConfig(config) {
|
|
1314
|
+
const accounts = listEnabledFeishuAccounts(config);
|
|
1336
1315
|
const merged = {
|
|
1337
1316
|
doc: false,
|
|
1338
1317
|
chat: false,
|
|
@@ -1444,38 +1423,19 @@ function resolveAmbientCommentTarget(context) {
|
|
|
1444
1423
|
if (deliveryContext?.channel && deliveryContext.channel !== "feishu") return null;
|
|
1445
1424
|
return parseFeishuCommentTarget(deliveryContext?.to);
|
|
1446
1425
|
}
|
|
1447
|
-
function
|
|
1426
|
+
function resolveDriveCommentParams(params, context, action) {
|
|
1448
1427
|
const ambient = resolveAmbientCommentTarget(context);
|
|
1449
|
-
|
|
1450
|
-
|
|
1428
|
+
let resolved = params;
|
|
1429
|
+
if (ambient && (action !== "add_comment" || ambient.fileType === "doc" || ambient.fileType === "docx")) resolved = {
|
|
1451
1430
|
...params,
|
|
1452
1431
|
file_token: params.file_token?.trim() || ambient.fileToken,
|
|
1453
1432
|
file_type: params.file_type ?? ambient.fileType,
|
|
1454
|
-
comment_id: params.comment_id?.trim() || ambient.commentId
|
|
1455
|
-
};
|
|
1456
|
-
}
|
|
1457
|
-
function applyAddCommentAmbientDefaults(params, context) {
|
|
1458
|
-
const ambient = resolveAmbientCommentTarget(context);
|
|
1459
|
-
if (!ambient || ambient.fileType !== "doc" && ambient.fileType !== "docx") return params;
|
|
1460
|
-
return {
|
|
1461
|
-
...params,
|
|
1462
|
-
file_token: params.file_token?.trim() || ambient.fileToken,
|
|
1463
|
-
file_type: params.file_type ?? ambient.fileType
|
|
1464
|
-
};
|
|
1465
|
-
}
|
|
1466
|
-
function applyAddCommentDefaults(params) {
|
|
1467
|
-
const fileType = params.file_type ?? "docx";
|
|
1468
|
-
if (!params.file_type) console.info(`[feishu_drive] add_comment missing file_type; defaulting to docx file_token=${params.file_token ?? "unknown"}`);
|
|
1469
|
-
return {
|
|
1470
|
-
...params,
|
|
1471
|
-
file_type: fileType
|
|
1433
|
+
...action !== "add_comment" && { comment_id: params.comment_id?.trim() || ambient.commentId }
|
|
1472
1434
|
};
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
const fileType = params.file_type ?? "docx";
|
|
1476
|
-
if (!params.file_type) console.info(`[feishu_drive] ${action} missing file_type; defaulting to docx file_token=${params.file_token ?? "unknown"}`);
|
|
1435
|
+
const fileType = resolved.file_type ?? "docx";
|
|
1436
|
+
if (!resolved.file_type) console.info(`[feishu_drive] ${action} missing file_type; defaulting to docx file_token=${resolved.file_token ?? "unknown"}`);
|
|
1477
1437
|
return {
|
|
1478
|
-
...
|
|
1438
|
+
...resolved,
|
|
1479
1439
|
file_type: fileType
|
|
1480
1440
|
};
|
|
1481
1441
|
}
|
|
@@ -1619,28 +1579,13 @@ async function deleteFile(client, fileToken, type) {
|
|
|
1619
1579
|
task_id: res.data?.task_id
|
|
1620
1580
|
};
|
|
1621
1581
|
}
|
|
1622
|
-
async function
|
|
1623
|
-
const
|
|
1624
|
-
|
|
1625
|
-
method: "GET",
|
|
1626
|
-
url: `/open-apis/drive/v1/files/${encodeURIComponent(params.file_token)}/comments` + encodeQuery({
|
|
1627
|
-
file_type: params.file_type,
|
|
1628
|
-
page_size: normalizeCommentPageSize(params.page_size),
|
|
1629
|
-
page_token: params.page_token,
|
|
1630
|
-
user_id_type: "open_id"
|
|
1631
|
-
})
|
|
1632
|
-
}));
|
|
1633
|
-
return {
|
|
1634
|
-
has_more: response.data?.has_more ?? false,
|
|
1635
|
-
page_token: response.data?.page_token,
|
|
1636
|
-
comments: (response.data?.items ?? []).map(normalizeCommentCard)
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
async function listCommentReplies(client, params) {
|
|
1582
|
+
async function listDriveCommentPage(client, params) {
|
|
1583
|
+
const filePath = `/open-apis/drive/v1/files/${encodeURIComponent(params.file_token)}/comments`;
|
|
1584
|
+
const isReplies = params.action === "list_comment_replies";
|
|
1640
1585
|
const response = assertDriveApiSuccess(await requestDriveApi({
|
|
1641
1586
|
client,
|
|
1642
1587
|
method: "GET",
|
|
1643
|
-
url:
|
|
1588
|
+
url: (isReplies ? `${filePath}/${encodeURIComponent(params.comment_id)}/replies` : filePath) + encodeQuery({
|
|
1644
1589
|
file_type: params.file_type,
|
|
1645
1590
|
page_size: normalizeCommentPageSize(params.page_size),
|
|
1646
1591
|
page_token: params.page_token,
|
|
@@ -1650,7 +1595,7 @@ async function listCommentReplies(client, params) {
|
|
|
1650
1595
|
return {
|
|
1651
1596
|
has_more: response.data?.has_more ?? false,
|
|
1652
1597
|
page_token: response.data?.page_token,
|
|
1653
|
-
replies: (response.data?.items ?? []).map(normalizeCommentReply)
|
|
1598
|
+
...isReplies ? { replies: (response.data?.items ?? []).map(normalizeCommentReply) } : { comments: (response.data?.items ?? []).map(normalizeCommentCard) }
|
|
1654
1599
|
};
|
|
1655
1600
|
}
|
|
1656
1601
|
async function addComment(client, params) {
|
|
@@ -1763,9 +1708,7 @@ async function deliverCommentThreadText(client, params) {
|
|
|
1763
1708
|
}
|
|
1764
1709
|
function registerFeishuDriveTools(api) {
|
|
1765
1710
|
if (!api.config) return;
|
|
1766
|
-
|
|
1767
|
-
if (accounts.length === 0) return;
|
|
1768
|
-
if (!resolveAnyEnabledFeishuToolsConfig(accounts).drive) return;
|
|
1711
|
+
if (!resolveAnyEnabledFeishuToolsConfig(api.config).drive) return;
|
|
1769
1712
|
api.registerTool((ctx) => {
|
|
1770
1713
|
const defaultAccountId = ctx.agentAccountId;
|
|
1771
1714
|
return {
|
|
@@ -1796,23 +1739,13 @@ function registerFeishuDriveTools(api) {
|
|
|
1796
1739
|
case "create_folder": return feishuExternalToolResult(await createFolder(client, p.name, p.folder_token));
|
|
1797
1740
|
case "move": return feishuExternalToolResult(await moveFile(client, p.file_token, p.type, p.folder_token));
|
|
1798
1741
|
case "delete": return feishuExternalToolResult(await deleteFile(client, p.file_token, p.type));
|
|
1799
|
-
case "list_comments":
|
|
1800
|
-
case "list_comment_replies": return feishuExternalToolResult(await
|
|
1801
|
-
case "add_comment":
|
|
1802
|
-
const resolved = applyAddCommentDefaults(applyAddCommentAmbientDefaults(p, ctx));
|
|
1803
|
-
try {
|
|
1804
|
-
return feishuExternalToolResult(await addComment(client, resolved));
|
|
1805
|
-
} finally {
|
|
1806
|
-
cleanupAmbientCommentTypingReaction({
|
|
1807
|
-
client: getDriveInternalClient(client),
|
|
1808
|
-
deliveryContext: ctx.deliveryContext
|
|
1809
|
-
});
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1742
|
+
case "list_comments":
|
|
1743
|
+
case "list_comment_replies": return feishuExternalToolResult(await listDriveCommentPage(client, resolveDriveCommentParams(p, ctx, p.action)));
|
|
1744
|
+
case "add_comment":
|
|
1812
1745
|
case "reply_comment": {
|
|
1813
|
-
const resolved =
|
|
1746
|
+
const resolved = resolveDriveCommentParams(p, ctx, p.action);
|
|
1814
1747
|
try {
|
|
1815
|
-
return feishuExternalToolResult(await deliverCommentThreadText(client, resolved));
|
|
1748
|
+
return feishuExternalToolResult(await (resolved.action === "add_comment" ? addComment(client, resolved) : deliverCommentThreadText(client, resolved)));
|
|
1816
1749
|
} finally {
|
|
1817
1750
|
cleanupAmbientCommentTypingReaction({
|
|
1818
1751
|
client: getDriveInternalClient(client),
|
|
@@ -1880,6 +1813,11 @@ async function buildFeishuMediaFallbackText(params) {
|
|
|
1880
1813
|
return [params.text?.trim(), attachmentText].filter(Boolean).join("\n\n");
|
|
1881
1814
|
}
|
|
1882
1815
|
//#endregion
|
|
1816
|
+
//#region extensions/feishu/src/api-response.ts
|
|
1817
|
+
function assertFeishuApiSuccess(response, errorPrefix) {
|
|
1818
|
+
if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
|
|
1819
|
+
}
|
|
1820
|
+
//#endregion
|
|
1883
1821
|
//#region extensions/feishu/src/media-chunk-idle.ts
|
|
1884
1822
|
var FeishuInboundMediaTimeoutError = class extends Error {
|
|
1885
1823
|
constructor(chunkTimeoutMs) {
|
|
@@ -1953,14 +1891,11 @@ function createFeishuSendReceipt(params) {
|
|
|
1953
1891
|
chatId,
|
|
1954
1892
|
conversationId: chatId
|
|
1955
1893
|
}] : [],
|
|
1956
|
-
...
|
|
1894
|
+
...params.replyToId?.trim() ? { replyToId: params.replyToId.trim() } : {},
|
|
1957
1895
|
kind: params.kind ?? "unknown"
|
|
1958
1896
|
});
|
|
1959
1897
|
}
|
|
1960
|
-
function
|
|
1961
|
-
if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
|
|
1962
|
-
}
|
|
1963
|
-
function toFeishuSendResult(response, chatId, kind, errorPrefix = "Feishu send failed") {
|
|
1898
|
+
function toFeishuSendResult(response, chatId, kind, errorPrefix = "Feishu send failed", replyToId) {
|
|
1964
1899
|
const messageId = response.data?.message_id?.trim();
|
|
1965
1900
|
if (!messageId) throw createChannelPartialDeliveryError(/* @__PURE__ */ new Error(`${errorPrefix}: no message_id returned`), {
|
|
1966
1901
|
messageIds: [],
|
|
@@ -1972,7 +1907,8 @@ function toFeishuSendResult(response, chatId, kind, errorPrefix = "Feishu send f
|
|
|
1972
1907
|
receipt: createFeishuSendReceipt({
|
|
1973
1908
|
messageId,
|
|
1974
1909
|
chatId,
|
|
1975
|
-
kind
|
|
1910
|
+
kind,
|
|
1911
|
+
replyToId
|
|
1976
1912
|
})
|
|
1977
1913
|
};
|
|
1978
1914
|
}
|
|
@@ -1988,10 +1924,11 @@ function resolveFeishuSendTarget(params) {
|
|
|
1988
1924
|
const client = createFeishuClient(account);
|
|
1989
1925
|
const receiveId = normalizeFeishuTarget(target);
|
|
1990
1926
|
if (!receiveId) throw new Error(`Invalid Feishu target: ${params.to}`);
|
|
1927
|
+
const withoutProviderPrefix = target.replace(/^(feishu|lark):/i, "");
|
|
1991
1928
|
return {
|
|
1992
1929
|
client,
|
|
1993
1930
|
receiveId,
|
|
1994
|
-
receiveIdType: resolveReceiveIdType(
|
|
1931
|
+
receiveIdType: resolveReceiveIdType(withoutProviderPrefix)
|
|
1995
1932
|
};
|
|
1996
1933
|
}
|
|
1997
1934
|
//#endregion
|
|
@@ -2058,7 +1995,8 @@ function renderElement(element, attachments, mentionedOpenIds, renderMediaPlaceh
|
|
|
2058
1995
|
case "a": return renderLinkElement(element);
|
|
2059
1996
|
case "at":
|
|
2060
1997
|
{
|
|
2061
|
-
const
|
|
1998
|
+
const mentioned = toStringOrEmpty(element.open_id) || toStringOrEmpty(element.user_id);
|
|
1999
|
+
const normalizedMention = normalizeFeishuExternalKey(mentioned);
|
|
2062
2000
|
if (normalizedMention) mentionedOpenIds.push(normalizedMention);
|
|
2063
2001
|
}
|
|
2064
2002
|
return renderMentionElement(element);
|
|
@@ -2372,7 +2310,7 @@ async function sendFallbackDirect(client, params, errorPrefix) {
|
|
|
2372
2310
|
msg_type: params.msgType
|
|
2373
2311
|
}
|
|
2374
2312
|
}), errorPrefix, { includeNestedErrorLogId: true });
|
|
2375
|
-
|
|
2313
|
+
assertFeishuApiSuccess(response, errorPrefix);
|
|
2376
2314
|
return toFeishuSendResult(response, params.receiveId, resolveFeishuReceiptKind(params.msgType), errorPrefix);
|
|
2377
2315
|
}
|
|
2378
2316
|
async function sendReplyOrFallbackDirect(client, params) {
|
|
@@ -2397,8 +2335,8 @@ async function sendReplyOrFallbackDirect(client, params) {
|
|
|
2397
2335
|
if (replyTargetFallbackError) throw replyTargetFallbackError;
|
|
2398
2336
|
return sendFallbackDirect(client, params.directParams, params.directErrorPrefix);
|
|
2399
2337
|
}
|
|
2400
|
-
|
|
2401
|
-
return toFeishuSendResult(response, params.directParams.receiveId, resolveFeishuReceiptKind(params.msgType), params.replyErrorPrefix);
|
|
2338
|
+
assertFeishuApiSuccess(response, params.replyErrorPrefix);
|
|
2339
|
+
return toFeishuSendResult(response, params.directParams.receiveId, resolveFeishuReceiptKind(params.msgType), params.replyErrorPrefix, params.replyToMessageId);
|
|
2402
2340
|
}
|
|
2403
2341
|
function parseFeishuMessageContent(rawContent, msgType, messageId) {
|
|
2404
2342
|
if (!rawContent) return "";
|
|
@@ -2406,7 +2344,8 @@ function parseFeishuMessageContent(rawContent, msgType, messageId) {
|
|
|
2406
2344
|
try {
|
|
2407
2345
|
parsed = JSON.parse(rawContent);
|
|
2408
2346
|
} catch {
|
|
2409
|
-
|
|
2347
|
+
const safeId = messageId ? ` (id: ${messageId})` : "";
|
|
2348
|
+
logVerbose(`feishu message content parse failed for ${msgType} message${safeId}`);
|
|
2410
2349
|
return rawContent;
|
|
2411
2350
|
}
|
|
2412
2351
|
if (msgType === "text") {
|
|
@@ -2587,57 +2526,40 @@ async function editMessageFeishu(params) {
|
|
|
2587
2526
|
const client = createFeishuClient(account);
|
|
2588
2527
|
if (card) {
|
|
2589
2528
|
const content = JSON.stringify(card);
|
|
2590
|
-
|
|
2529
|
+
assertFeishuApiSuccess(await client.im.message.patch({
|
|
2591
2530
|
path: { message_id: messageId },
|
|
2592
2531
|
data: { content }
|
|
2593
|
-
});
|
|
2594
|
-
if (response.code !== 0) throw new Error(`Feishu message edit failed: ${response.msg || `code ${response.code}`}`);
|
|
2532
|
+
}), "Feishu message edit failed");
|
|
2595
2533
|
return {
|
|
2596
2534
|
messageId,
|
|
2597
2535
|
contentType: "interactive"
|
|
2598
2536
|
};
|
|
2599
2537
|
}
|
|
2600
|
-
const
|
|
2538
|
+
const tableMode = resolveMarkdownTableMode({
|
|
2601
2539
|
cfg,
|
|
2602
2540
|
channel: "feishu"
|
|
2603
|
-
})
|
|
2541
|
+
});
|
|
2542
|
+
const content = buildFeishuPostMessageContent({ messageText: materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, tableMode)) });
|
|
2604
2543
|
assertFeishuPostWithinEnvelope(content, "Feishu message edit");
|
|
2605
|
-
|
|
2544
|
+
assertFeishuApiSuccess(await client.im.message.update({
|
|
2606
2545
|
path: { message_id: messageId },
|
|
2607
2546
|
data: {
|
|
2608
2547
|
msg_type: "post",
|
|
2609
2548
|
content
|
|
2610
2549
|
}
|
|
2611
|
-
});
|
|
2612
|
-
if (response.code !== 0) throw new Error(`Feishu message edit failed: ${response.msg || `code ${response.code}`}`);
|
|
2550
|
+
}), "Feishu message edit failed");
|
|
2613
2551
|
return {
|
|
2614
2552
|
messageId,
|
|
2615
2553
|
contentType: "post"
|
|
2616
2554
|
};
|
|
2617
2555
|
}
|
|
2618
2556
|
/**
|
|
2619
|
-
* Build a Feishu interactive card with markdown content.
|
|
2620
|
-
* Cards render markdown properly (code blocks, tables, links, etc.)
|
|
2621
|
-
* Uses schema 2.0 format for proper markdown rendering.
|
|
2622
|
-
*/
|
|
2623
|
-
function buildMarkdownCard(text) {
|
|
2624
|
-
return {
|
|
2625
|
-
schema: "2.0",
|
|
2626
|
-
config: { width_mode: "fill" },
|
|
2627
|
-
body: { elements: [{
|
|
2628
|
-
tag: "markdown",
|
|
2629
|
-
content: text
|
|
2630
|
-
}] }
|
|
2631
|
-
};
|
|
2632
|
-
}
|
|
2633
|
-
/**
|
|
2634
2557
|
* Build a Feishu interactive card with optional header and note footer.
|
|
2635
|
-
* When header/note are omitted, behaves identically to buildMarkdownCard.
|
|
2636
2558
|
*/
|
|
2637
2559
|
function buildStructuredCard(text, options) {
|
|
2638
2560
|
const elements = [{
|
|
2639
2561
|
tag: "markdown",
|
|
2640
|
-
content: text
|
|
2562
|
+
content: options?.mentions?.length ? buildMentionedCardContent(options.mentions, text) : text
|
|
2641
2563
|
}];
|
|
2642
2564
|
if (options?.note) {
|
|
2643
2565
|
elements.push({ tag: "hr" });
|
|
@@ -2660,19 +2582,28 @@ function buildStructuredCard(text, options) {
|
|
|
2660
2582
|
};
|
|
2661
2583
|
return card;
|
|
2662
2584
|
}
|
|
2585
|
+
function chunkFeishuCardMarkdown(params) {
|
|
2586
|
+
return chunkFeishuMarkdownByEnvelope({
|
|
2587
|
+
...params,
|
|
2588
|
+
contentBytes: (text, isFirst) => Buffer.byteLength(JSON.stringify(buildStructuredCard(text, {
|
|
2589
|
+
header: params.header,
|
|
2590
|
+
note: params.note,
|
|
2591
|
+
mentions: [...params.chunkMentions ?? [], ...isFirst ? params.firstChunkMentions ?? [] : []]
|
|
2592
|
+
})), "utf8")
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2663
2595
|
/**
|
|
2664
2596
|
* Send a message as a structured card with optional header and note.
|
|
2665
2597
|
*/
|
|
2666
2598
|
async function sendStructuredCardFeishu(params) {
|
|
2667
2599
|
const { cfg, to, text, replyToMessageId, replyInThread, allowTopLevelReplyFallback, mentions, accountId, header, note } = params;
|
|
2668
|
-
let cardText = text;
|
|
2669
|
-
if (mentions && mentions.length > 0) cardText = buildMentionedCardContent(mentions, text);
|
|
2670
2600
|
return sendCardFeishu({
|
|
2671
2601
|
cfg,
|
|
2672
2602
|
to,
|
|
2673
|
-
card: buildStructuredCard(
|
|
2603
|
+
card: buildStructuredCard(text, {
|
|
2674
2604
|
header,
|
|
2675
|
-
note
|
|
2605
|
+
note,
|
|
2606
|
+
mentions
|
|
2676
2607
|
}),
|
|
2677
2608
|
replyToMessageId,
|
|
2678
2609
|
replyInThread,
|
|
@@ -2680,29 +2611,11 @@ async function sendStructuredCardFeishu(params) {
|
|
|
2680
2611
|
accountId
|
|
2681
2612
|
});
|
|
2682
2613
|
}
|
|
2683
|
-
/**
|
|
2684
|
-
* Send a message as a markdown card (interactive message).
|
|
2685
|
-
* This renders markdown properly in Feishu (code blocks, tables, bold/italic, etc.)
|
|
2686
|
-
*/
|
|
2687
|
-
async function sendMarkdownCardFeishu(params) {
|
|
2688
|
-
const { cfg, to, text, replyToMessageId, replyInThread, allowTopLevelReplyFallback, mentions, accountId } = params;
|
|
2689
|
-
let cardText = text;
|
|
2690
|
-
if (mentions && mentions.length > 0) cardText = buildMentionedCardContent(mentions, text);
|
|
2691
|
-
return sendCardFeishu({
|
|
2692
|
-
cfg,
|
|
2693
|
-
to,
|
|
2694
|
-
card: buildMarkdownCard(cardText),
|
|
2695
|
-
replyToMessageId,
|
|
2696
|
-
replyInThread,
|
|
2697
|
-
allowTopLevelReplyFallback,
|
|
2698
|
-
accountId
|
|
2699
|
-
});
|
|
2700
|
-
}
|
|
2701
2614
|
//#endregion
|
|
2702
2615
|
//#region extensions/feishu/src/media.ts
|
|
2703
2616
|
const FEISHU_MEDIA_HTTP_TIMEOUT_MS = 12e4;
|
|
2704
|
-
const FEISHU_MAX_FILE_UPLOAD_BYTES =
|
|
2705
|
-
const FEISHU_MAX_IMAGE_UPLOAD_BYTES =
|
|
2617
|
+
const FEISHU_MAX_FILE_UPLOAD_BYTES = 31457280;
|
|
2618
|
+
const FEISHU_MAX_IMAGE_UPLOAD_BYTES = 10485760;
|
|
2706
2619
|
const FEISHU_VOICE_FILE_NAME = "voice.ogg";
|
|
2707
2620
|
const FEISHU_VOICE_SAMPLE_RATE_HZ = 48e3;
|
|
2708
2621
|
const FEISHU_VOICE_BITRATE = "64k";
|
|
@@ -2823,7 +2736,7 @@ function extractFeishuDownloadMetadata(response) {
|
|
|
2823
2736
|
};
|
|
2824
2737
|
}
|
|
2825
2738
|
function mediaLimitError(maxBytes) {
|
|
2826
|
-
return /* @__PURE__ */ new Error(`Media exceeds ${Math.round(maxBytes /
|
|
2739
|
+
return /* @__PURE__ */ new Error(`Media exceeds ${Math.round(maxBytes / 1048576)}MB limit`);
|
|
2827
2740
|
}
|
|
2828
2741
|
async function saveFeishuResponseMedia(params) {
|
|
2829
2742
|
const { response, maxBytes, contentType, fileName } = params;
|
|
@@ -2987,7 +2900,7 @@ async function sendImageFeishu(params) {
|
|
|
2987
2900
|
msg_type: "image"
|
|
2988
2901
|
}
|
|
2989
2902
|
}), "Feishu image send failed", { includeNestedErrorLogId: true });
|
|
2990
|
-
|
|
2903
|
+
assertFeishuApiSuccess(response, "Feishu image send failed");
|
|
2991
2904
|
return toFeishuSendResult(response, receiveId, "media", "Feishu image send failed");
|
|
2992
2905
|
}
|
|
2993
2906
|
/**
|
|
@@ -3168,6 +3081,7 @@ async function probeMediaDurationMs(params) {
|
|
|
3168
3081
|
}, async (workspace) => {
|
|
3169
3082
|
const ext = normalizeLowercaseStringOrEmpty(path.extname(params.fileName));
|
|
3170
3083
|
const inferredExt = ext && ext.length <= 12 ? ext : mediaKindFromMime(params.contentType) === "video" ? ".mp4" : ".ogg";
|
|
3084
|
+
const inputPath = await workspace.write(`input${inferredExt}`, params.buffer);
|
|
3171
3085
|
const stdout = await runFfprobe([
|
|
3172
3086
|
"-v",
|
|
3173
3087
|
"error",
|
|
@@ -3175,7 +3089,7 @@ async function probeMediaDurationMs(params) {
|
|
|
3175
3089
|
"format=duration",
|
|
3176
3090
|
"-of",
|
|
3177
3091
|
"csv=p=0",
|
|
3178
|
-
|
|
3092
|
+
inputPath
|
|
3179
3093
|
], { timeoutMs: 5e3 });
|
|
3180
3094
|
const seconds = Number.parseFloat(stdout.trim());
|
|
3181
3095
|
if (!Number.isFinite(seconds) || seconds <= 0) return;
|
|
@@ -3315,7 +3229,29 @@ async function sendMediaFeishu(params) {
|
|
|
3315
3229
|
}
|
|
3316
3230
|
//#endregion
|
|
3317
3231
|
//#region extensions/feishu/src/presentation-card.ts
|
|
3318
|
-
const
|
|
3232
|
+
const RENDERED_FEISHU_CARD = Symbol("openclaw.renderedFeishuCard");
|
|
3233
|
+
const FEISHU_PRESENTATION_FALLBACK_MARKER = "__openclawPresentationFallback";
|
|
3234
|
+
const FEISHU_PRESENTATION_CAPABILITIES = {
|
|
3235
|
+
supported: true,
|
|
3236
|
+
buttons: true,
|
|
3237
|
+
selects: false,
|
|
3238
|
+
context: true,
|
|
3239
|
+
divider: true,
|
|
3240
|
+
limits: {
|
|
3241
|
+
actions: {
|
|
3242
|
+
maxActions: 20,
|
|
3243
|
+
maxActionsPerRow: 5,
|
|
3244
|
+
maxLabelLength: 40,
|
|
3245
|
+
maxValueBytes: 1024
|
|
3246
|
+
},
|
|
3247
|
+
text: {
|
|
3248
|
+
maxLength: 4e3,
|
|
3249
|
+
encoding: "characters",
|
|
3250
|
+
markdownDialect: "markdown"
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3254
|
+
const FEISHU_CARD_MAX_BYTES = 30720;
|
|
3319
3255
|
const FEISHU_CARD_MAX_ELEMENTS = 200;
|
|
3320
3256
|
function resolveFeishuRichReply(payload) {
|
|
3321
3257
|
const interactive = normalizeLegacyInteractiveReply(payload.interactive);
|
|
@@ -3324,23 +3260,22 @@ function resolveFeishuRichReply(payload) {
|
|
|
3324
3260
|
presentation: normalizeMessagePresentation(payload.presentation) ?? (interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0)
|
|
3325
3261
|
};
|
|
3326
3262
|
}
|
|
3327
|
-
function
|
|
3328
|
-
const fallbackText =
|
|
3329
|
-
const fallbackHasCommand = params.fallbackHasCommand === true || params.presentation?.blocks.some((block) => block.type === "select" ? block.options.some(({ action }) => action?.type === "command") : block.type === "buttons" && block.buttons.some(({ action, disabled
|
|
3263
|
+
function buildFeishuPresentationFallback(params) {
|
|
3264
|
+
const fallbackText = renderFeishuPresentationFallbackText(params, params.textFormat);
|
|
3265
|
+
const fallbackHasCommand = params.fallbackHasCommand === true || params.presentation?.blocks.some((block) => block.type === "select" ? block.options.some(({ action }) => action?.type === "command") : block.type === "buttons" && block.buttons.some(({ action, disabled }) => !disabled && action?.type === "command")) === true;
|
|
3330
3266
|
return {
|
|
3331
3267
|
fallbackText,
|
|
3332
3268
|
fallbackHasCommand,
|
|
3333
|
-
|
|
3269
|
+
commentText: fallbackHasCommand ? `${fallbackText}\n\n> Interactive buttons are unavailable in Feishu document comments. You can type the command shown above manually.` : fallbackText
|
|
3334
3270
|
};
|
|
3335
3271
|
}
|
|
3336
3272
|
function countFeishuCardElements(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
3337
3273
|
if (Array.isArray(value)) return value.reduce((count, entry) => count + countFeishuCardElements(entry, ancestors), 0);
|
|
3338
|
-
if (!value
|
|
3274
|
+
if (!isRecord(value)) return 0;
|
|
3339
3275
|
if (ancestors.has(value)) return 201;
|
|
3340
3276
|
ancestors.add(value);
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
for (const entry of Object.values(record)) {
|
|
3277
|
+
let count = typeof value.tag === "string" ? 1 : 0;
|
|
3278
|
+
for (const entry of Object.values(value)) {
|
|
3344
3279
|
count += countFeishuCardElements(entry, ancestors);
|
|
3345
3280
|
if (count > FEISHU_CARD_MAX_ELEMENTS) break;
|
|
3346
3281
|
}
|
|
@@ -3357,26 +3292,6 @@ function isFeishuCardWithinEnvelope(card) {
|
|
|
3357
3292
|
function assertFeishuCardWithinEnvelope(card, label = "Feishu card") {
|
|
3358
3293
|
if (!isFeishuCardWithinEnvelope(card)) throw new Error(`${label} exceeds the 30 KB or 200-element API limit.`);
|
|
3359
3294
|
}
|
|
3360
|
-
function escapeFeishuCardMarkdownText(text) {
|
|
3361
|
-
return text.replace(/[&<>]/g, (char) => {
|
|
3362
|
-
switch (char) {
|
|
3363
|
-
case "&": return "&";
|
|
3364
|
-
case "<": return "<";
|
|
3365
|
-
case ">": return ">";
|
|
3366
|
-
default: return char;
|
|
3367
|
-
}
|
|
3368
|
-
});
|
|
3369
|
-
}
|
|
3370
|
-
function resolveSafeFeishuButtonUrl(url) {
|
|
3371
|
-
const trimmed = url?.trim();
|
|
3372
|
-
if (!trimmed) return;
|
|
3373
|
-
try {
|
|
3374
|
-
const parsed = new URL(trimmed);
|
|
3375
|
-
return parsed.protocol === "https:" || parsed.protocol === "http:" ? trimmed : void 0;
|
|
3376
|
-
} catch {
|
|
3377
|
-
return;
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
3295
|
function resolveFeishuButtonUrl(button) {
|
|
3381
3296
|
if (button.action?.type === "url" || button.action?.type === "web-app") return button.action.url;
|
|
3382
3297
|
if (button.action) return;
|
|
@@ -3387,30 +3302,43 @@ function resolveFeishuCommandButtonValue(button) {
|
|
|
3387
3302
|
if (button.action) return;
|
|
3388
3303
|
return button.value;
|
|
3389
3304
|
}
|
|
3305
|
+
function renderFeishuPresentationFallbackText(params, textFormat = "plain") {
|
|
3306
|
+
const presentation = params.presentation;
|
|
3307
|
+
return renderMessagePresentationFallbackText({
|
|
3308
|
+
...params,
|
|
3309
|
+
presentation: presentation && {
|
|
3310
|
+
...presentation,
|
|
3311
|
+
blocks: presentation.blocks.map((block) => block.type === "buttons" ? {
|
|
3312
|
+
type: block.type,
|
|
3313
|
+
buttons: block.buttons.map((button) => {
|
|
3314
|
+
const url = resolveFeishuButtonUrl(button);
|
|
3315
|
+
return {
|
|
3316
|
+
...button,
|
|
3317
|
+
...textFormat === "markdown" ? { label: escapeFeishuCardPlainText(button.label) } : {},
|
|
3318
|
+
...url && !resolveSafeFeishuButtonUrl(url) ? { disabled: true } : {}
|
|
3319
|
+
};
|
|
3320
|
+
})
|
|
3321
|
+
} : block)
|
|
3322
|
+
}
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3390
3325
|
function mapFeishuButtonType(style) {
|
|
3391
3326
|
if (style === "primary" || style === "success") return "primary";
|
|
3392
3327
|
if (style === "danger") return "danger";
|
|
3393
3328
|
return "default";
|
|
3394
3329
|
}
|
|
3395
3330
|
function buildFeishuPayloadButton(button) {
|
|
3396
|
-
const
|
|
3397
|
-
const rendered = {
|
|
3398
|
-
tag: "button",
|
|
3399
|
-
text: {
|
|
3400
|
-
tag: "plain_text",
|
|
3401
|
-
content: button.label
|
|
3402
|
-
},
|
|
3403
|
-
type: mapFeishuButtonType(button.style)
|
|
3404
|
-
};
|
|
3405
|
-
const url = resolveFeishuButtonUrl(button);
|
|
3406
|
-
if (url) {
|
|
3407
|
-
const safeUrl = resolveSafeFeishuButtonUrl(url);
|
|
3408
|
-
if (safeUrl) behaviors.push({
|
|
3409
|
-
type: "open_url",
|
|
3410
|
-
default_url: safeUrl
|
|
3411
|
-
});
|
|
3412
|
-
}
|
|
3331
|
+
const url = resolveSafeFeishuButtonUrl(resolveFeishuButtonUrl(button));
|
|
3413
3332
|
const value = resolveFeishuCommandButtonValue(button);
|
|
3333
|
+
if (button.disabled || !url && !value) return {
|
|
3334
|
+
tag: "markdown",
|
|
3335
|
+
content: `- ${escapeFeishuCardPlainText(button.label)}`
|
|
3336
|
+
};
|
|
3337
|
+
const behaviors = [];
|
|
3338
|
+
if (url) behaviors.push({
|
|
3339
|
+
type: "open_url",
|
|
3340
|
+
default_url: url
|
|
3341
|
+
});
|
|
3414
3342
|
if (value) behaviors.push({
|
|
3415
3343
|
type: "callback",
|
|
3416
3344
|
value: createFeishuCardInteractionEnvelope({
|
|
@@ -3419,9 +3347,15 @@ function buildFeishuPayloadButton(button) {
|
|
|
3419
3347
|
q: value
|
|
3420
3348
|
})
|
|
3421
3349
|
});
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3350
|
+
return {
|
|
3351
|
+
tag: "button",
|
|
3352
|
+
text: {
|
|
3353
|
+
tag: "plain_text",
|
|
3354
|
+
content: button.label
|
|
3355
|
+
},
|
|
3356
|
+
type: mapFeishuButtonType(button.style),
|
|
3357
|
+
behaviors
|
|
3358
|
+
};
|
|
3425
3359
|
}
|
|
3426
3360
|
function buildFeishuCardElementsForBlock(block) {
|
|
3427
3361
|
if (block.type === "text") return [{
|
|
@@ -3433,7 +3367,7 @@ function buildFeishuCardElementsForBlock(block) {
|
|
|
3433
3367
|
content: `<font color='grey'>${escapeFeishuCardMarkdownText(block.text)}</font>`
|
|
3434
3368
|
}];
|
|
3435
3369
|
if (block.type === "divider") return [{ tag: "hr" }];
|
|
3436
|
-
if (block.type === "buttons") return block.buttons.map(
|
|
3370
|
+
if (block.type === "buttons") return block.buttons.map(buildFeishuPayloadButton);
|
|
3437
3371
|
if (block.type === "chart") return [{
|
|
3438
3372
|
tag: "markdown",
|
|
3439
3373
|
content: escapeFeishuCardMarkdownText(renderMessagePresentationChartFallbackText(block))
|
|
@@ -3442,10 +3376,9 @@ function buildFeishuCardElementsForBlock(block) {
|
|
|
3442
3376
|
tag: "markdown",
|
|
3443
3377
|
content: escapeFeishuCardMarkdownText(renderMessagePresentationTableFallbackText(block))
|
|
3444
3378
|
}];
|
|
3445
|
-
const labels = block.options.map((option) => `- ${option.label}`).join("\n");
|
|
3446
3379
|
return [{
|
|
3447
3380
|
tag: "markdown",
|
|
3448
|
-
content:
|
|
3381
|
+
content: escapeFeishuCardMarkdownText(renderMessagePresentationFallbackText({ presentation: { blocks: [block] } }))
|
|
3449
3382
|
}];
|
|
3450
3383
|
}
|
|
3451
3384
|
function resolvePresentationHeaderTemplate(tone) {
|
|
@@ -3465,13 +3398,7 @@ function buildFeishuPresentationCardElements(params) {
|
|
|
3465
3398
|
if (elements.length > 0) return elements;
|
|
3466
3399
|
return [{
|
|
3467
3400
|
tag: "markdown",
|
|
3468
|
-
content:
|
|
3469
|
-
text: params.fallbackText,
|
|
3470
|
-
presentation: params.presentation.title ? {
|
|
3471
|
-
...params.presentation.tone ? { tone: params.presentation.tone } : {},
|
|
3472
|
-
blocks: params.presentation.blocks
|
|
3473
|
-
} : params.presentation
|
|
3474
|
-
})
|
|
3401
|
+
content: ""
|
|
3475
3402
|
}];
|
|
3476
3403
|
}
|
|
3477
3404
|
function buildFeishuPresentationCard(params) {
|
|
@@ -3488,5 +3415,172 @@ function buildFeishuPresentationCard(params) {
|
|
|
3488
3415
|
body: { elements: buildFeishuPresentationCardElements(params) }
|
|
3489
3416
|
};
|
|
3490
3417
|
}
|
|
3418
|
+
function markRenderedFeishuCard(card) {
|
|
3419
|
+
Object.defineProperty(card, RENDERED_FEISHU_CARD, {
|
|
3420
|
+
value: true,
|
|
3421
|
+
enumerable: false
|
|
3422
|
+
});
|
|
3423
|
+
return card;
|
|
3424
|
+
}
|
|
3425
|
+
function readNativeFeishuCard(payload) {
|
|
3426
|
+
const feishuData = payload.channelData?.feishu;
|
|
3427
|
+
if (!isRecord(feishuData)) return;
|
|
3428
|
+
const card = feishuData.card ?? feishuData.interactiveCard;
|
|
3429
|
+
if (!isRecord(card)) return;
|
|
3430
|
+
const rendered = card;
|
|
3431
|
+
if (rendered[RENDERED_FEISHU_CARD] === true) return rendered;
|
|
3432
|
+
const sanitizedCard = sanitizeNativeFeishuCard(card);
|
|
3433
|
+
return sanitizedCard ? markRenderedFeishuCard(sanitizedCard) : void 0;
|
|
3434
|
+
}
|
|
3435
|
+
function consumeFeishuPresentationFallbackMarker(payload) {
|
|
3436
|
+
const feishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
|
|
3437
|
+
const presentationFallback = feishuData?.[FEISHU_PRESENTATION_FALLBACK_MARKER];
|
|
3438
|
+
if (!isRecord(presentationFallback) || typeof presentationFallback.hasVisibleContent !== "boolean") return { payload };
|
|
3439
|
+
const nextFeishuData = { ...feishuData };
|
|
3440
|
+
delete nextFeishuData[FEISHU_PRESENTATION_FALLBACK_MARKER];
|
|
3441
|
+
const nextChannelData = { ...payload.channelData };
|
|
3442
|
+
if (Object.keys(nextFeishuData).length > 0) nextChannelData.feishu = nextFeishuData;
|
|
3443
|
+
else delete nextChannelData.feishu;
|
|
3444
|
+
return {
|
|
3445
|
+
payload: {
|
|
3446
|
+
...payload,
|
|
3447
|
+
channelData: Object.keys(nextChannelData).length > 0 ? nextChannelData : void 0
|
|
3448
|
+
},
|
|
3449
|
+
presentationFallback: { hasVisibleContent: presentationFallback.hasVisibleContent }
|
|
3450
|
+
};
|
|
3451
|
+
}
|
|
3452
|
+
function buildFeishuPayloadCard(params) {
|
|
3453
|
+
const nativeCard = readNativeFeishuCard(params.payload);
|
|
3454
|
+
const rawText = params.text ?? params.payload.text;
|
|
3455
|
+
const textCard = readNativeFeishuCardJson(rawText);
|
|
3456
|
+
const { interactive, presentation } = resolveFeishuRichReply(params.payload);
|
|
3457
|
+
let card = nativeCard ?? (!presentation ? textCard : void 0);
|
|
3458
|
+
const isNativeCard = card !== void 0;
|
|
3459
|
+
if (!card && presentation) card = buildFeishuPresentationCard({
|
|
3460
|
+
presentation: {
|
|
3461
|
+
...presentation,
|
|
3462
|
+
title: presentation.title ?? resolveFeishuIdentityHeaderTitle(params.identity)
|
|
3463
|
+
},
|
|
3464
|
+
fallbackText: textCard ? void 0 : resolveLegacyInteractiveTextFallback({
|
|
3465
|
+
text: rawText,
|
|
3466
|
+
interactive
|
|
3467
|
+
})
|
|
3468
|
+
});
|
|
3469
|
+
if (!card) return;
|
|
3470
|
+
if (params.mentions?.length) card = {
|
|
3471
|
+
...card,
|
|
3472
|
+
body: {
|
|
3473
|
+
...card.body,
|
|
3474
|
+
elements: [{
|
|
3475
|
+
tag: "markdown",
|
|
3476
|
+
content: buildMentionedCardContent(params.mentions, "").trimEnd()
|
|
3477
|
+
}, ...card.body.elements]
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
if (isNativeCard) assertFeishuCardWithinEnvelope(card, "Feishu native card");
|
|
3481
|
+
return isFeishuCardWithinEnvelope(card) ? markRenderedFeishuCard(card) : void 0;
|
|
3482
|
+
}
|
|
3483
|
+
function renderFeishuPresentationPayload({ payload, presentation, sourcePresentation, ctx }) {
|
|
3484
|
+
const card = buildFeishuPayloadCard({
|
|
3485
|
+
payload,
|
|
3486
|
+
text: payload.text,
|
|
3487
|
+
identity: ctx.identity,
|
|
3488
|
+
mentions: ctx.mentions
|
|
3489
|
+
});
|
|
3490
|
+
const isComment = Boolean(parseFeishuCommentTarget(ctx.to));
|
|
3491
|
+
const fallbackPresentation = !card || isComment ? sourcePresentation ?? presentation : presentation;
|
|
3492
|
+
const { fallbackText, fallbackHasCommand } = buildFeishuPresentationFallback({
|
|
3493
|
+
text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
|
|
3494
|
+
presentation: fallbackPresentation,
|
|
3495
|
+
textFormat: isComment ? "plain" : "markdown"
|
|
3496
|
+
});
|
|
3497
|
+
const existingFeishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
|
|
3498
|
+
if (!card) return {
|
|
3499
|
+
...payload,
|
|
3500
|
+
text: fallbackText,
|
|
3501
|
+
channelData: {
|
|
3502
|
+
...payload.channelData,
|
|
3503
|
+
feishu: {
|
|
3504
|
+
...existingFeishuData,
|
|
3505
|
+
[FEISHU_PRESENTATION_FALLBACK_MARKER]: { hasVisibleContent: Boolean(renderFeishuPresentationFallbackText({ presentation: fallbackPresentation }).trim()) },
|
|
3506
|
+
...fallbackHasCommand ? { fallbackHasCommand: true } : {}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
};
|
|
3510
|
+
return {
|
|
3511
|
+
...payload,
|
|
3512
|
+
text: fallbackText,
|
|
3513
|
+
channelData: {
|
|
3514
|
+
...payload.channelData,
|
|
3515
|
+
feishu: {
|
|
3516
|
+
...existingFeishuData,
|
|
3517
|
+
card,
|
|
3518
|
+
...fallbackHasCommand ? { fallbackHasCommand: true } : {}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
async function renderFeishuReplyPayload(payload, ctx) {
|
|
3524
|
+
const { presentation } = resolveFeishuRichReply(payload);
|
|
3525
|
+
if (!presentation) return { payload };
|
|
3526
|
+
const { interactive: _interactive, ...withoutInteractive } = await renderPresentationForDelivery({
|
|
3527
|
+
presentationCapabilities: FEISHU_PRESENTATION_CAPABILITIES,
|
|
3528
|
+
renderPresentation: (adapted, sourcePresentation) => renderFeishuPresentationPayload({
|
|
3529
|
+
payload: adapted,
|
|
3530
|
+
presentation: adapted.presentation,
|
|
3531
|
+
sourcePresentation,
|
|
3532
|
+
ctx
|
|
3533
|
+
})
|
|
3534
|
+
}, {
|
|
3535
|
+
...payload,
|
|
3536
|
+
presentation
|
|
3537
|
+
});
|
|
3538
|
+
return {
|
|
3539
|
+
payload: withoutInteractive,
|
|
3540
|
+
card: readNativeFeishuCard(withoutInteractive)
|
|
3541
|
+
};
|
|
3542
|
+
}
|
|
3543
|
+
//#endregion
|
|
3544
|
+
//#region extensions/feishu/src/reply-delivery-result.ts
|
|
3545
|
+
const noVisibleFeishuReplyDelivery = { visibleReplySent: false };
|
|
3546
|
+
function hasProviderIdentity(result) {
|
|
3547
|
+
return Boolean(result && (result.messageId?.trim() || result.receipt?.primaryPlatformMessageId?.trim() || result.receipt?.platformMessageIds.length));
|
|
3548
|
+
}
|
|
3549
|
+
/** Normalizes every physical Lark send behind one logical reply payload. */
|
|
3550
|
+
function createFeishuReplyDeliveryResult(params) {
|
|
3551
|
+
const results = params.visibleReplySent ? (params.results ?? []).filter(hasProviderIdentity) : [];
|
|
3552
|
+
const receipt = results.length > 0 ? createMessageReceiptFromOutboundResults({
|
|
3553
|
+
results,
|
|
3554
|
+
...params.kind ? { kind: params.kind } : {}
|
|
3555
|
+
}) : void 0;
|
|
3556
|
+
if (!receipt) return {
|
|
3557
|
+
visibleReplySent: params.visibleReplySent,
|
|
3558
|
+
...params.content === void 0 ? {} : { content: params.content }
|
|
3559
|
+
};
|
|
3560
|
+
return {
|
|
3561
|
+
messageIds: [...receipt.platformMessageIds],
|
|
3562
|
+
receipt,
|
|
3563
|
+
visibleReplySent: params.visibleReplySent,
|
|
3564
|
+
...params.content === void 0 ? {} : { content: params.content }
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
/** Preserves the first result's provider identity while retaining supplemental ids. */
|
|
3568
|
+
function mergeFeishuReplyDeliveryResults(results, content) {
|
|
3569
|
+
const visible = results.filter((result) => result.visibleReplySent === true);
|
|
3570
|
+
const acceptedContent = visible.flatMap((result) => result.content === void 0 ? [] : [result.content]);
|
|
3571
|
+
return createFeishuReplyDeliveryResult({
|
|
3572
|
+
results: visible,
|
|
3573
|
+
visibleReplySent: visible.length > 0,
|
|
3574
|
+
content: content === void 0 ? acceptedContent.length > 0 ? acceptedContent.join("\n\n") : void 0 : content
|
|
3575
|
+
});
|
|
3576
|
+
}
|
|
3577
|
+
/** Keeps a failed outcome while exposing the provider-visible subset to core lifecycle hooks. */
|
|
3578
|
+
function createFeishuPartialReplyDeliveryError(cause, result) {
|
|
3579
|
+
if (result.visibleReplySent !== true) return cause instanceof Error ? cause : new Error(formatErrorMessage(cause), { cause });
|
|
3580
|
+
return createChannelPartialDeliveryError(cause, {
|
|
3581
|
+
...result,
|
|
3582
|
+
visibleReplySent: true
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3491
3585
|
//#endregion
|
|
3492
|
-
export {
|
|
3586
|
+
export { parseCommentContentElements as $, isFeishuBroadcastMention as A, decodeFeishuCardAction as At, deliverCommentThreadText as B, editMessageFeishu as C, resolveFeishuGroupToolPolicy as Ct, sendMessageFeishu as D, FEISHU_CARD_INTERACTION_VERSION as Dt, sendCardFeishu as E, resolveFeishuChatType as Et, createFeishuSendReceipt as F, createFeishuToolClient as G, feishuExternalToolResult as H, assertFeishuApiSuccess as I, cleanupAmbientCommentTypingReaction as J, resolveAnyEnabledFeishuToolsConfig as K, buildFeishuMediaFallbackText as L, isFeishuGroupChatType as M, parseInteractiveCardContent as N, sendStructuredCardFeishu as O, buildFeishuCardActionTextFallback as Ot, parsePostContent as P, formatFeishuApiError as Q, resolveFeishuIdentityEmoji as R, chunkFeishuCardMarkdown as S, resolveFeishuGroupSenderActivationIngressAccess as St, listFeishuThreadMessages as T, normalizeFeishuChatType as Tt, toolExecutionErrorResult as U, registerFeishuDriveTools as V, unknownToolActionResult as W, encodeQuery as X, createCommentTypingReactionLifecycle as Y, extractReplyText as Z, resolveFeishuRichReply as _, hasExplicitFeishuGroupConfig as _t, FEISHU_PRESENTATION_CAPABILITIES as a, resolveFeishuCardTemplate as at, sendStickerFeishu as b, resolveFeishuGroupConfig as bt, buildFeishuPresentationCard as c, materializeFeishuPostMarkdownSoftBreaks as ct, isFeishuCardWithinEnvelope as d, authorizeFeishuChatMemberRead as dt, requestFeishuApi as et, markRenderedFeishuCard as f, canEnumerateAllFeishuGroups as ft, renderFeishuReplyPayload as g, resolveFeishuChatReadPreliminaryAuthorization as gt, renderFeishuPresentationPayload as h, isFeishuGroupReadEnabled as ht, noVisibleFeishuReplyDelivery as i, readNativeFeishuCardJson as it, isMentionForwardRequest as j, extractMentionTargets as k, createFeishuCardInteractionEnvelope as kt, buildFeishuPresentationFallback as l, parseFeishuMarkdown as lt, renderFeishuPresentationFallbackText as m, isFeishuGroupReadAllowed as mt, createFeishuReplyDeliveryResult as n, normalizeCommentFileType as nt, assertFeishuCardWithinEnvelope as o, chunkFeishuMarkdown as ot, readNativeFeishuCard as p, canEnumerateAllFeishuPeers as pt, resolveFeishuToolAccount as q, mergeFeishuReplyDeliveryResults as r, parseFeishuCommentTarget as rt, buildFeishuPayloadCard as s, chunkFeishuPostMarkdown as st, createFeishuPartialReplyDeliveryError as t, buildFeishuCommentTarget as tt, consumeFeishuPresentationFallbackMarker as u, assertFeishuChatReadAllowed as ut, saveMessageResourceFeishu as v, normalizeFeishuAllowEntry as vt, getMessageFeishu as w, resolveFeishuReplyPolicy as wt, shouldSuppressFeishuTextForVoiceMedia as x, resolveFeishuGroupConversationIngressAccess as xt, sendMediaFeishu as y, resolveFeishuDmIngressAccess as yt, resolveFeishuIdentityHeaderTitle as z };
|