@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,12 +1,12 @@
|
|
|
1
|
-
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
2
|
-
import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, n as inspectFeishuCredentials, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
|
+
import { i as resolveReceiveIdType, n as looksLikeFeishuId, r as normalizeFeishuTarget } from "./targets-BTQCYRZQ.js";
|
|
3
|
+
import { B as deliverCommentThreadText, Ct as resolveFeishuGroupToolPolicy, D as sendMessageFeishu, E as sendCardFeishu, Et as resolveFeishuChatType, F as createFeishuSendReceipt, J as cleanupAmbientCommentTypingReaction, L as buildFeishuMediaFallbackText, O as sendStructuredCardFeishu, S as chunkFeishuCardMarkdown, Tt as normalizeFeishuChatType, _ as resolveFeishuRichReply, a as FEISHU_PRESENTATION_CAPABILITIES, bt as resolveFeishuGroupConfig, c as buildFeishuPresentationCard, ct as materializeFeishuPostMarkdownSoftBreaks, d as isFeishuCardWithinEnvelope, dt as authorizeFeishuChatMemberRead, f as markRenderedFeishuCard, ft as canEnumerateAllFeishuGroups, gt as resolveFeishuChatReadPreliminaryAuthorization, h as renderFeishuPresentationPayload, ht as isFeishuGroupReadEnabled, it as readNativeFeishuCardJson, l as buildFeishuPresentationFallback, m as renderFeishuPresentationFallbackText, mt as isFeishuGroupReadAllowed, n as createFeishuReplyDeliveryResult, o as assertFeishuCardWithinEnvelope, ot as chunkFeishuMarkdown, p as readNativeFeishuCard, pt as canEnumerateAllFeishuPeers, rt as parseFeishuCommentTarget, s as buildFeishuPayloadCard, st as chunkFeishuPostMarkdown, t as createFeishuPartialReplyDeliveryError, u as consumeFeishuPresentationFallbackMarker, ut as assertFeishuChatReadAllowed, x as shouldSuppressFeishuTextForVoiceMedia, y as sendMediaFeishu, z as resolveFeishuIdentityHeaderTitle } from "./reply-delivery-result-CYaKkbUN.js";
|
|
4
|
+
import { n as normalizeCompatibilityConfig, o as normalizeFeishuExternalKey, r as FeishuChannelConfigSchema, t as legacyConfigRules } from "./doctor-contract-w6FpMypv.js";
|
|
5
5
|
import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, n as buildFeishuModelOverrideParentCandidates, o as resolveConfiguredFeishuGroupSessionScope, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
|
|
6
|
-
import { t as messageActionTargetAliases } from "./security-audit-
|
|
7
|
-
import { r as createFeishuClient } from "./client-
|
|
6
|
+
import { t as messageActionTargetAliases } from "./security-audit-DKfR4Cv3.js";
|
|
7
|
+
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
8
8
|
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-BCpDLdg9.js";
|
|
9
|
-
import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-
|
|
9
|
+
import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-CK5rVR-1.js";
|
|
10
10
|
import { t as resolveFeishuSessionConversation } from "./session-conversation-DFCIvQK-.js";
|
|
11
11
|
import { createLazyRuntimeModule, createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
12
12
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
@@ -16,23 +16,19 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
|
16
16
|
import { ToolAuthorizationError, createActionGate } from "openclaw/plugin-sdk/channel-actions";
|
|
17
17
|
import { adaptScopedAccountAccessor, createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
18
18
|
import { buildChannelOutboundSessionRoute, createChatChannelPlugin, stripChannelTargetPrefix } from "openclaw/plugin-sdk/channel-core";
|
|
19
|
-
import { createAccountStatusSink, createReplyToFanout, createRuntimeOutboundDelegates, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
19
|
+
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, createReplyToFanout, createRuntimeOutboundDelegates, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
20
20
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
21
21
|
import { createAllowlistProviderGroupPolicyWarningCollector, createConditionalWarningCollector } from "openclaw/plugin-sdk/channel-policy";
|
|
22
22
|
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
|
|
23
23
|
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
|
|
24
24
|
import { applyDirectoryQueryAndLimit, createChannelDirectoryAdapter, createRuntimeDirectoryLiveAdapter, listDirectoryGroupEntriesFromMapKeysAndAllowFrom, listDirectoryUserEntriesFromAllowFrom, listDirectoryUserEntriesFromAllowFromAndMapKeys } from "openclaw/plugin-sdk/directory-runtime";
|
|
25
25
|
import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
|
|
26
|
-
import {
|
|
26
|
+
import { resolveLegacyInteractiveTextFallback } from "openclaw/plugin-sdk/interactive-runtime";
|
|
27
27
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
28
28
|
import { buildProbeChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
29
29
|
import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
30
30
|
import { convertMarkdownTables, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
31
31
|
import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
32
|
-
import { normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/account-id";
|
|
33
|
-
import { DmPolicySchema, GroupPolicySchema, ReplyToModeSchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
34
|
-
import { z } from "zod";
|
|
35
|
-
import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$2 } from "openclaw/plugin-sdk/secret-input";
|
|
36
32
|
import fs from "node:fs";
|
|
37
33
|
import os from "node:os";
|
|
38
34
|
import path from "node:path";
|
|
@@ -41,17 +37,19 @@ import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
|
41
37
|
import { deleteSessionEntry, isValidAgentHarnessSessionStoreEntry, listSessionEntries, loadTranscriptEventsSync, resolveSessionStoreBackupPaths, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
42
38
|
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
43
39
|
import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
44
|
-
import {
|
|
40
|
+
import { isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
|
|
45
41
|
import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
|
|
46
42
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
47
43
|
import { getReplyPayloadTtsSupplement, resolvePayloadMediaUrls, sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload } from "openclaw/plugin-sdk/reply-payload";
|
|
48
44
|
import { statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
45
|
+
import { readStringParam } from "openclaw/plugin-sdk/param-readers";
|
|
49
46
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
50
|
-
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$
|
|
47
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createSetupTranslator, formatDocsLink, hasConfiguredSecretInput, mergeAllowFromEntries, patchScopedAccountConfig, promptSingleChannelSecretInput, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
51
48
|
import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
|
|
52
49
|
//#region extensions/feishu/src/approval-auth.ts
|
|
53
50
|
function normalizeFeishuApproverId(value) {
|
|
54
|
-
const
|
|
51
|
+
const normalized = normalizeFeishuTarget(String(value));
|
|
52
|
+
const trimmed = normalizeOptionalLowercaseString(normalized);
|
|
55
53
|
return trimmed?.startsWith("ou_") ? trimmed : void 0;
|
|
56
54
|
}
|
|
57
55
|
const feishuApprovalAuth = createChannelApprovalAuth({
|
|
@@ -65,282 +63,6 @@ const feishuApprovalAuth = createChannelApprovalAuth({
|
|
|
65
63
|
normalizeApprover: normalizeFeishuApproverId
|
|
66
64
|
}).approvalAuth;
|
|
67
65
|
//#endregion
|
|
68
|
-
//#region extensions/feishu/src/config-schema.ts
|
|
69
|
-
const ChannelActionsSchema = z.object({
|
|
70
|
-
reactions: z.boolean().optional(),
|
|
71
|
-
sticker: z.boolean().optional()
|
|
72
|
-
}).strict().optional();
|
|
73
|
-
const FeishuGroupPolicySchema = z.union([GroupPolicySchema, z.literal("allowall").transform(() => "open")]);
|
|
74
|
-
const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().url().startsWith("https://")]);
|
|
75
|
-
const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
|
|
76
|
-
const FeishuWebhookPathSchema = z.string().refine((value) => normalizeFeishuWebhookPath(value) === value, { message: "webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\" to repair it" });
|
|
77
|
-
const TtsOverrideSchema = z.object({
|
|
78
|
-
auto: z.enum([
|
|
79
|
-
"off",
|
|
80
|
-
"always",
|
|
81
|
-
"inbound",
|
|
82
|
-
"tagged"
|
|
83
|
-
]).optional(),
|
|
84
|
-
enabled: z.boolean().optional(),
|
|
85
|
-
mode: z.enum(["final", "all"]).optional(),
|
|
86
|
-
provider: z.string().optional(),
|
|
87
|
-
persona: z.string().optional(),
|
|
88
|
-
personas: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
89
|
-
summaryModel: z.string().optional(),
|
|
90
|
-
modelOverrides: z.record(z.string(), z.unknown()).optional(),
|
|
91
|
-
providers: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
92
|
-
prefsPath: z.string().optional(),
|
|
93
|
-
maxTextLength: z.number().int().min(1).optional(),
|
|
94
|
-
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
|
|
95
|
-
}).strict().optional();
|
|
96
|
-
const ToolPolicySchema = z.object({
|
|
97
|
-
allow: z.array(z.string()).optional(),
|
|
98
|
-
deny: z.array(z.string()).optional()
|
|
99
|
-
}).strict().optional();
|
|
100
|
-
const DmConfigSchema = z.object({
|
|
101
|
-
enabled: z.boolean().optional(),
|
|
102
|
-
systemPrompt: z.string().optional()
|
|
103
|
-
}).strict().optional();
|
|
104
|
-
const MarkdownConfigSchema = z.object({
|
|
105
|
-
mode: z.enum([
|
|
106
|
-
"native",
|
|
107
|
-
"escape",
|
|
108
|
-
"strip"
|
|
109
|
-
]).optional(),
|
|
110
|
-
tableMode: z.enum([
|
|
111
|
-
"native",
|
|
112
|
-
"ascii",
|
|
113
|
-
"simple"
|
|
114
|
-
]).optional()
|
|
115
|
-
}).strict().optional();
|
|
116
|
-
const RenderModeSchema = z.enum([
|
|
117
|
-
"auto",
|
|
118
|
-
"raw",
|
|
119
|
-
"card"
|
|
120
|
-
]).optional();
|
|
121
|
-
const BlockStreamingCoalesceSchema = z.object({
|
|
122
|
-
minChars: z.number().int().positive().optional(),
|
|
123
|
-
maxChars: z.number().int().positive().optional(),
|
|
124
|
-
idleMs: z.number().int().nonnegative().optional()
|
|
125
|
-
}).strict().optional();
|
|
126
|
-
const FeishuStreamingSchema = z.object({
|
|
127
|
-
mode: z.enum(["off", "partial"]).optional(),
|
|
128
|
-
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
129
|
-
block: z.object({
|
|
130
|
-
enabled: z.boolean().optional(),
|
|
131
|
-
coalesce: BlockStreamingCoalesceSchema
|
|
132
|
-
}).strict().optional()
|
|
133
|
-
}).strict().optional();
|
|
134
|
-
const ChannelHeartbeatVisibilitySchema = z.object({
|
|
135
|
-
visibility: z.enum(["visible", "hidden"]).optional(),
|
|
136
|
-
intervalMs: z.number().int().positive().optional()
|
|
137
|
-
}).strict().optional();
|
|
138
|
-
/**
|
|
139
|
-
* Dynamic agent creation configuration.
|
|
140
|
-
* When enabled, a new agent is created for each unique DM user.
|
|
141
|
-
*/
|
|
142
|
-
const DynamicAgentCreationSchema = z.object({
|
|
143
|
-
enabled: z.boolean().optional(),
|
|
144
|
-
workspaceTemplate: z.string().optional(),
|
|
145
|
-
agentDirTemplate: z.string().optional(),
|
|
146
|
-
maxAgents: z.number().int().positive().optional()
|
|
147
|
-
}).strict().optional();
|
|
148
|
-
/**
|
|
149
|
-
* Feishu tools configuration.
|
|
150
|
-
* Controls which tool categories are enabled.
|
|
151
|
-
*
|
|
152
|
-
* Dependencies:
|
|
153
|
-
* - wiki requires doc (wiki content is edited via doc tools)
|
|
154
|
-
* - perm can work independently but is typically used with drive
|
|
155
|
-
*/
|
|
156
|
-
const FeishuToolsConfigSchema = z.object({
|
|
157
|
-
doc: z.boolean().optional(),
|
|
158
|
-
chat: z.boolean().optional(),
|
|
159
|
-
wiki: z.boolean().optional(),
|
|
160
|
-
drive: z.boolean().optional(),
|
|
161
|
-
perm: z.boolean().optional(),
|
|
162
|
-
scopes: z.boolean().optional(),
|
|
163
|
-
bitable: z.boolean().optional()
|
|
164
|
-
}).strict().optional();
|
|
165
|
-
/**
|
|
166
|
-
* Group session scope for routing Feishu group messages.
|
|
167
|
-
* - "group" (default): one session per group chat
|
|
168
|
-
* - "group_sender": one session per (group + sender)
|
|
169
|
-
* - "group_topic": one session per group topic thread (falls back to group if no topic)
|
|
170
|
-
* - "group_topic_sender": one session per (group + topic thread + sender),
|
|
171
|
-
* falls back to (group + sender) if no topic
|
|
172
|
-
*/
|
|
173
|
-
const GroupSessionScopeSchema = z.enum([
|
|
174
|
-
"group",
|
|
175
|
-
"group_sender",
|
|
176
|
-
"group_topic",
|
|
177
|
-
"group_topic_sender"
|
|
178
|
-
]).optional();
|
|
179
|
-
/**
|
|
180
|
-
* @deprecated Use groupSessionScope instead.
|
|
181
|
-
*
|
|
182
|
-
* Topic session isolation mode for group chats.
|
|
183
|
-
* - "disabled" (default): All messages in a group share one session
|
|
184
|
-
* - "enabled": Messages in different topics get separate sessions
|
|
185
|
-
*
|
|
186
|
-
* Topic routing uses Feishu topic-group `thread_id` when the event identifies a
|
|
187
|
-
* native topic group, and keeps `root_id` precedence for normal groups so
|
|
188
|
-
* reply-created threads stay on the initiating message session.
|
|
189
|
-
*/
|
|
190
|
-
const TopicSessionModeSchema = z.enum(["disabled", "enabled"]).optional();
|
|
191
|
-
const ReactionNotificationModeSchema = z.enum([
|
|
192
|
-
"off",
|
|
193
|
-
"own",
|
|
194
|
-
"all"
|
|
195
|
-
]).optional();
|
|
196
|
-
/**
|
|
197
|
-
* Reply-in-thread mode for group chats.
|
|
198
|
-
* - "disabled" (default): Bot replies are normal inline replies
|
|
199
|
-
* - "enabled": Bot replies create or continue a Feishu topic thread
|
|
200
|
-
*
|
|
201
|
-
* When enabled, the Feishu reply API is called with `reply_in_thread: true`,
|
|
202
|
-
* causing the reply to appear as a topic (话题) under the original message.
|
|
203
|
-
*/
|
|
204
|
-
const ReplyInThreadSchema = z.enum(["disabled", "enabled"]).optional();
|
|
205
|
-
const FeishuGroupSchema = buildGroupEntrySchema({
|
|
206
|
-
tools: ToolPolicySchema,
|
|
207
|
-
groupSessionScope: GroupSessionScopeSchema,
|
|
208
|
-
topicSessionMode: TopicSessionModeSchema,
|
|
209
|
-
replyInThread: ReplyInThreadSchema
|
|
210
|
-
}).omit({ toolsBySender: true });
|
|
211
|
-
const FeishuSharedConfigShape = {
|
|
212
|
-
webhookHost: z.string().optional(),
|
|
213
|
-
webhookPort: z.number().int().positive().optional(),
|
|
214
|
-
capabilities: z.array(z.string()).optional(),
|
|
215
|
-
markdown: MarkdownConfigSchema,
|
|
216
|
-
configWrites: z.boolean().optional(),
|
|
217
|
-
replyToMode: ReplyToModeSchema.optional(),
|
|
218
|
-
responsePrefix: z.string().optional(),
|
|
219
|
-
dmPolicy: DmPolicySchema.optional(),
|
|
220
|
-
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
221
|
-
groupPolicy: FeishuGroupPolicySchema.optional(),
|
|
222
|
-
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
223
|
-
groupSenderAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
224
|
-
requireMention: z.boolean().optional(),
|
|
225
|
-
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
|
226
|
-
historyLimit: z.number().int().min(0).optional(),
|
|
227
|
-
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
228
|
-
dms: z.record(z.string(), DmConfigSchema).optional(),
|
|
229
|
-
textChunkLimit: z.number().int().positive().optional(),
|
|
230
|
-
mediaMaxMb: z.number().positive().optional(),
|
|
231
|
-
httpTimeoutMs: z.number().int().positive().max(3e5).optional(),
|
|
232
|
-
heartbeatVisibility: ChannelHeartbeatVisibilitySchema,
|
|
233
|
-
renderMode: RenderModeSchema,
|
|
234
|
-
streaming: FeishuStreamingSchema,
|
|
235
|
-
tools: FeishuToolsConfigSchema,
|
|
236
|
-
actions: ChannelActionsSchema,
|
|
237
|
-
replyInThread: ReplyInThreadSchema,
|
|
238
|
-
reactionNotifications: ReactionNotificationModeSchema,
|
|
239
|
-
typingIndicator: z.boolean().optional(),
|
|
240
|
-
resolveSenderNames: z.boolean().optional(),
|
|
241
|
-
allowBots: z.boolean().optional(),
|
|
242
|
-
vcAutoJoin: z.boolean().optional(),
|
|
243
|
-
tts: TtsOverrideSchema
|
|
244
|
-
};
|
|
245
|
-
/**
|
|
246
|
-
* Per-account configuration.
|
|
247
|
-
* All fields are optional - missing fields inherit from top-level config.
|
|
248
|
-
*/
|
|
249
|
-
const FeishuAccountConfigSchema = z.object({
|
|
250
|
-
enabled: z.boolean().optional(),
|
|
251
|
-
name: z.string().optional(),
|
|
252
|
-
appId: z.string().optional(),
|
|
253
|
-
appSecret: buildSecretInputSchema().optional(),
|
|
254
|
-
encryptKey: buildSecretInputSchema().optional(),
|
|
255
|
-
verificationToken: buildSecretInputSchema().optional(),
|
|
256
|
-
domain: FeishuDomainSchema.optional(),
|
|
257
|
-
connectionMode: FeishuConnectionModeSchema.optional(),
|
|
258
|
-
webhookPath: FeishuWebhookPathSchema.optional(),
|
|
259
|
-
...FeishuSharedConfigShape,
|
|
260
|
-
groupSessionScope: GroupSessionScopeSchema,
|
|
261
|
-
topicSessionMode: TopicSessionModeSchema
|
|
262
|
-
}).strict();
|
|
263
|
-
const FeishuChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(z.object({
|
|
264
|
-
enabled: z.boolean().optional(),
|
|
265
|
-
defaultAccount: z.string().optional(),
|
|
266
|
-
appId: z.string().optional(),
|
|
267
|
-
appSecret: buildSecretInputSchema().optional(),
|
|
268
|
-
encryptKey: buildSecretInputSchema().optional(),
|
|
269
|
-
verificationToken: buildSecretInputSchema().optional(),
|
|
270
|
-
domain: FeishuDomainSchema.optional().default("feishu"),
|
|
271
|
-
connectionMode: FeishuConnectionModeSchema.optional().default("websocket"),
|
|
272
|
-
webhookPath: FeishuWebhookPathSchema.optional().default(DEFAULT_FEISHU_WEBHOOK_PATH),
|
|
273
|
-
...FeishuSharedConfigShape,
|
|
274
|
-
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
275
|
-
reactionNotifications: ReactionNotificationModeSchema.optional().default("own"),
|
|
276
|
-
groupPolicy: FeishuGroupPolicySchema.optional().default("allowlist"),
|
|
277
|
-
requireMention: z.boolean().optional(),
|
|
278
|
-
groupSessionScope: GroupSessionScopeSchema,
|
|
279
|
-
topicSessionMode: TopicSessionModeSchema,
|
|
280
|
-
dynamicAgentCreation: DynamicAgentCreationSchema,
|
|
281
|
-
typingIndicator: z.boolean().optional().default(true),
|
|
282
|
-
resolveSenderNames: z.boolean().optional().default(true)
|
|
283
|
-
}).strict(), {
|
|
284
|
-
accountSchema: FeishuAccountConfigSchema,
|
|
285
|
-
optionalAccount: true
|
|
286
|
-
}).superRefine((value, ctx) => {
|
|
287
|
-
const defaultAccount = value.defaultAccount?.trim();
|
|
288
|
-
if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
|
|
289
|
-
const normalizedDefaultAccount = normalizeAccountId$1(defaultAccount);
|
|
290
|
-
if (!Object.hasOwn(value.accounts, normalizedDefaultAccount)) ctx.addIssue({
|
|
291
|
-
code: z.ZodIssueCode.custom,
|
|
292
|
-
path: ["defaultAccount"],
|
|
293
|
-
message: `channels.feishu.defaultAccount="${defaultAccount}" does not match a configured account key`
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
const defaultConnectionMode = value.connectionMode ?? "websocket";
|
|
297
|
-
const defaultVerificationTokenConfigured = hasConfiguredSecretInput$2(value.verificationToken);
|
|
298
|
-
const defaultEncryptKeyConfigured = hasConfiguredSecretInput$2(value.encryptKey);
|
|
299
|
-
if (defaultConnectionMode === "webhook") {
|
|
300
|
-
if (!defaultVerificationTokenConfigured) ctx.addIssue({
|
|
301
|
-
code: z.ZodIssueCode.custom,
|
|
302
|
-
path: ["verificationToken"],
|
|
303
|
-
message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.verificationToken"
|
|
304
|
-
});
|
|
305
|
-
if (!defaultEncryptKeyConfigured) ctx.addIssue({
|
|
306
|
-
code: z.ZodIssueCode.custom,
|
|
307
|
-
path: ["encryptKey"],
|
|
308
|
-
message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.encryptKey"
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
for (const [accountId, account] of Object.entries(value.accounts ?? {})) {
|
|
312
|
-
if (!account) continue;
|
|
313
|
-
if ((account.connectionMode ?? defaultConnectionMode) !== "webhook") continue;
|
|
314
|
-
const accountVerificationTokenConfigured = hasConfiguredSecretInput$2(account.verificationToken) || defaultVerificationTokenConfigured;
|
|
315
|
-
const accountEncryptKeyConfigured = hasConfiguredSecretInput$2(account.encryptKey) || defaultEncryptKeyConfigured;
|
|
316
|
-
if (!accountVerificationTokenConfigured) ctx.addIssue({
|
|
317
|
-
code: z.ZodIssueCode.custom,
|
|
318
|
-
path: [
|
|
319
|
-
"accounts",
|
|
320
|
-
accountId,
|
|
321
|
-
"verificationToken"
|
|
322
|
-
],
|
|
323
|
-
message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires a verificationToken (account-level or top-level)`
|
|
324
|
-
});
|
|
325
|
-
if (!accountEncryptKeyConfigured) ctx.addIssue({
|
|
326
|
-
code: z.ZodIssueCode.custom,
|
|
327
|
-
path: [
|
|
328
|
-
"accounts",
|
|
329
|
-
accountId,
|
|
330
|
-
"encryptKey"
|
|
331
|
-
],
|
|
332
|
-
message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires an encryptKey (account-level or top-level)`
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
if (value.dmPolicy === "open") {
|
|
336
|
-
if (!(value.allowFrom ?? []).some((entry) => String(entry).trim() === "*")) ctx.addIssue({
|
|
337
|
-
code: z.ZodIssueCode.custom,
|
|
338
|
-
path: ["allowFrom"],
|
|
339
|
-
message: "channels.feishu.dmPolicy=\"open\" requires channels.feishu.allowFrom to include \"*\""
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}), { jsonSchemaMode: "input" });
|
|
343
|
-
//#endregion
|
|
344
66
|
//#region extensions/feishu/src/directory.static.ts
|
|
345
67
|
function toFeishuDirectoryPeers(ids) {
|
|
346
68
|
return ids.map((id) => ({
|
|
@@ -381,11 +103,12 @@ async function listFeishuDirectoryGroups(params) {
|
|
|
381
103
|
}).map((entry) => entry.id));
|
|
382
104
|
}
|
|
383
105
|
async function listAuthorizedFeishuDirectoryPeers(params) {
|
|
106
|
+
const account = resolveFeishuAccount({
|
|
107
|
+
cfg: params.cfg,
|
|
108
|
+
accountId: params.accountId
|
|
109
|
+
});
|
|
384
110
|
return toFeishuDirectoryPeers(listDirectoryUserEntriesFromAllowFrom({
|
|
385
|
-
allowFrom:
|
|
386
|
-
cfg: params.cfg,
|
|
387
|
-
accountId: params.accountId
|
|
388
|
-
}).config.allowFrom,
|
|
111
|
+
allowFrom: account.config.allowFrom,
|
|
389
112
|
query: params.query,
|
|
390
113
|
limit: params.limit,
|
|
391
114
|
normalizeId: (entry) => normalizeFeishuTarget(entry) ?? entry
|
|
@@ -396,17 +119,19 @@ async function listAuthorizedFeishuDirectoryGroups(params) {
|
|
|
396
119
|
cfg: params.cfg,
|
|
397
120
|
accountId: params.accountId
|
|
398
121
|
});
|
|
399
|
-
|
|
400
|
-
|
|
122
|
+
const enabledGroups = Object.fromEntries(Object.entries(account.config.groups ?? {}).filter(([, group]) => group?.enabled !== false));
|
|
123
|
+
const authorizedEntries = listDirectoryGroupEntriesFromMapKeysAndAllowFrom({
|
|
124
|
+
groups: enabledGroups,
|
|
401
125
|
allowFrom: account.config.groupAllowFrom
|
|
402
|
-
}).filter((entry) => isFeishuGroupReadAllowed(params.cfg, account, entry.id, false))
|
|
126
|
+
}).filter((entry) => isFeishuGroupReadAllowed(params.cfg, account, entry.id, false));
|
|
127
|
+
return toFeishuDirectoryGroups(applyDirectoryQueryAndLimit(authorizedEntries.map((entry) => entry.id), params));
|
|
403
128
|
}
|
|
404
129
|
//#endregion
|
|
405
130
|
//#region extensions/feishu/src/doctor.ts
|
|
406
131
|
const FEISHU_STATE_DIR = "feishu";
|
|
407
132
|
const BACKUP_PREFIX = "feishu-state-repair";
|
|
408
133
|
const BLANK_USER_MESSAGE_REPAIR_THRESHOLD = 3;
|
|
409
|
-
const SESSION_FILE_INSPECTION_MAX_BYTES =
|
|
134
|
+
const SESSION_FILE_INSPECTION_MAX_BYTES = 16777216;
|
|
410
135
|
function timestampForPath(now = /* @__PURE__ */ new Date()) {
|
|
411
136
|
return now.toISOString().replaceAll(":", "-");
|
|
412
137
|
}
|
|
@@ -470,9 +195,12 @@ function isFeishuSessionEntry(key, value) {
|
|
|
470
195
|
if (isFeishuSessionStoreKey(key)) return true;
|
|
471
196
|
if (!isRecord(value)) return false;
|
|
472
197
|
if (normalizeLowercaseStringOrEmpty(value.channel) === "feishu" || normalizeLowercaseStringOrEmpty(value.lastChannel) === "feishu") return true;
|
|
473
|
-
|
|
474
|
-
if (normalizeLowercaseStringOrEmpty(
|
|
475
|
-
|
|
198
|
+
const route = isRecord(value.route) ? value.route : null;
|
|
199
|
+
if (normalizeLowercaseStringOrEmpty(route?.channel) === "feishu") return true;
|
|
200
|
+
const deliveryContext = isRecord(value.deliveryContext) ? value.deliveryContext : null;
|
|
201
|
+
if (normalizeLowercaseStringOrEmpty(deliveryContext?.channel) === "feishu") return true;
|
|
202
|
+
const pendingDeliveryContext = isRecord(value.pendingFinalDeliveryContext) ? value.pendingFinalDeliveryContext : null;
|
|
203
|
+
if (normalizeLowercaseStringOrEmpty(pendingDeliveryContext?.channel) === "feishu") return true;
|
|
476
204
|
const origin = isRecord(value.origin) ? value.origin : null;
|
|
477
205
|
const originProvider = normalizeLowercaseStringOrEmpty(origin?.provider);
|
|
478
206
|
const originSurface = normalizeLowercaseStringOrEmpty(origin?.surface);
|
|
@@ -1003,8 +731,6 @@ const feishuDoctor = {
|
|
|
1003
731
|
};
|
|
1004
732
|
//#endregion
|
|
1005
733
|
//#region extensions/feishu/src/outbound.ts
|
|
1006
|
-
const RENDERED_FEISHU_CARD = Symbol("openclaw.renderedFeishuCard");
|
|
1007
|
-
const FEISHU_PRESENTATION_FALLBACK_MARKER = "__openclawPresentationFallback";
|
|
1008
734
|
const FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER = "__openclawPropagateMediaUploadFailure";
|
|
1009
735
|
const FEISHU_TEXT_CHUNK_LIMIT = 4e3;
|
|
1010
736
|
function normalizePossibleLocalImagePath(text) {
|
|
@@ -1036,22 +762,6 @@ function normalizePossibleLocalImagePath(text) {
|
|
|
1036
762
|
function shouldUseCard(text) {
|
|
1037
763
|
return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text);
|
|
1038
764
|
}
|
|
1039
|
-
function markRenderedFeishuCard(card) {
|
|
1040
|
-
Object.defineProperty(card, RENDERED_FEISHU_CARD, {
|
|
1041
|
-
value: true,
|
|
1042
|
-
enumerable: false
|
|
1043
|
-
});
|
|
1044
|
-
return card;
|
|
1045
|
-
}
|
|
1046
|
-
function readNativeFeishuCard(payload) {
|
|
1047
|
-
const feishuData = payload.channelData?.feishu;
|
|
1048
|
-
if (!isRecord(feishuData)) return;
|
|
1049
|
-
const card = feishuData.card ?? feishuData.interactiveCard;
|
|
1050
|
-
if (!isRecord(card)) return;
|
|
1051
|
-
if (card[RENDERED_FEISHU_CARD] === true) return card;
|
|
1052
|
-
const sanitizedCard = sanitizeNativeFeishuCard(card);
|
|
1053
|
-
return sanitizedCard ? markRenderedFeishuCard(sanitizedCard) : void 0;
|
|
1054
|
-
}
|
|
1055
765
|
function toFeishuOutboundResult(result) {
|
|
1056
766
|
const { chatId, ...delivery } = result;
|
|
1057
767
|
return {
|
|
@@ -1066,25 +776,26 @@ async function reportFeishuOutboundDelivery(result, onDeliveryResult) {
|
|
|
1066
776
|
await onDeliveryResult?.(attachChannelToResult("feishu", toFeishuOutboundResult(result)));
|
|
1067
777
|
return result;
|
|
1068
778
|
}
|
|
1069
|
-
function
|
|
1070
|
-
const feishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
|
|
1071
|
-
if (feishuData?.[FEISHU_PRESENTATION_FALLBACK_MARKER] !== true) return {
|
|
1072
|
-
payload,
|
|
1073
|
-
presentationFallback: false
|
|
1074
|
-
};
|
|
1075
|
-
const nextFeishuData = { ...feishuData };
|
|
1076
|
-
delete nextFeishuData[FEISHU_PRESENTATION_FALLBACK_MARKER];
|
|
1077
|
-
const nextChannelData = { ...payload.channelData };
|
|
1078
|
-
if (Object.keys(nextFeishuData).length > 0) nextChannelData.feishu = nextFeishuData;
|
|
1079
|
-
else delete nextChannelData.feishu;
|
|
779
|
+
function aggregateFeishuSendResult(result, results) {
|
|
1080
780
|
return {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
781
|
+
...result,
|
|
782
|
+
receipt: {
|
|
783
|
+
...createMessageReceiptFromOutboundResults({ results }),
|
|
784
|
+
primaryPlatformMessageId: result.messageId
|
|
785
|
+
}
|
|
1086
786
|
};
|
|
1087
787
|
}
|
|
788
|
+
function partialFeishuSendError(error, results) {
|
|
789
|
+
if (results.length === 0 && error instanceof Error) return error;
|
|
790
|
+
const accepted = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
|
|
791
|
+
return createFeishuPartialReplyDeliveryError(error, {
|
|
792
|
+
...accepted,
|
|
793
|
+
...createFeishuReplyDeliveryResult({
|
|
794
|
+
results: [...results, accepted],
|
|
795
|
+
visibleReplySent: results.length > 0 || accepted !== void 0
|
|
796
|
+
})
|
|
797
|
+
});
|
|
798
|
+
}
|
|
1088
799
|
function readFeishuPropagateMediaUploadFailure(payload) {
|
|
1089
800
|
return (isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.[FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER] === true;
|
|
1090
801
|
}
|
|
@@ -1092,87 +803,6 @@ function buildFeishuPropagationOnlyChannelData(payload) {
|
|
|
1092
803
|
if ((isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0)?.["__openclawPropagateMediaUploadFailure"] !== true) return;
|
|
1093
804
|
return { feishu: { [FEISHU_PROPAGATE_MEDIA_UPLOAD_FAILURE_MARKER]: true } };
|
|
1094
805
|
}
|
|
1095
|
-
function buildFeishuPayloadCard(params) {
|
|
1096
|
-
const nativeCard = readNativeFeishuCard(params.payload);
|
|
1097
|
-
if (nativeCard) {
|
|
1098
|
-
assertFeishuCardWithinEnvelope(nativeCard, "Feishu native card");
|
|
1099
|
-
return nativeCard;
|
|
1100
|
-
}
|
|
1101
|
-
const rawText = params.text ?? params.payload.text;
|
|
1102
|
-
const textCard = readNativeFeishuCardJson(rawText);
|
|
1103
|
-
const { interactive, presentation } = resolveFeishuRichReply(params.payload);
|
|
1104
|
-
if (!presentation && !interactive) {
|
|
1105
|
-
if (!textCard) return;
|
|
1106
|
-
assertFeishuCardWithinEnvelope(textCard, "Feishu native card");
|
|
1107
|
-
return markRenderedFeishuCard(textCard);
|
|
1108
|
-
}
|
|
1109
|
-
const text = textCard ? void 0 : resolveLegacyInteractiveTextFallback({
|
|
1110
|
-
text: rawText,
|
|
1111
|
-
interactive
|
|
1112
|
-
});
|
|
1113
|
-
const elements = presentation ? buildFeishuPresentationCardElements({
|
|
1114
|
-
presentation,
|
|
1115
|
-
fallbackText: text
|
|
1116
|
-
}) : [{
|
|
1117
|
-
tag: "markdown",
|
|
1118
|
-
content: renderMessagePresentationFallbackText({
|
|
1119
|
-
text,
|
|
1120
|
-
presentation
|
|
1121
|
-
})
|
|
1122
|
-
}];
|
|
1123
|
-
const identityTitle = resolveFeishuIdentityHeaderTitle(params.identity);
|
|
1124
|
-
const title = presentation?.title ?? identityTitle;
|
|
1125
|
-
const template = resolveFeishuCardTemplate(presentation?.tone === "danger" ? "red" : presentation?.tone === "warning" ? "orange" : presentation?.tone === "success" ? "green" : "blue");
|
|
1126
|
-
const card = markRenderedFeishuCard({
|
|
1127
|
-
schema: "2.0",
|
|
1128
|
-
config: { width_mode: "fill" },
|
|
1129
|
-
...title ? { header: {
|
|
1130
|
-
title: {
|
|
1131
|
-
tag: "plain_text",
|
|
1132
|
-
content: title
|
|
1133
|
-
},
|
|
1134
|
-
template: template ?? "blue"
|
|
1135
|
-
} } : {},
|
|
1136
|
-
body: { elements }
|
|
1137
|
-
});
|
|
1138
|
-
return isFeishuCardWithinEnvelope(card) ? card : void 0;
|
|
1139
|
-
}
|
|
1140
|
-
function renderFeishuPresentationPayload({ payload, presentation, ctx }) {
|
|
1141
|
-
const { fallbackText, fallbackHasCommand } = buildFeishuCommentPresentationFallback({
|
|
1142
|
-
text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
|
|
1143
|
-
presentation
|
|
1144
|
-
});
|
|
1145
|
-
const card = buildFeishuPayloadCard({
|
|
1146
|
-
payload,
|
|
1147
|
-
text: payload.text,
|
|
1148
|
-
identity: ctx.identity
|
|
1149
|
-
});
|
|
1150
|
-
const existingFeishuData = isRecord(payload.channelData?.feishu) ? payload.channelData.feishu : void 0;
|
|
1151
|
-
if (!card) return {
|
|
1152
|
-
...payload,
|
|
1153
|
-
text: fallbackText,
|
|
1154
|
-
channelData: {
|
|
1155
|
-
...payload.channelData,
|
|
1156
|
-
feishu: {
|
|
1157
|
-
...existingFeishuData,
|
|
1158
|
-
[FEISHU_PRESENTATION_FALLBACK_MARKER]: true,
|
|
1159
|
-
...fallbackHasCommand ? { fallbackHasCommand: true } : {}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
};
|
|
1163
|
-
return {
|
|
1164
|
-
...payload,
|
|
1165
|
-
text: fallbackText,
|
|
1166
|
-
channelData: {
|
|
1167
|
-
...payload.channelData,
|
|
1168
|
-
feishu: {
|
|
1169
|
-
...existingFeishuData,
|
|
1170
|
-
card,
|
|
1171
|
-
...fallbackHasCommand ? { fallbackHasCommand: true } : {}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
};
|
|
1175
|
-
}
|
|
1176
806
|
function resolveFeishuReplyMode(params) {
|
|
1177
807
|
const replyToMessageId = params.replyToId?.trim();
|
|
1178
808
|
if (replyToMessageId) return {
|
|
@@ -1194,10 +824,11 @@ function resolveFeishuReplyMode(params) {
|
|
|
1194
824
|
async function sendCommentThreadReply(params) {
|
|
1195
825
|
const target = parseFeishuCommentTarget(params.to);
|
|
1196
826
|
if (!target) return null;
|
|
1197
|
-
const
|
|
827
|
+
const account = resolveFeishuAccount({
|
|
1198
828
|
cfg: params.cfg,
|
|
1199
829
|
accountId: params.accountId
|
|
1200
|
-
})
|
|
830
|
+
});
|
|
831
|
+
const client = createFeishuClient(account);
|
|
1201
832
|
const replyId = params.replyId?.trim();
|
|
1202
833
|
try {
|
|
1203
834
|
const result = await deliverCommentThreadText(client, {
|
|
@@ -1236,40 +867,50 @@ async function sendOutboundText(params) {
|
|
|
1236
867
|
cfg,
|
|
1237
868
|
accountId
|
|
1238
869
|
}).config?.renderMode ?? "auto";
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
to,
|
|
1242
|
-
text,
|
|
1243
|
-
accountId,
|
|
1244
|
-
replyToMessageId,
|
|
1245
|
-
replyInThread
|
|
1246
|
-
}), onDeliveryResult);
|
|
1247
|
-
const normalizedText = materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, resolveMarkdownTableMode({
|
|
870
|
+
const useCard = renderMode === "card" || renderMode === "auto" && shouldUseCard(text);
|
|
871
|
+
const tableMode = resolveMarkdownTableMode({
|
|
1248
872
|
cfg,
|
|
1249
873
|
channel: "feishu"
|
|
1250
|
-
})
|
|
1251
|
-
const
|
|
874
|
+
});
|
|
875
|
+
const normalizedText = useCard ? text : materializeFeishuPostMarkdownSoftBreaks(convertMarkdownTables(text, tableMode));
|
|
876
|
+
const chunkOptions = {
|
|
1252
877
|
text: normalizedText,
|
|
1253
878
|
limit: resolveTextChunkLimit(cfg, "feishu", accountId, { fallbackLimit: FEISHU_TEXT_CHUNK_LIMIT }),
|
|
1254
879
|
mode: resolveChunkMode(cfg, "feishu", accountId)
|
|
1255
|
-
}
|
|
1256
|
-
|
|
880
|
+
};
|
|
881
|
+
const subChunks = useCard ? chunkFeishuCardMarkdown({
|
|
882
|
+
...chunkOptions,
|
|
883
|
+
header: params.header
|
|
884
|
+
}) : chunkFeishuPostMarkdown(chunkOptions);
|
|
885
|
+
const results = [];
|
|
1257
886
|
const preserveThread = replyInThread === true;
|
|
1258
887
|
const nextReplyToMessageId = createReplyToFanout({
|
|
1259
888
|
replyToId: replyToMessageId,
|
|
1260
889
|
replyToIdSource: params.replyToIdSource,
|
|
1261
890
|
replyToMode: params.replyToMode ?? "first"
|
|
1262
891
|
});
|
|
1263
|
-
for (const [i, chunk] of (subChunks.length ? subChunks : [normalizedText]).entries())
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
892
|
+
for (const [i, chunk] of (subChunks.length ? subChunks : [normalizedText]).entries()) try {
|
|
893
|
+
const sendParams = {
|
|
894
|
+
cfg,
|
|
895
|
+
to,
|
|
896
|
+
text: chunk,
|
|
897
|
+
accountId,
|
|
898
|
+
replyToMessageId: preserveThread ? replyToMessageId : nextReplyToMessageId(),
|
|
899
|
+
replyInThread: preserveThread ? true : i === 0 ? replyInThread : void 0
|
|
900
|
+
};
|
|
901
|
+
const result = useCard ? await sendStructuredCardFeishu({
|
|
902
|
+
...sendParams,
|
|
903
|
+
header: params.header
|
|
904
|
+
}) : await sendMessageFeishu({
|
|
905
|
+
...sendParams,
|
|
906
|
+
preparedPostText: true
|
|
907
|
+
});
|
|
908
|
+
results.push(result);
|
|
909
|
+
await reportFeishuOutboundDelivery(result, onDeliveryResult);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
throw partialFeishuSendError(error, results);
|
|
912
|
+
}
|
|
913
|
+
return aggregateFeishuSendResult(results.at(-1), results);
|
|
1273
914
|
}
|
|
1274
915
|
async function sendFeishuFallbackPayload(params) {
|
|
1275
916
|
const propagateMediaUploadFailure = readFeishuPropagateMediaUploadFailure(params.payload);
|
|
@@ -1334,7 +975,7 @@ async function sendFeishuTtsSupplementPayload(params) {
|
|
|
1334
975
|
if (params.sendVisiblePayload) {
|
|
1335
976
|
lastResult = await params.sendVisiblePayload(nextReplyToId());
|
|
1336
977
|
await ctx.onDeliveryResult?.(lastResult);
|
|
1337
|
-
} else if (params.supplement.visibleTextAlreadyDelivered !== true) {
|
|
978
|
+
} else if (params.hasVisiblePresentationFallback || params.supplement.visibleTextAlreadyDelivered !== true) {
|
|
1338
979
|
const text = params.payload.text?.trim() ? params.payload.text : params.supplement.spokenText;
|
|
1339
980
|
for (const chunk of chunkFeishuMarkdown(text, FEISHU_TEXT_CHUNK_LIMIT)) lastResult = await sendText({
|
|
1340
981
|
...ctx,
|
|
@@ -1359,26 +1000,7 @@ const feishuOutbound = {
|
|
|
1359
1000
|
chunker: chunkFeishuMarkdown,
|
|
1360
1001
|
chunkerMode: "markdown",
|
|
1361
1002
|
textChunkLimit: FEISHU_TEXT_CHUNK_LIMIT,
|
|
1362
|
-
presentationCapabilities:
|
|
1363
|
-
supported: true,
|
|
1364
|
-
buttons: true,
|
|
1365
|
-
selects: false,
|
|
1366
|
-
context: true,
|
|
1367
|
-
divider: true,
|
|
1368
|
-
limits: {
|
|
1369
|
-
actions: {
|
|
1370
|
-
maxActions: 20,
|
|
1371
|
-
maxActionsPerRow: 5,
|
|
1372
|
-
maxLabelLength: 40,
|
|
1373
|
-
maxValueBytes: 1024
|
|
1374
|
-
},
|
|
1375
|
-
text: {
|
|
1376
|
-
maxLength: FEISHU_TEXT_CHUNK_LIMIT,
|
|
1377
|
-
encoding: "characters",
|
|
1378
|
-
markdownDialect: "markdown"
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
},
|
|
1003
|
+
presentationCapabilities: FEISHU_PRESENTATION_CAPABILITIES,
|
|
1382
1004
|
renderPresentation: renderFeishuPresentationPayload,
|
|
1383
1005
|
sendPayload: async (ctx) => {
|
|
1384
1006
|
const { payload, presentationFallback } = consumeFeishuPresentationFallbackMarker(ctx.payload);
|
|
@@ -1386,8 +1008,9 @@ const feishuOutbound = {
|
|
|
1386
1008
|
if (parseFeishuCommentTarget(ctx.to)) {
|
|
1387
1009
|
const { presentation } = resolveFeishuRichReply(payload);
|
|
1388
1010
|
const textCard = readNativeFeishuCardJson(payload.text);
|
|
1389
|
-
const
|
|
1390
|
-
|
|
1011
|
+
const fallbackSourceText = textCard ? void 0 : payload.text;
|
|
1012
|
+
const { commentText: text, fallbackText } = buildFeishuPresentationFallback({
|
|
1013
|
+
text: fallbackSourceText,
|
|
1391
1014
|
presentation,
|
|
1392
1015
|
fallbackHasCommand: isRecord(payload.channelData?.feishu) && payload.channelData.feishu.fallbackHasCommand === true
|
|
1393
1016
|
});
|
|
@@ -1411,24 +1034,26 @@ const feishuOutbound = {
|
|
|
1411
1034
|
identity: ctx.identity
|
|
1412
1035
|
});
|
|
1413
1036
|
if (!card) {
|
|
1037
|
+
const { presentation } = resolveFeishuRichReply(payload);
|
|
1038
|
+
const fallbackPayload = presentation ? {
|
|
1039
|
+
...payload,
|
|
1040
|
+
text: renderFeishuPresentationFallbackText({
|
|
1041
|
+
text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
|
|
1042
|
+
presentation
|
|
1043
|
+
}, "markdown"),
|
|
1044
|
+
presentation: void 0,
|
|
1045
|
+
interactive: void 0
|
|
1046
|
+
} : payload;
|
|
1414
1047
|
if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
|
|
1415
1048
|
ctx,
|
|
1416
|
-
payload,
|
|
1417
|
-
supplement: ttsSupplement
|
|
1049
|
+
payload: fallbackPayload,
|
|
1050
|
+
supplement: ttsSupplement,
|
|
1051
|
+
hasVisiblePresentationFallback: presentationFallback?.hasVisibleContent ?? Boolean(renderFeishuPresentationFallbackText({ presentation }).trim())
|
|
1418
1052
|
});
|
|
1419
|
-
const { presentation } = resolveFeishuRichReply(payload);
|
|
1420
1053
|
return await sendFeishuFallbackPayload({
|
|
1421
1054
|
ctx,
|
|
1422
|
-
payload:
|
|
1423
|
-
|
|
1424
|
-
text: renderMessagePresentationFallbackText({
|
|
1425
|
-
text: readNativeFeishuCardJson(payload.text) ? void 0 : payload.text,
|
|
1426
|
-
presentation
|
|
1427
|
-
}),
|
|
1428
|
-
presentation: void 0,
|
|
1429
|
-
interactive: void 0
|
|
1430
|
-
} : payload,
|
|
1431
|
-
separateMediaAndText: presentationFallback || presentation !== void 0
|
|
1055
|
+
payload: fallbackPayload,
|
|
1056
|
+
separateMediaAndText: presentationFallback !== void 0 || presentation !== void 0
|
|
1432
1057
|
});
|
|
1433
1058
|
}
|
|
1434
1059
|
if (ttsSupplement) return await sendFeishuTtsSupplementPayload({
|
|
@@ -1561,25 +1186,7 @@ const feishuOutbound = {
|
|
|
1561
1186
|
replyInThread
|
|
1562
1187
|
}), onDeliveryResult));
|
|
1563
1188
|
}
|
|
1564
|
-
const
|
|
1565
|
-
cfg,
|
|
1566
|
-
accountId: accountId ?? void 0
|
|
1567
|
-
}).config?.renderMode ?? "auto";
|
|
1568
|
-
if (renderMode === "card" || renderMode === "auto" && shouldUseCard(text)) {
|
|
1569
|
-
const header = identity ? {
|
|
1570
|
-
title: resolveFeishuIdentityHeaderTitle(identity),
|
|
1571
|
-
template: "blue"
|
|
1572
|
-
} : void 0;
|
|
1573
|
-
return toFeishuOutboundResult(await reportFeishuOutboundDelivery(await sendStructuredCardFeishu({
|
|
1574
|
-
cfg,
|
|
1575
|
-
to,
|
|
1576
|
-
text,
|
|
1577
|
-
replyToMessageId,
|
|
1578
|
-
replyInThread,
|
|
1579
|
-
accountId: accountId ?? void 0,
|
|
1580
|
-
header: header?.title ? header : void 0
|
|
1581
|
-
}), onDeliveryResult));
|
|
1582
|
-
}
|
|
1189
|
+
const title = identity ? resolveFeishuIdentityHeaderTitle(identity) : void 0;
|
|
1583
1190
|
return toFeishuOutboundResult(await sendOutboundText({
|
|
1584
1191
|
cfg,
|
|
1585
1192
|
to,
|
|
@@ -1587,6 +1194,10 @@ const feishuOutbound = {
|
|
|
1587
1194
|
accountId: accountId ?? void 0,
|
|
1588
1195
|
replyToMessageId,
|
|
1589
1196
|
replyInThread,
|
|
1197
|
+
header: title ? {
|
|
1198
|
+
title,
|
|
1199
|
+
template: "blue"
|
|
1200
|
+
} : void 0,
|
|
1590
1201
|
...deliveryOptions
|
|
1591
1202
|
}));
|
|
1592
1203
|
},
|
|
@@ -1639,19 +1250,16 @@ const feishuOutbound = {
|
|
|
1639
1250
|
mediaUrl,
|
|
1640
1251
|
audioAsVoice
|
|
1641
1252
|
});
|
|
1642
|
-
let textSent = false;
|
|
1643
1253
|
let captionResult;
|
|
1644
|
-
if (text?.trim() && !suppressTextForVoiceMedia) {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
textSent = true;
|
|
1654
|
-
}
|
|
1254
|
+
if (text?.trim() && !suppressTextForVoiceMedia) captionResult = await sendOutboundText({
|
|
1255
|
+
cfg,
|
|
1256
|
+
to,
|
|
1257
|
+
text,
|
|
1258
|
+
accountId: accountId ?? void 0,
|
|
1259
|
+
...nextReplyMode(),
|
|
1260
|
+
...deliveryOptions
|
|
1261
|
+
});
|
|
1262
|
+
const results = captionResult ? [captionResult] : [];
|
|
1655
1263
|
let mediaResult;
|
|
1656
1264
|
const mediaReplyMode = nextReplyMode();
|
|
1657
1265
|
try {
|
|
@@ -1667,37 +1275,45 @@ const feishuOutbound = {
|
|
|
1667
1275
|
...audioAsVoice === true ? { audioAsVoice: true } : {}
|
|
1668
1276
|
});
|
|
1669
1277
|
} catch (err) {
|
|
1670
|
-
if (isChannelPartialDeliveryError(err)) throw err;
|
|
1278
|
+
if (isChannelPartialDeliveryError(err)) throw partialFeishuSendError(err, results);
|
|
1671
1279
|
if (propagateMediaUploadFailure) {
|
|
1672
|
-
if (
|
|
1673
|
-
messageIds: [captionResult.messageId],
|
|
1674
|
-
visibleReplySent: true
|
|
1675
|
-
});
|
|
1280
|
+
if (captionResult) throw partialFeishuSendError(err, results);
|
|
1676
1281
|
throw new Error(`Feishu send could not deliver the requested media attachment: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
1677
1282
|
}
|
|
1678
1283
|
console.error(`[feishu] sendMediaFeishu failed:`, err);
|
|
1679
|
-
|
|
1284
|
+
const fallbackText = await buildFeishuMediaFallbackText({
|
|
1285
|
+
text: captionResult ? void 0 : text,
|
|
1286
|
+
mediaUrl
|
|
1287
|
+
});
|
|
1288
|
+
try {
|
|
1289
|
+
const fallbackResult = await sendOutboundText({
|
|
1290
|
+
cfg,
|
|
1291
|
+
to,
|
|
1292
|
+
text: fallbackText,
|
|
1293
|
+
accountId: accountId ?? void 0,
|
|
1294
|
+
...captionResult ? nextReplyMode() : mediaReplyMode,
|
|
1295
|
+
...deliveryOptions
|
|
1296
|
+
});
|
|
1297
|
+
return toFeishuOutboundResult(aggregateFeishuSendResult(fallbackResult, [...results, fallbackResult]));
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
throw partialFeishuSendError(error, results);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
results.push(mediaResult);
|
|
1303
|
+
try {
|
|
1304
|
+
await reportFeishuOutboundDelivery(mediaResult, onDeliveryResult);
|
|
1305
|
+
if (mediaResult.voiceIntentDegradedToFile && text?.trim()) results.push(await sendOutboundText({
|
|
1680
1306
|
cfg,
|
|
1681
1307
|
to,
|
|
1682
|
-
text
|
|
1683
|
-
text: textSent ? void 0 : text,
|
|
1684
|
-
mediaUrl
|
|
1685
|
-
}),
|
|
1308
|
+
text,
|
|
1686
1309
|
accountId: accountId ?? void 0,
|
|
1687
|
-
...
|
|
1310
|
+
...nextReplyMode(),
|
|
1688
1311
|
...deliveryOptions
|
|
1689
1312
|
}));
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
throw partialFeishuSendError(error, results);
|
|
1690
1315
|
}
|
|
1691
|
-
|
|
1692
|
-
if (mediaResult.voiceIntentDegradedToFile && text?.trim()) await sendOutboundText({
|
|
1693
|
-
cfg,
|
|
1694
|
-
to,
|
|
1695
|
-
text,
|
|
1696
|
-
accountId: accountId ?? void 0,
|
|
1697
|
-
...nextReplyMode(),
|
|
1698
|
-
...deliveryOptions
|
|
1699
|
-
});
|
|
1700
|
-
return toFeishuOutboundResult(mediaResult);
|
|
1316
|
+
return toFeishuOutboundResult(aggregateFeishuSendResult(mediaResult, results));
|
|
1701
1317
|
}
|
|
1702
1318
|
})
|
|
1703
1319
|
};
|
|
@@ -1759,7 +1375,7 @@ function setFeishuNamedAccountEnabled(cfg, accountId, enabled) {
|
|
|
1759
1375
|
const feishuSetupAdapter = {
|
|
1760
1376
|
resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
|
|
1761
1377
|
applyAccountConfig: ({ cfg, accountId }) => {
|
|
1762
|
-
if (!accountId || accountId === DEFAULT_ACCOUNT_ID$
|
|
1378
|
+
if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return {
|
|
1763
1379
|
...cfg,
|
|
1764
1380
|
channels: {
|
|
1765
1381
|
...cfg.channels,
|
|
@@ -1791,15 +1407,15 @@ function isFeishuConfigured(cfg) {
|
|
|
1791
1407
|
const source = normalizeOptionalString(rec.source)?.toLowerCase();
|
|
1792
1408
|
const id = normalizeOptionalString(rec.id);
|
|
1793
1409
|
if (source === "env" && id) return Boolean(normalizeOptionalString(process.env[id]));
|
|
1794
|
-
return hasConfiguredSecretInput
|
|
1410
|
+
return hasConfiguredSecretInput(value);
|
|
1795
1411
|
};
|
|
1796
|
-
const topLevelConfigured = isAppIdConfigured(feishuCfg?.appId) && hasConfiguredSecretInput
|
|
1412
|
+
const topLevelConfigured = isAppIdConfigured(feishuCfg?.appId) && hasConfiguredSecretInput(feishuCfg?.appSecret);
|
|
1797
1413
|
const accountConfigured = Object.values(feishuCfg?.accounts ?? {}).some((account) => {
|
|
1798
1414
|
if (!account || typeof account !== "object") return false;
|
|
1799
1415
|
const hasOwnAppId = Object.hasOwn(account, "appId");
|
|
1800
1416
|
const hasOwnAppSecret = Object.hasOwn(account, "appSecret");
|
|
1801
1417
|
const accountAppIdConfigured = hasOwnAppId ? isAppIdConfigured(account.appId) : isAppIdConfigured(feishuCfg?.appId);
|
|
1802
|
-
const accountSecretConfigured = hasOwnAppSecret ? hasConfiguredSecretInput
|
|
1418
|
+
const accountSecretConfigured = hasOwnAppSecret ? hasConfiguredSecretInput(account.appSecret) : hasConfiguredSecretInput(feishuCfg?.appSecret);
|
|
1803
1419
|
return accountAppIdConfigured && accountSecretConfigured;
|
|
1804
1420
|
});
|
|
1805
1421
|
return topLevelConfigured || accountConfigured;
|
|
@@ -1827,7 +1443,7 @@ function patchFeishuConfig(cfg, accountId, patch) {
|
|
|
1827
1443
|
async function promptFeishuAllowFrom(params) {
|
|
1828
1444
|
const feishuCfg = params.cfg.channels?.feishu;
|
|
1829
1445
|
const resolvedAccountId = params.accountId ?? resolveDefaultFeishuAccountId(params.cfg);
|
|
1830
|
-
const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$
|
|
1446
|
+
const existingAllowFrom = (resolvedAccountId !== DEFAULT_ACCOUNT_ID$1 ? feishuCfg?.accounts?.[resolvedAccountId] : void 0)?.allowFrom ?? feishuCfg?.allowFrom ?? [];
|
|
1831
1447
|
await params.prompter.note([
|
|
1832
1448
|
t("wizard.feishu.allowlistIntro"),
|
|
1833
1449
|
t("wizard.feishu.allowlistFindUser"),
|
|
@@ -1835,11 +1451,12 @@ async function promptFeishuAllowFrom(params) {
|
|
|
1835
1451
|
"- ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
1836
1452
|
"- on_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
1837
1453
|
].join("\n"), t("wizard.feishu.allowlistTitle"));
|
|
1838
|
-
const
|
|
1454
|
+
const entry = await params.prompter.text({
|
|
1839
1455
|
message: t("wizard.feishu.allowFromPrompt"),
|
|
1840
1456
|
placeholder: "ou_xxxxx, ou_yyyyy",
|
|
1841
1457
|
initialValue: existingAllowFrom.length > 0 ? existingAllowFrom.map(String).join(", ") : void 0
|
|
1842
|
-
})
|
|
1458
|
+
});
|
|
1459
|
+
const mergedAllowFrom = mergeAllowFromEntries(existingAllowFrom, splitSetupEntries(entry));
|
|
1843
1460
|
return patchFeishuConfig(params.cfg, resolvedAccountId, { allowFrom: mergedAllowFrom });
|
|
1844
1461
|
}
|
|
1845
1462
|
async function noteFeishuCredentialHelp(prompter) {
|
|
@@ -1866,7 +1483,7 @@ const feishuDmPolicy = createChannelDmPolicy({
|
|
|
1866
1483
|
resolveAccount: (cfg, accountId) => {
|
|
1867
1484
|
const feishuCfg = cfg.channels?.feishu;
|
|
1868
1485
|
const resolvedAccountId = accountId ?? resolveDefaultFeishuAccountId(cfg);
|
|
1869
|
-
const account = resolvedAccountId === DEFAULT_ACCOUNT_ID$
|
|
1486
|
+
const account = resolvedAccountId === DEFAULT_ACCOUNT_ID$1 ? void 0 : feishuCfg?.accounts?.[resolvedAccountId];
|
|
1870
1487
|
return {
|
|
1871
1488
|
accountId: resolvedAccountId,
|
|
1872
1489
|
config: {
|
|
@@ -1890,7 +1507,7 @@ function applyNewAppSecurityPolicy(cfg, accountId, openId, groupPolicy) {
|
|
|
1890
1507
|
next = patchFeishuConfig(next, accountId, groupPatch);
|
|
1891
1508
|
return next;
|
|
1892
1509
|
}
|
|
1893
|
-
const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-
|
|
1510
|
+
const loadAppRegistrationModule = createLazyRuntimeModule(() => import("./app-registration-BI99e_JI.js"));
|
|
1894
1511
|
async function promptFeishuDomain(params) {
|
|
1895
1512
|
return await params.prompter.select({
|
|
1896
1513
|
message: t("wizard.feishu.domainPrompt"),
|
|
@@ -2054,7 +1671,7 @@ async function runEditFlow(params) {
|
|
|
2054
1671
|
if (value && typeof value === "object") {
|
|
2055
1672
|
const rec = value;
|
|
2056
1673
|
if (normalizeOptionalString(rec.source) && normalizeOptionalString(rec.id)) return normalizeOptionalString(process.env[rec.id]) ?? `env:${String(rec.id)}`;
|
|
2057
|
-
if (hasConfiguredSecretInput
|
|
1674
|
+
if (hasConfiguredSecretInput(value)) return "(configured)";
|
|
2058
1675
|
}
|
|
2059
1676
|
};
|
|
2060
1677
|
const existingAppId = resolveAppIdLabel(feishuCfg?.appId) ?? Object.values(feishuCfg?.accounts ?? {}).reduce((found, account) => {
|
|
@@ -2115,7 +1732,7 @@ const feishuSetupWizard = {
|
|
|
2115
1732
|
});
|
|
2116
1733
|
let probeResult = null;
|
|
2117
1734
|
if (configured && account.configured) try {
|
|
2118
|
-
const { probeFeishu } = await import("./probe-
|
|
1735
|
+
const { probeFeishu } = await import("./probe-D_SatdZl.js").then((n) => n.n);
|
|
2119
1736
|
probeResult = await probeFeishu(account);
|
|
2120
1737
|
} catch {}
|
|
2121
1738
|
if (!configured) return [formatFeishuStatusLine("needs-credentials")];
|
|
@@ -2154,6 +1771,45 @@ const feishuSetupWizard = {
|
|
|
2154
1771
|
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
|
2155
1772
|
};
|
|
2156
1773
|
//#endregion
|
|
1774
|
+
//#region extensions/feishu/src/sticker-catalog.ts
|
|
1775
|
+
const STICKER_QUERY_PATTERN = /^[^\p{Cs}]{1,128}$/u;
|
|
1776
|
+
function fitsStickerSearchResult(stickers) {
|
|
1777
|
+
return Buffer.byteLength(JSON.stringify({
|
|
1778
|
+
stickers,
|
|
1779
|
+
truncated: false
|
|
1780
|
+
}), "utf8") <= 3072;
|
|
1781
|
+
}
|
|
1782
|
+
function resolveFeishuStickerSet(cfg, account) {
|
|
1783
|
+
const sets = (cfg.channels?.feishu)?.stickerSets;
|
|
1784
|
+
const set = sets && account.appId && Object.hasOwn(sets, account.appId) ? sets[account.appId] : void 0;
|
|
1785
|
+
return set ? Object.entries(set) : [];
|
|
1786
|
+
}
|
|
1787
|
+
function searchFeishuStickerSet(entries, params) {
|
|
1788
|
+
const query = readStringParam(params, "query", { required: true });
|
|
1789
|
+
if (!STICKER_QUERY_PATTERN.test(query)) throw new Error("Feishu sticker-search query must contain 1–128 Unicode characters.");
|
|
1790
|
+
const limit = params.limit === void 0 ? 5 : params.limit;
|
|
1791
|
+
if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 10) throw new Error("Feishu sticker-search limit must be an integer from 1 through 10.");
|
|
1792
|
+
const needle = query.toLowerCase();
|
|
1793
|
+
const stickers = [];
|
|
1794
|
+
for (const [fileId, keywords] of entries) {
|
|
1795
|
+
const keyword = keywords.find((label) => label.toLowerCase().includes(needle));
|
|
1796
|
+
if (keyword === void 0) continue;
|
|
1797
|
+
const match = {
|
|
1798
|
+
fileId,
|
|
1799
|
+
keyword
|
|
1800
|
+
};
|
|
1801
|
+
if (stickers.length === limit || !fitsStickerSearchResult([...stickers, match])) return {
|
|
1802
|
+
stickers,
|
|
1803
|
+
truncated: true
|
|
1804
|
+
};
|
|
1805
|
+
stickers.push(match);
|
|
1806
|
+
}
|
|
1807
|
+
return {
|
|
1808
|
+
stickers,
|
|
1809
|
+
truncated: false
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
//#endregion
|
|
2157
1813
|
//#region extensions/feishu/src/channel.ts
|
|
2158
1814
|
function resolveFeishuSendAttachmentMedia(params) {
|
|
2159
1815
|
const sourceKeys = [
|
|
@@ -2244,7 +1900,7 @@ const meta = {
|
|
|
2244
1900
|
order: 70,
|
|
2245
1901
|
preferSessionLookupForAnnounceTarget: true
|
|
2246
1902
|
};
|
|
2247
|
-
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-
|
|
1903
|
+
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CAD74kBO.js"), "feishuChannelRuntime");
|
|
2248
1904
|
async function resolveFeishuMessageSender(params) {
|
|
2249
1905
|
try {
|
|
2250
1906
|
const sender = params.resolve(await loadFeishuChannelRuntime());
|
|
@@ -2308,12 +1964,13 @@ const feishuMessageAdapter = defineChannelMessageAdapter({
|
|
|
2308
1964
|
}
|
|
2309
1965
|
});
|
|
2310
1966
|
async function createFeishuActionClient(account) {
|
|
2311
|
-
const { createFeishuClient } = await import("./client-
|
|
1967
|
+
const { createFeishuClient } = await import("./client-DMbIL3UH.js").then((n) => n.t);
|
|
2312
1968
|
return createFeishuClient(account);
|
|
2313
1969
|
}
|
|
2314
1970
|
async function resolveFeishuChatTypeById(params) {
|
|
2315
1971
|
const client = await createFeishuActionClient(params.account);
|
|
2316
|
-
|
|
1972
|
+
const chat = await params.runtime.getChatInfo(client, params.chatId);
|
|
1973
|
+
return resolveFeishuChatType(chat);
|
|
2317
1974
|
}
|
|
2318
1975
|
async function resolveFeishuMessageChatType(params) {
|
|
2319
1976
|
const knownChatType = normalizeFeishuChatType(params.message.chatType);
|
|
@@ -2371,6 +2028,11 @@ function describeFeishuMessageTool({ cfg, accountId }) {
|
|
|
2371
2028
|
actions.add("reactions");
|
|
2372
2029
|
}
|
|
2373
2030
|
if (enabledAccounts.some((account) => isFeishuActionEnabled(account, "sticker"))) actions.add("sticker");
|
|
2031
|
+
const selectedAccount = resolveFeishuAccount({
|
|
2032
|
+
cfg,
|
|
2033
|
+
accountId
|
|
2034
|
+
});
|
|
2035
|
+
if (isFeishuActionEnabled(selectedAccount, "sticker") && resolveFeishuStickerSet(cfg, selectedAccount).length > 0) actions.add("sticker-search");
|
|
2374
2036
|
return {
|
|
2375
2037
|
actions: Array.from(actions),
|
|
2376
2038
|
capabilities: enabled ? ["presentation"] : []
|
|
@@ -2708,15 +2370,19 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2708
2370
|
edit: true,
|
|
2709
2371
|
reply: true
|
|
2710
2372
|
},
|
|
2711
|
-
agentPrompt: { messageToolHints: ({ cfg, accountId }) =>
|
|
2712
|
-
|
|
2713
|
-
"- Feishu supports interactive cards plus native image, file, audio, and video/media delivery.",
|
|
2714
|
-
"- Feishu supports `send`, `read`, `edit`, `thread-reply`, pins, and channel/member lookup, plus reactions when enabled.",
|
|
2715
|
-
...describeFeishuMessageTool({
|
|
2373
|
+
agentPrompt: { messageToolHints: ({ cfg, accountId }) => {
|
|
2374
|
+
const actions = describeFeishuMessageTool({
|
|
2716
2375
|
cfg,
|
|
2717
2376
|
accountId: accountId ?? void 0
|
|
2718
|
-
}).actions
|
|
2719
|
-
|
|
2377
|
+
}).actions;
|
|
2378
|
+
return [
|
|
2379
|
+
"- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
|
|
2380
|
+
"- Feishu supports interactive cards plus native image, file, audio, and video/media delivery.",
|
|
2381
|
+
"- Feishu supports `send`, `read`, `edit`, `thread-reply`, pins, and channel/member lookup, plus reactions when enabled.",
|
|
2382
|
+
...actions?.includes("sticker") ? ["- Feishu stickers: use `action=sticker` with `fileId` (or the first `stickerId`) from a sticker this bot previously received. Sticker upload is not supported."] : [],
|
|
2383
|
+
...actions?.includes("sticker-search") ? ["- Feishu `action=sticker-search`: configured keyword lookup only, not store search or learned/visual matching. Supply `query` (1–128 characters) and optional `limit` (1–10, default 5); send a returned `fileId` with `action=sticker` on the same account. If `truncated` is true, narrow the query."] : []
|
|
2384
|
+
];
|
|
2385
|
+
} },
|
|
2720
2386
|
groups: { resolveToolPolicy: resolveFeishuGroupToolPolicy },
|
|
2721
2387
|
conversationBindings: {
|
|
2722
2388
|
bindingStore: "adapter",
|
|
@@ -2724,7 +2390,10 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2724
2390
|
buildModelOverrideParentCandidates: ({ parentConversationId }) => buildFeishuModelOverrideParentCandidates(parentConversationId)
|
|
2725
2391
|
},
|
|
2726
2392
|
mentions: { stripPatterns: () => ["<at user_id=\"[^\"]*\">[^<]*</at>"] },
|
|
2727
|
-
reload: {
|
|
2393
|
+
reload: {
|
|
2394
|
+
configPrefixes: ["channels.feishu"],
|
|
2395
|
+
noopPrefixes: ["messages.inbound"]
|
|
2396
|
+
},
|
|
2728
2397
|
doctor: feishuDoctor,
|
|
2729
2398
|
configSchema: FeishuChannelConfigSchema,
|
|
2730
2399
|
config: {
|
|
@@ -2777,8 +2446,13 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
2777
2446
|
accountId: ctx.accountId ?? void 0
|
|
2778
2447
|
});
|
|
2779
2448
|
if ((ctx.action === "react" || ctx.action === "reactions") && !isFeishuActionEnabled(account, "reactions")) throw new Error("Feishu reactions are disabled via actions.reactions.");
|
|
2780
|
-
if (ctx.action === "sticker") {
|
|
2449
|
+
if (ctx.action === "sticker" || ctx.action === "sticker-search") {
|
|
2781
2450
|
if (!isFeishuActionEnabled(account, "sticker")) throw new Error("Feishu stickers are disabled; enable actions.sticker for a configured account.");
|
|
2451
|
+
if (ctx.action === "sticker-search") {
|
|
2452
|
+
const stickers = resolveFeishuStickerSet(ctx.cfg, account);
|
|
2453
|
+
if (stickers.length === 0) throw new Error("Feishu sticker-search requires a nonempty channels.feishu.stickerSets entry for this account's appId.");
|
|
2454
|
+
return jsonActionResult(searchFeishuStickerSet(stickers, ctx.params));
|
|
2455
|
+
}
|
|
2782
2456
|
const to = resolveFeishuActionTarget(ctx);
|
|
2783
2457
|
if (!to) throw new Error("Feishu sticker requires a target (to).");
|
|
2784
2458
|
const fileKey = normalizeFeishuExternalKey(readFirstString(ctx.params, ["fileId"]) ?? (Array.isArray(ctx.params.stickerId) ? ctx.params.stickerId[0] : void 0));
|
|
@@ -3353,7 +3027,7 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
3353
3027
|
})
|
|
3354
3028
|
}),
|
|
3355
3029
|
gateway: { startAccount: async (ctx) => {
|
|
3356
|
-
const { monitorFeishuProvider } = await import("./monitor-
|
|
3030
|
+
const { monitorFeishuProvider } = await import("./monitor-rPJ_Mz4M.js");
|
|
3357
3031
|
const account = resolveFeishuRuntimeAccount({
|
|
3358
3032
|
cfg: ctx.cfg,
|
|
3359
3033
|
accountId: ctx.accountId
|
|
@@ -3400,39 +3074,27 @@ const feishuPlugin = createChatChannelPlugin({
|
|
|
3400
3074
|
});
|
|
3401
3075
|
}
|
|
3402
3076
|
} },
|
|
3403
|
-
threading: {
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3077
|
+
threading: {
|
|
3078
|
+
matchesToolContextTarget: ({ target, toolContext }) => {
|
|
3079
|
+
const normalizedTarget = normalizeFeishuTarget(target);
|
|
3080
|
+
if (!normalizedTarget) return false;
|
|
3081
|
+
return [toolContext.currentChannelId, toolContext.currentMessagingTarget].some((currentTarget) => currentTarget !== void 0 && normalizeFeishuTarget(currentTarget) === normalizedTarget);
|
|
3082
|
+
},
|
|
3083
|
+
buildToolContext: ({ context, hasRepliedRef }) => ({
|
|
3084
|
+
currentChannelId: normalizeOptionalString(context.NativeChannelId) ?? normalizeOptionalString(context.To),
|
|
3085
|
+
currentChatType: context.ChatType === "direct" || context.ChatType === "group" || context.ChatType === "channel" ? context.ChatType : void 0,
|
|
3086
|
+
currentMessagingTarget: normalizeOptionalString(context.To),
|
|
3087
|
+
currentThreadTs: context.MessageThreadId != null ? String(context.MessageThreadId) : void 0,
|
|
3088
|
+
hasRepliedRef
|
|
3089
|
+
})
|
|
3090
|
+
},
|
|
3410
3091
|
outbound: {
|
|
3411
3092
|
deliveryMode: "direct",
|
|
3412
3093
|
chunker: chunkFeishuMarkdown,
|
|
3413
3094
|
chunkerMode: "markdown",
|
|
3414
3095
|
textChunkLimit: 4e3,
|
|
3415
3096
|
sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text),
|
|
3416
|
-
presentationCapabilities:
|
|
3417
|
-
supported: true,
|
|
3418
|
-
buttons: true,
|
|
3419
|
-
selects: false,
|
|
3420
|
-
context: true,
|
|
3421
|
-
divider: true,
|
|
3422
|
-
limits: {
|
|
3423
|
-
actions: {
|
|
3424
|
-
maxActions: 20,
|
|
3425
|
-
maxActionsPerRow: 5,
|
|
3426
|
-
maxLabelLength: 40,
|
|
3427
|
-
maxValueBytes: 1024
|
|
3428
|
-
},
|
|
3429
|
-
text: {
|
|
3430
|
-
maxLength: 4e3,
|
|
3431
|
-
encoding: "characters",
|
|
3432
|
-
markdownDialect: "markdown"
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
},
|
|
3097
|
+
presentationCapabilities: FEISHU_PRESENTATION_CAPABILITIES,
|
|
3436
3098
|
...createRuntimeOutboundDelegates({
|
|
3437
3099
|
getRuntime: loadFeishuChannelRuntime,
|
|
3438
3100
|
renderPresentation: { resolve: (runtime) => runtime.feishuOutbound.renderPresentation },
|