@elizaos/plugin-feishu 2.0.0-alpha.7 → 2.0.0-beta.1

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/index.js CHANGED
@@ -1,3 +1,257 @@
1
+ // src/index.ts
2
+ import { getConnectorAccountManager, logger as logger3 } from "@elizaos/core";
3
+
4
+ // src/accounts.ts
5
+ var DEFAULT_ACCOUNT_ID = "default";
6
+ function normalizeAccountId(accountId) {
7
+ if (!accountId || typeof accountId !== "string") {
8
+ return DEFAULT_ACCOUNT_ID;
9
+ }
10
+ const trimmed = accountId.trim().toLowerCase();
11
+ if (!trimmed || trimmed === "default") {
12
+ return DEFAULT_ACCOUNT_ID;
13
+ }
14
+ return trimmed;
15
+ }
16
+ function getMultiAccountConfig(runtime) {
17
+ const characterFeishu = runtime.character?.settings?.feishu;
18
+ return {
19
+ enabled: characterFeishu?.enabled,
20
+ appId: characterFeishu?.appId,
21
+ appSecret: characterFeishu?.appSecret,
22
+ appSecretFile: characterFeishu?.appSecretFile,
23
+ encryptKey: characterFeishu?.encryptKey,
24
+ verificationToken: characterFeishu?.verificationToken,
25
+ apiUrl: characterFeishu?.apiUrl,
26
+ dmPolicy: characterFeishu?.dmPolicy,
27
+ groupPolicy: characterFeishu?.groupPolicy,
28
+ mediaMaxMb: characterFeishu?.mediaMaxMb,
29
+ textChunkLimit: characterFeishu?.textChunkLimit,
30
+ webhookPath: characterFeishu?.webhookPath,
31
+ accounts: characterFeishu?.accounts,
32
+ groups: characterFeishu?.groups
33
+ };
34
+ }
35
+ function listFeishuAccountIds(runtime) {
36
+ const config = getMultiAccountConfig(runtime);
37
+ const accounts = config.accounts;
38
+ const ids = new Set;
39
+ const envAppId = runtime.getSetting("FEISHU_APP_ID");
40
+ const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
41
+ const baseConfigured = Boolean(config.appId?.trim() && (config.appSecret?.trim() || config.appSecretFile));
42
+ const envConfigured = Boolean(envAppId?.trim() && envAppSecret?.trim());
43
+ if (baseConfigured || envConfigured) {
44
+ ids.add(DEFAULT_ACCOUNT_ID);
45
+ }
46
+ if (accounts && typeof accounts === "object") {
47
+ for (const id of Object.keys(accounts)) {
48
+ if (id) {
49
+ ids.add(normalizeAccountId(id));
50
+ }
51
+ }
52
+ }
53
+ const result = Array.from(ids);
54
+ if (result.length === 0) {
55
+ return [DEFAULT_ACCOUNT_ID];
56
+ }
57
+ return result.toSorted((a, b) => a.localeCompare(b));
58
+ }
59
+ function resolveDefaultFeishuAccountId(runtime) {
60
+ const ids = listFeishuAccountIds(runtime);
61
+ if (ids.includes(DEFAULT_ACCOUNT_ID)) {
62
+ return DEFAULT_ACCOUNT_ID;
63
+ }
64
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
65
+ }
66
+ function getAccountConfig(runtime, accountId) {
67
+ const config = getMultiAccountConfig(runtime);
68
+ const accounts = config.accounts;
69
+ if (!accounts || typeof accounts !== "object") {
70
+ return;
71
+ }
72
+ const direct = accounts[accountId];
73
+ if (direct) {
74
+ return direct;
75
+ }
76
+ const normalized = normalizeAccountId(accountId);
77
+ const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === normalized);
78
+ return matchKey ? accounts[matchKey] : undefined;
79
+ }
80
+ function mergeFeishuAccountConfig(runtime, accountId) {
81
+ const multiConfig = getMultiAccountConfig(runtime);
82
+ const { accounts: _ignored, ...baseConfig } = multiConfig;
83
+ const accountConfig = getAccountConfig(runtime, accountId) ?? {};
84
+ const envAppId = runtime.getSetting("FEISHU_APP_ID");
85
+ const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
86
+ const envEncryptKey = runtime.getSetting("FEISHU_ENCRYPT_KEY");
87
+ const envVerificationToken = runtime.getSetting("FEISHU_VERIFICATION_TOKEN");
88
+ const envDmPolicy = runtime.getSetting("FEISHU_DM_POLICY");
89
+ const envGroupPolicy = runtime.getSetting("FEISHU_GROUP_POLICY");
90
+ const envConfig = {
91
+ appId: envAppId || undefined,
92
+ appSecret: envAppSecret || undefined,
93
+ encryptKey: envEncryptKey || undefined,
94
+ verificationToken: envVerificationToken || undefined,
95
+ dmPolicy: envDmPolicy,
96
+ groupPolicy: envGroupPolicy
97
+ };
98
+ return {
99
+ ...envConfig,
100
+ ...baseConfig,
101
+ ...accountConfig
102
+ };
103
+ }
104
+ function resolveFeishuAccount(runtime, accountId) {
105
+ const normalizedAccountId = normalizeAccountId(accountId);
106
+ const multiConfig = getMultiAccountConfig(runtime);
107
+ const baseEnabled = multiConfig.enabled !== false;
108
+ const merged = mergeFeishuAccountConfig(runtime, normalizedAccountId);
109
+ const accountEnabled = merged.enabled !== false;
110
+ const enabled = baseEnabled && accountEnabled;
111
+ const appId = merged.appId?.trim() || "";
112
+ const appSecret = merged.appSecret?.trim() || "";
113
+ let tokenSource = "none";
114
+ if (merged.appSecret?.trim()) {
115
+ tokenSource = "config";
116
+ } else {
117
+ const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
118
+ if (envAppSecret?.trim()) {
119
+ tokenSource = "env";
120
+ }
121
+ }
122
+ if (!appId || !appSecret) {
123
+ tokenSource = "none";
124
+ }
125
+ const configured = Boolean(appId && appSecret);
126
+ const name = merged.name?.trim() || merged.botName?.trim() || undefined;
127
+ return {
128
+ accountId: normalizedAccountId,
129
+ enabled,
130
+ name,
131
+ appId,
132
+ appSecret,
133
+ tokenSource,
134
+ configured,
135
+ config: merged
136
+ };
137
+ }
138
+ function listEnabledFeishuAccounts(runtime) {
139
+ return listFeishuAccountIds(runtime).map((accountId) => resolveFeishuAccount(runtime, accountId)).filter((account) => account.enabled && account.configured);
140
+ }
141
+ function isMultiAccountEnabled(runtime) {
142
+ const accounts = listEnabledFeishuAccounts(runtime);
143
+ return accounts.length > 1;
144
+ }
145
+ function resolveFeishuGroupConfig(runtime, accountId, groupId) {
146
+ const multiConfig = getMultiAccountConfig(runtime);
147
+ const accountConfig = getAccountConfig(runtime, accountId);
148
+ const accountGroup = accountConfig?.groups?.[groupId];
149
+ if (accountGroup) {
150
+ return accountGroup;
151
+ }
152
+ return multiConfig.groups?.[groupId];
153
+ }
154
+ function isFeishuUserAllowed(params) {
155
+ const { userId, accountConfig, isGroup, groupConfig } = params;
156
+ if (isGroup) {
157
+ const policy2 = accountConfig.groupPolicy ?? "allowlist";
158
+ if (policy2 === "disabled") {
159
+ return false;
160
+ }
161
+ if (policy2 === "open") {
162
+ return true;
163
+ }
164
+ if (groupConfig?.allowFrom?.length) {
165
+ return groupConfig.allowFrom.some((allowed) => String(allowed) === userId);
166
+ }
167
+ if (accountConfig.groupAllowFrom?.length) {
168
+ return accountConfig.groupAllowFrom.some((allowed) => String(allowed) === userId);
169
+ }
170
+ return policy2 !== "allowlist";
171
+ }
172
+ const policy = accountConfig.dmPolicy ?? "pairing";
173
+ if (policy === "disabled") {
174
+ return false;
175
+ }
176
+ if (policy === "open") {
177
+ return true;
178
+ }
179
+ if (policy === "pairing") {
180
+ return true;
181
+ }
182
+ if (accountConfig.allowFrom?.length) {
183
+ return accountConfig.allowFrom.some((allowed) => String(allowed) === userId);
184
+ }
185
+ return false;
186
+ }
187
+ function isFeishuMentionRequired(params) {
188
+ const { groupConfig } = params;
189
+ return groupConfig?.requireMention ?? false;
190
+ }
191
+
192
+ // src/connector-account-provider.ts
193
+ var FEISHU_PROVIDER_ID = "feishu";
194
+ function purposeForAccount(_account) {
195
+ return ["messaging"];
196
+ }
197
+ function accessGateForAccount(account) {
198
+ const dmPolicy = account.config?.dmPolicy;
199
+ if (dmPolicy === "pairing")
200
+ return "pairing";
201
+ if (dmPolicy === "disabled")
202
+ return "disabled";
203
+ return "open";
204
+ }
205
+ function toConnectorAccount(account) {
206
+ const now = Date.now();
207
+ return {
208
+ id: normalizeAccountId(account.accountId),
209
+ provider: FEISHU_PROVIDER_ID,
210
+ label: account.name ?? account.accountId,
211
+ role: "AGENT",
212
+ purpose: purposeForAccount(account),
213
+ accessGate: accessGateForAccount(account),
214
+ status: account.enabled && account.configured ? "connected" : "disabled",
215
+ externalId: account.appId || undefined,
216
+ createdAt: now,
217
+ updatedAt: now,
218
+ metadata: {
219
+ tokenSource: account.tokenSource,
220
+ dmPolicy: account.config?.dmPolicy ?? "open",
221
+ groupPolicy: account.config?.groupPolicy ?? "allowlist",
222
+ appId: account.appId || ""
223
+ }
224
+ };
225
+ }
226
+ function createFeishuConnectorAccountProvider(runtime) {
227
+ return {
228
+ provider: FEISHU_PROVIDER_ID,
229
+ label: "Feishu",
230
+ listAccounts: async (_manager) => {
231
+ const enabled = listEnabledFeishuAccounts(runtime);
232
+ if (enabled.length > 0) {
233
+ return enabled.map(toConnectorAccount);
234
+ }
235
+ const fallback = resolveFeishuAccount(runtime, DEFAULT_ACCOUNT_ID);
236
+ return [toConnectorAccount(fallback)];
237
+ },
238
+ createAccount: async (input, _manager) => {
239
+ return {
240
+ ...input,
241
+ provider: FEISHU_PROVIDER_ID,
242
+ role: input.role ?? "AGENT",
243
+ purpose: input.purpose ?? ["messaging"],
244
+ accessGate: input.accessGate ?? "open",
245
+ status: input.status ?? "pending"
246
+ };
247
+ },
248
+ patchAccount: async (_accountId, patch, _manager) => {
249
+ return { ...patch, provider: FEISHU_PROVIDER_ID };
250
+ },
251
+ deleteAccount: async (_accountId, _manager) => {}
252
+ };
253
+ }
254
+
1
255
  // src/constants.ts
2
256
  var FEISHU_SERVICE_NAME = "feishu";
3
257
  var FEISHU_DOMAINS = {
@@ -7,85 +261,6 @@ var FEISHU_DOMAINS = {
7
261
  var MAX_MESSAGE_LENGTH = 4000;
8
262
  var DEFAULT_TIMEOUT_MS = 30000;
9
263
 
10
- // src/actions/sendMessage.ts
11
- var SEND_MESSAGE_ACTION = "SEND_FEISHU_MESSAGE";
12
- var sendMessageAction = {
13
- name: SEND_MESSAGE_ACTION,
14
- similes: [
15
- "FEISHU_SEND_MESSAGE",
16
- "FEISHU_REPLY",
17
- "FEISHU_MESSAGE",
18
- "SEND_FEISHU",
19
- "REPLY_FEISHU",
20
- "LARK_SEND_MESSAGE",
21
- "LARK_REPLY",
22
- "SEND_LARK"
23
- ],
24
- description: "Send a message to a Feishu/Lark chat",
25
- validate: async (_runtime, message) => {
26
- const textRaw = typeof message.content?.text === "string" ? message.content.text : "";
27
- const text = textRaw.toLowerCase();
28
- const sourceOk = message.content?.source === "feishu";
29
- const keywordOk = text.includes("send") || text.includes("message") || text.includes("reply") || text.includes("feishu") || text.includes("lark") || sourceOk;
30
- const regexOk = /\b(?:send|message|reply|feishu|lark)\b/i.test(textRaw) || sourceOk;
31
- const inputOk = textRaw.trim().length > 0 || Boolean(message.content?.chatId || message.content?.messageId) || sourceOk;
32
- return sourceOk && keywordOk && regexOk && inputOk;
33
- },
34
- handler: async (runtime, message, state, _options, callback) => {
35
- const feishuService = runtime.getService(FEISHU_SERVICE_NAME);
36
- if (!feishuService) {
37
- if (callback) {
38
- await callback({
39
- text: "Feishu service not available"
40
- });
41
- }
42
- return { success: false, error: "Feishu service not initialized" };
43
- }
44
- const currentState = state ?? await runtime.composeState(message);
45
- const responseText = currentState.values?.response?.toString() || "";
46
- const chatId = message.content?.chatId;
47
- if (!chatId) {
48
- if (callback) {
49
- await callback({
50
- text: "No chat ID available"
51
- });
52
- }
53
- return { success: false, error: "Missing chat ID" };
54
- }
55
- if (callback) {
56
- await callback({
57
- text: responseText,
58
- action: SEND_MESSAGE_ACTION
59
- });
60
- }
61
- return {
62
- success: true,
63
- data: {
64
- action: SEND_MESSAGE_ACTION,
65
- chatId,
66
- text: responseText,
67
- replyToMessageId: message.content?.messageId
68
- }
69
- };
70
- },
71
- examples: [
72
- [
73
- {
74
- name: "{{name1}}",
75
- content: {
76
- text: "Send a message to this Feishu chat"
77
- }
78
- },
79
- {
80
- name: "{{agentName}}",
81
- content: {
82
- text: "I'll send a message to this chat now.",
83
- actions: [SEND_MESSAGE_ACTION]
84
- }
85
- }
86
- ]
87
- ]
88
- };
89
264
  // src/messageManager.ts
90
265
  import {
91
266
  ChannelType,
@@ -444,35 +619,6 @@ ${line}` : line;
444
619
  }
445
620
  }
446
621
 
447
- // src/providers/chatState.ts
448
- var CHAT_STATE_PROVIDER = "FEISHU_CHAT_STATE";
449
- var chatStateProvider = {
450
- name: CHAT_STATE_PROVIDER,
451
- description: "Provides Feishu chat context and state information",
452
- dynamic: true,
453
- get: async (_runtime, message, state) => {
454
- if (message.content?.source !== "feishu") {
455
- return { text: "" };
456
- }
457
- const chatId = message.content?.chatId;
458
- const messageId = message.content?.messageId;
459
- if (!chatId) {
460
- return { text: "" };
461
- }
462
- const stateInfo = [`Platform: Feishu/Lark`, `Chat ID: ${chatId}`];
463
- if (messageId) {
464
- stateInfo.push(`Message ID: ${messageId}`);
465
- }
466
- if (state?.values?.feishuChatType) {
467
- stateInfo.push(`Chat Type: ${state.values.feishuChatType}`);
468
- }
469
- if (state?.values?.feishuChatName) {
470
- stateInfo.push(`Chat Name: ${state.values.feishuChatName}`);
471
- }
472
- return { text: stateInfo.join(`
473
- `) };
474
- }
475
- };
476
622
  // src/service.ts
477
623
  import {
478
624
  ChannelType as ChannelType2,
@@ -482,6 +628,70 @@ import {
482
628
  Service
483
629
  } from "@elizaos/core";
484
630
  import * as lark from "@larksuiteoapi/node-sdk";
631
+ function normalizeFeishuQuery(query) {
632
+ return query.trim().toLowerCase();
633
+ }
634
+ function scoreFeishuCandidate(values, query) {
635
+ const normalized = normalizeFeishuQuery(query);
636
+ if (!normalized) {
637
+ return 0.45;
638
+ }
639
+ const candidates = values.filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim().toLowerCase());
640
+ if (candidates.some((candidate) => candidate === normalized)) {
641
+ return 1;
642
+ }
643
+ return candidates.some((candidate) => candidate.includes(normalized)) ? 0.8 : 0;
644
+ }
645
+ function feishuChatToConnectorTarget(chat, score = 0.55, roomId) {
646
+ return {
647
+ target: {
648
+ source: FEISHU_SERVICE_NAME,
649
+ channelId: chat.chatId,
650
+ roomId
651
+ },
652
+ label: chat.name || chat.chatId,
653
+ kind: chat.chatType === "p2p" /* P2P */ ? "user" : "group",
654
+ description: chat.description || "Feishu/Lark chat",
655
+ score,
656
+ contexts: ["social", "connectors"],
657
+ metadata: {
658
+ chatType: chat.chatType,
659
+ ownerOpenId: chat.ownerOpenId,
660
+ tenantKey: chat.tenantKey
661
+ }
662
+ };
663
+ }
664
+ function normalizeConnectorLimit(limit, fallback = 50) {
665
+ if (!Number.isFinite(limit) || !limit || limit <= 0) {
666
+ return fallback;
667
+ }
668
+ return Math.min(Math.floor(limit), 200);
669
+ }
670
+ async function readStoredMessageMemories(runtime, roomId, limit) {
671
+ return runtime.getMemories({
672
+ tableName: "messages",
673
+ roomId,
674
+ limit,
675
+ orderBy: "createdAt",
676
+ orderDirection: "desc"
677
+ });
678
+ }
679
+ async function readStoredMessagesForTargets(runtime, targets, limit) {
680
+ const roomIds = Array.from(new Set(targets.map((target) => target.target.roomId).filter((id) => Boolean(id))));
681
+ const chunks = await Promise.all(roomIds.map((roomId) => readStoredMessageMemories(runtime, roomId, limit)));
682
+ return chunks.flat().sort((left, right) => (right.createdAt ?? 0) - (left.createdAt ?? 0)).slice(0, limit);
683
+ }
684
+ function filterMemoriesByQuery(memories, query, limit) {
685
+ const normalized = query.trim().toLowerCase();
686
+ if (!normalized) {
687
+ return memories.slice(0, limit);
688
+ }
689
+ return memories.filter((memory) => {
690
+ const text = typeof memory.content?.text === "string" ? memory.content.text : "";
691
+ return text.toLowerCase().includes(normalized);
692
+ }).slice(0, limit);
693
+ }
694
+
485
695
  class FeishuService extends Service {
486
696
  static serviceType = FEISHU_SERVICE_NAME;
487
697
  capabilityDescription = "The agent is able to send and receive messages on Feishu/Lark";
@@ -774,8 +984,103 @@ class FeishuService extends Service {
774
984
  }
775
985
  static registerSendHandlers(runtime, serviceInstance) {
776
986
  if (serviceInstance?.client && serviceInstance?.messageManager) {
777
- runtime.registerSendHandler("feishu", serviceInstance.handleSendMessage.bind(serviceInstance));
778
- logger2.info("[Feishu] Registered send handler");
987
+ const sendHandler = async (handlerRuntime, target, content) => {
988
+ await serviceInstance.handleSendMessage(handlerRuntime, target, content);
989
+ return;
990
+ };
991
+ if (typeof runtime.registerMessageConnector === "function") {
992
+ const registration = {
993
+ source: FEISHU_SERVICE_NAME,
994
+ label: "Feishu/Lark",
995
+ capabilities: [
996
+ "send_message",
997
+ "send_card",
998
+ "send_image",
999
+ "send_file"
1000
+ ],
1001
+ supportedTargetKinds: ["group", "room", "user", "channel"],
1002
+ contexts: ["social", "connectors"],
1003
+ description: "Send Feishu/Lark text, card, image, and file messages to known chats.",
1004
+ sendHandler,
1005
+ resolveTargets: async (query, context) => {
1006
+ const chats = await serviceInstance.listConnectorChats(context.runtime);
1007
+ return chats.map(({ chat, roomId }) => ({
1008
+ chat,
1009
+ roomId,
1010
+ score: scoreFeishuCandidate([chat.chatId, chat.name, chat.description, chat.ownerOpenId], query)
1011
+ })).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score).slice(0, 10).map(({ chat, score, roomId }) => feishuChatToConnectorTarget(chat, score, roomId));
1012
+ },
1013
+ listRecentTargets: async (context) => (await serviceInstance.listConnectorChats(context.runtime)).slice(0, 10).map(({ chat, roomId }) => feishuChatToConnectorTarget(chat, 0.55, roomId)),
1014
+ listRooms: async (context) => (await serviceInstance.listConnectorChats(context.runtime)).map(({ chat, roomId }) => feishuChatToConnectorTarget(chat, 0.55, roomId)),
1015
+ fetchMessages: async (context, params) => {
1016
+ const limit = normalizeConnectorLimit(params?.limit);
1017
+ const target = params?.target ?? context.target;
1018
+ if (target?.roomId) {
1019
+ return readStoredMessageMemories(context.runtime, target.roomId, limit);
1020
+ }
1021
+ const targets = (await serviceInstance.listConnectorChats(context.runtime)).slice(0, 10).map(({ chat, roomId }) => feishuChatToConnectorTarget(chat, 0.55, roomId));
1022
+ return readStoredMessagesForTargets(context.runtime, targets, limit);
1023
+ },
1024
+ searchMessages: async (context, params) => {
1025
+ const limit = normalizeConnectorLimit(params?.limit);
1026
+ const target = params?.target ?? context.target;
1027
+ const messages = target?.roomId ? await readStoredMessageMemories(context.runtime, target.roomId, Math.max(limit, 100)) : await readStoredMessagesForTargets(context.runtime, (await serviceInstance.listConnectorChats(context.runtime)).slice(0, 10).map(({ chat, roomId }) => feishuChatToConnectorTarget(chat, 0.55, roomId)), Math.max(limit, 100));
1028
+ return filterMemoriesByQuery(messages, params.query, limit);
1029
+ },
1030
+ getChatContext: async (target, context) => {
1031
+ const room = target.roomId ? await context.runtime.getRoom(target.roomId) : null;
1032
+ const chatId = String(target.channelId ?? room?.channelId ?? "").trim();
1033
+ if (!chatId) {
1034
+ return null;
1035
+ }
1036
+ const chat = serviceInstance.knownChats.get(chatId) || {
1037
+ chatId,
1038
+ chatType: room?.type === ChannelType2.DM ? "p2p" /* P2P */ : "group" /* GROUP */,
1039
+ name: room?.name
1040
+ };
1041
+ return {
1042
+ target: {
1043
+ source: FEISHU_SERVICE_NAME,
1044
+ roomId: target.roomId,
1045
+ channelId: chatId
1046
+ },
1047
+ label: chat.name || chat.chatId,
1048
+ summary: chat.chatType === "p2p" /* P2P */ ? "Feishu/Lark direct chat" : "Feishu/Lark group chat",
1049
+ metadata: {
1050
+ chatType: chat.chatType,
1051
+ ownerOpenId: chat.ownerOpenId,
1052
+ tenantKey: chat.tenantKey
1053
+ }
1054
+ };
1055
+ },
1056
+ getUserContext: async (entityId, context) => {
1057
+ const entity = typeof context.runtime.getEntityById === "function" ? await context.runtime.getEntityById(String(entityId)) : null;
1058
+ if (!entity) {
1059
+ return null;
1060
+ }
1061
+ return {
1062
+ entityId,
1063
+ label: entity.names?.[0],
1064
+ aliases: entity.names,
1065
+ handles: {},
1066
+ metadata: entity.metadata
1067
+ };
1068
+ },
1069
+ getUser: async (handlerRuntime, params) => {
1070
+ const lookupParams = params;
1071
+ const entityId = String(lookupParams.entityId ?? params.userId ?? params.username ?? params.handle ?? params.target?.entityId ?? "").trim();
1072
+ if (!entityId || typeof handlerRuntime.getEntityById !== "function") {
1073
+ return null;
1074
+ }
1075
+ const entity = await handlerRuntime.getEntityById(entityId).catch(() => null);
1076
+ return entity;
1077
+ }
1078
+ };
1079
+ runtime.registerMessageConnector(registration);
1080
+ } else {
1081
+ runtime.registerSendHandler(FEISHU_SERVICE_NAME, sendHandler);
1082
+ }
1083
+ logger2.info("[Feishu] Registered message connector");
779
1084
  } else {
780
1085
  logger2.warn("[Feishu] Cannot register send handler - client not initialized");
781
1086
  }
@@ -804,206 +1109,87 @@ class FeishuService extends Service {
804
1109
  text: content.text || ""
805
1110
  };
806
1111
  const contentData = content.data;
807
- if (contentData?.card) {
808
- feishuContent.card = contentData.card;
1112
+ const feishuData = contentData?.feishu && typeof contentData.feishu === "object" ? contentData.feishu : contentData;
1113
+ if (feishuData?.card) {
1114
+ feishuContent.card = feishuData.card;
809
1115
  }
810
- if (contentData?.imageKey) {
811
- feishuContent.imageKey = contentData.imageKey;
1116
+ if (feishuData?.imageKey) {
1117
+ feishuContent.imageKey = feishuData.imageKey;
812
1118
  }
813
- if (contentData?.fileKey) {
814
- feishuContent.fileKey = contentData.fileKey;
1119
+ if (feishuData?.fileKey) {
1120
+ feishuContent.fileKey = feishuData.fileKey;
815
1121
  }
816
1122
  await this.messageManager.sendMessage(chatId, feishuContent);
817
1123
  logger2.info(`[Feishu] Message sent to chat ID: ${chatId}`);
818
1124
  }
819
- }
820
-
821
- // src/accounts.ts
822
- var DEFAULT_ACCOUNT_ID = "default";
823
- function normalizeAccountId(accountId) {
824
- if (!accountId || typeof accountId !== "string") {
825
- return DEFAULT_ACCOUNT_ID;
826
- }
827
- const trimmed = accountId.trim().toLowerCase();
828
- if (!trimmed || trimmed === "default") {
829
- return DEFAULT_ACCOUNT_ID;
830
- }
831
- return trimmed;
832
- }
833
- function getMultiAccountConfig(runtime) {
834
- const characterFeishu = runtime.character?.settings?.feishu;
835
- return {
836
- enabled: characterFeishu?.enabled,
837
- appId: characterFeishu?.appId,
838
- appSecret: characterFeishu?.appSecret,
839
- appSecretFile: characterFeishu?.appSecretFile,
840
- encryptKey: characterFeishu?.encryptKey,
841
- verificationToken: characterFeishu?.verificationToken,
842
- apiUrl: characterFeishu?.apiUrl,
843
- dmPolicy: characterFeishu?.dmPolicy,
844
- groupPolicy: characterFeishu?.groupPolicy,
845
- mediaMaxMb: characterFeishu?.mediaMaxMb,
846
- textChunkLimit: characterFeishu?.textChunkLimit,
847
- webhookPath: characterFeishu?.webhookPath,
848
- accounts: characterFeishu?.accounts,
849
- groups: characterFeishu?.groups
850
- };
851
- }
852
- function listFeishuAccountIds(runtime) {
853
- const config = getMultiAccountConfig(runtime);
854
- const accounts = config.accounts;
855
- const ids = new Set;
856
- const envAppId = runtime.getSetting("FEISHU_APP_ID");
857
- const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
858
- const baseConfigured = Boolean(config.appId?.trim() && (config.appSecret?.trim() || config.appSecretFile));
859
- const envConfigured = Boolean(envAppId?.trim() && envAppSecret?.trim());
860
- if (baseConfigured || envConfigured) {
861
- ids.add(DEFAULT_ACCOUNT_ID);
862
- }
863
- if (accounts && typeof accounts === "object") {
864
- for (const id of Object.keys(accounts)) {
865
- if (id) {
866
- ids.add(normalizeAccountId(id));
867
- }
868
- }
869
- }
870
- const result = Array.from(ids);
871
- if (result.length === 0) {
872
- return [DEFAULT_ACCOUNT_ID];
873
- }
874
- return result.toSorted((a, b) => a.localeCompare(b));
875
- }
876
- function resolveDefaultFeishuAccountId(runtime) {
877
- const ids = listFeishuAccountIds(runtime);
878
- if (ids.includes(DEFAULT_ACCOUNT_ID)) {
879
- return DEFAULT_ACCOUNT_ID;
880
- }
881
- return ids[0] ?? DEFAULT_ACCOUNT_ID;
882
- }
883
- function getAccountConfig(runtime, accountId) {
884
- const config = getMultiAccountConfig(runtime);
885
- const accounts = config.accounts;
886
- if (!accounts || typeof accounts !== "object") {
887
- return;
888
- }
889
- const direct = accounts[accountId];
890
- if (direct) {
891
- return direct;
892
- }
893
- const normalized = normalizeAccountId(accountId);
894
- const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === normalized);
895
- return matchKey ? accounts[matchKey] : undefined;
896
- }
897
- function mergeFeishuAccountConfig(runtime, accountId) {
898
- const multiConfig = getMultiAccountConfig(runtime);
899
- const { accounts: _ignored, ...baseConfig } = multiConfig;
900
- const accountConfig = getAccountConfig(runtime, accountId) ?? {};
901
- const envAppId = runtime.getSetting("FEISHU_APP_ID");
902
- const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
903
- const envEncryptKey = runtime.getSetting("FEISHU_ENCRYPT_KEY");
904
- const envVerificationToken = runtime.getSetting("FEISHU_VERIFICATION_TOKEN");
905
- const envDmPolicy = runtime.getSetting("FEISHU_DM_POLICY");
906
- const envGroupPolicy = runtime.getSetting("FEISHU_GROUP_POLICY");
907
- const envConfig = {
908
- appId: envAppId || undefined,
909
- appSecret: envAppSecret || undefined,
910
- encryptKey: envEncryptKey || undefined,
911
- verificationToken: envVerificationToken || undefined,
912
- dmPolicy: envDmPolicy,
913
- groupPolicy: envGroupPolicy
914
- };
915
- return {
916
- ...envConfig,
917
- ...baseConfig,
918
- ...accountConfig
919
- };
920
- }
921
- function resolveFeishuAccount(runtime, accountId) {
922
- const normalizedAccountId = normalizeAccountId(accountId);
923
- const multiConfig = getMultiAccountConfig(runtime);
924
- const baseEnabled = multiConfig.enabled !== false;
925
- const merged = mergeFeishuAccountConfig(runtime, normalizedAccountId);
926
- const accountEnabled = merged.enabled !== false;
927
- const enabled = baseEnabled && accountEnabled;
928
- const appId = merged.appId?.trim() || "";
929
- const appSecret = merged.appSecret?.trim() || "";
930
- let tokenSource = "none";
931
- if (merged.appSecret?.trim()) {
932
- tokenSource = "config";
933
- } else {
934
- const envAppSecret = runtime.getSetting("FEISHU_APP_SECRET");
935
- if (envAppSecret?.trim()) {
936
- tokenSource = "env";
937
- }
938
- }
939
- if (!appId || !appSecret) {
940
- tokenSource = "none";
1125
+ async sendRoomMessage(target, content) {
1126
+ await this.handleSendMessage(this.runtime, { source: FEISHU_SERVICE_NAME, channelId: target }, content);
941
1127
  }
942
- const configured = Boolean(appId && appSecret);
943
- const name = merged.name?.trim() || merged.botName?.trim() || undefined;
944
- return {
945
- accountId: normalizedAccountId,
946
- enabled,
947
- name,
948
- appId,
949
- appSecret,
950
- tokenSource,
951
- configured,
952
- config: merged
953
- };
954
- }
955
- function listEnabledFeishuAccounts(runtime) {
956
- return listFeishuAccountIds(runtime).map((accountId) => resolveFeishuAccount(runtime, accountId)).filter((account) => account.enabled && account.configured);
957
- }
958
- function isMultiAccountEnabled(runtime) {
959
- const accounts = listEnabledFeishuAccounts(runtime);
960
- return accounts.length > 1;
961
- }
962
- function resolveFeishuGroupConfig(runtime, accountId, groupId) {
963
- const multiConfig = getMultiAccountConfig(runtime);
964
- const accountConfig = getAccountConfig(runtime, accountId);
965
- const accountGroup = accountConfig?.groups?.[groupId];
966
- if (accountGroup) {
967
- return accountGroup;
1128
+ async sendDirectMessage(target, content) {
1129
+ await this.sendRoomMessage(target, content);
968
1130
  }
969
- return multiConfig.groups?.[groupId];
970
- }
971
- function isFeishuUserAllowed(params) {
972
- const { userId, accountConfig, isGroup, groupConfig } = params;
973
- if (isGroup) {
974
- const policy2 = accountConfig.groupPolicy ?? "allowlist";
975
- if (policy2 === "disabled") {
976
- return false;
1131
+ async listConnectorChats(runtime) {
1132
+ const chats = new Map;
1133
+ for (const chat of this.knownChats.values()) {
1134
+ chats.set(chat.chatId, { chat });
977
1135
  }
978
- if (policy2 === "open") {
979
- return true;
1136
+ if (typeof runtime.getRoomsForParticipant !== "function") {
1137
+ return Array.from(chats.values());
980
1138
  }
981
- if (groupConfig?.allowFrom?.length) {
982
- return groupConfig.allowFrom.some((allowed) => String(allowed) === userId);
983
- }
984
- if (accountConfig.groupAllowFrom?.length) {
985
- return accountConfig.groupAllowFrom.some((allowed) => String(allowed) === userId);
1139
+ const roomIds = await runtime.getRoomsForParticipant(runtime.agentId).catch(() => []);
1140
+ for (const roomId of roomIds) {
1141
+ const room = await runtime.getRoom(roomId).catch(() => null);
1142
+ if (room?.source !== FEISHU_SERVICE_NAME || !room.channelId) {
1143
+ continue;
1144
+ }
1145
+ const known = chats.get(room.channelId)?.chat;
1146
+ chats.set(room.channelId, {
1147
+ chat: known || {
1148
+ chatId: room.channelId,
1149
+ chatType: room.type === ChannelType2.DM ? "p2p" /* P2P */ : "group" /* GROUP */,
1150
+ name: room.name
1151
+ },
1152
+ roomId
1153
+ });
986
1154
  }
987
- return policy2 !== "allowlist";
1155
+ return Array.from(chats.values());
988
1156
  }
989
- const policy = accountConfig.dmPolicy ?? "pairing";
990
- if (policy === "disabled") {
991
- return false;
992
- }
993
- if (policy === "open") {
994
- return true;
995
- }
996
- if (policy === "pairing") {
997
- return true;
1157
+ }
1158
+
1159
+ // src/workflow-credential-provider.ts
1160
+ import { Service as Service2 } from "@elizaos/core";
1161
+ var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
1162
+ var SUPPORTED = ["httpHeaderAuth"];
1163
+
1164
+ class FeishuWorkflowCredentialProvider extends Service2 {
1165
+ static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
1166
+ capabilityDescription = "Supplies Feishu/Lark credentials to the workflow plugin.";
1167
+ static async start(runtime) {
1168
+ return new FeishuWorkflowCredentialProvider(runtime);
1169
+ }
1170
+ async stop() {}
1171
+ async resolve(_userId, credType) {
1172
+ if (credType !== "httpHeaderAuth")
1173
+ return null;
1174
+ const appId = this.runtime.getSetting("FEISHU_APP_ID");
1175
+ const appSecret = this.runtime.getSetting("FEISHU_APP_SECRET");
1176
+ if (!appId?.trim() || !appSecret?.trim())
1177
+ return null;
1178
+ return {
1179
+ status: "credential_data",
1180
+ data: {
1181
+ name: "X-Feishu-App-Id",
1182
+ value: appId.trim(),
1183
+ appSecret: appSecret.trim()
1184
+ }
1185
+ };
998
1186
  }
999
- if (accountConfig.allowFrom?.length) {
1000
- return accountConfig.allowFrom.some((allowed) => String(allowed) === userId);
1187
+ checkCredentialTypes(credTypes) {
1188
+ return {
1189
+ supported: credTypes.filter((t) => SUPPORTED.includes(t)),
1190
+ unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
1191
+ };
1001
1192
  }
1002
- return false;
1003
- }
1004
- function isFeishuMentionRequired(params) {
1005
- const { groupConfig } = params;
1006
- return groupConfig?.requireMention ?? false;
1007
1193
  }
1008
1194
  // src/formatting.ts
1009
1195
  var FEISHU_TEXT_CHUNK_LIMIT = 4000;
@@ -1347,17 +1533,30 @@ function isGroupChat(chatType) {
1347
1533
  var feishuPlugin = {
1348
1534
  name: FEISHU_SERVICE_NAME,
1349
1535
  description: "Feishu/Lark client plugin for elizaOS",
1350
- services: [FeishuService],
1351
- actions: [sendMessageAction],
1352
- providers: [chatStateProvider],
1353
- tests: []
1536
+ services: [FeishuService, FeishuWorkflowCredentialProvider],
1537
+ actions: [],
1538
+ providers: [],
1539
+ tests: [],
1540
+ autoEnable: {
1541
+ connectorKeys: ["feishu"]
1542
+ },
1543
+ init: async (_config, runtime) => {
1544
+ try {
1545
+ const manager = getConnectorAccountManager(runtime);
1546
+ manager.registerProvider(createFeishuConnectorAccountProvider(runtime));
1547
+ } catch (err) {
1548
+ logger3.warn({
1549
+ src: "plugin:feishu",
1550
+ err: err instanceof Error ? err.message : String(err)
1551
+ }, "Failed to register Feishu provider with ConnectorAccountManager");
1552
+ }
1553
+ }
1354
1554
  };
1355
1555
  var src_default = feishuPlugin;
1356
1556
  export {
1357
1557
  validateConfig,
1358
1558
  truncateText,
1359
1559
  stripMarkdown,
1360
- sendMessageAction,
1361
1560
  resolveFeishuSystemLocation,
1362
1561
  resolveFeishuGroupConfig,
1363
1562
  resolveFeishuAccount,
@@ -1378,8 +1577,6 @@ export {
1378
1577
  src_default as default,
1379
1578
  containsMarkdown,
1380
1579
  chunkFeishuText,
1381
- chatStateProvider,
1382
- SEND_MESSAGE_ACTION,
1383
1580
  MessageManager,
1384
1581
  MAX_MESSAGE_LENGTH,
1385
1582
  FeishuService,
@@ -1389,9 +1586,8 @@ export {
1389
1586
  FEISHU_SERVICE_NAME,
1390
1587
  FEISHU_DOMAINS,
1391
1588
  DEFAULT_TIMEOUT_MS,
1392
- DEFAULT_ACCOUNT_ID,
1393
- CHAT_STATE_PROVIDER
1589
+ DEFAULT_ACCOUNT_ID
1394
1590
  };
1395
1591
 
1396
- //# debugId=C5079321E56197B464756E2164756E21
1592
+ //# debugId=A7228C401E12666064756E2164756E21
1397
1593
  //# sourceMappingURL=index.js.map