@xopcai/xopc 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/dist/extensions/feishu/src/adapters/onboard-cli.d.ts +7 -0
  2. package/dist/extensions/feishu/src/adapters/onboard-cli.js +432 -0
  3. package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -0
  4. package/dist/extensions/feishu/src/auth/pairing.d.ts +7 -0
  5. package/dist/extensions/feishu/src/auth/pairing.js +45 -0
  6. package/dist/extensions/feishu/src/auth/pairing.js.map +1 -0
  7. package/dist/extensions/feishu/src/auth/paths.d.ts +2 -0
  8. package/dist/extensions/feishu/src/auth/paths.js +18 -0
  9. package/dist/extensions/feishu/src/auth/paths.js.map +1 -0
  10. package/dist/extensions/feishu/src/directory/directory-adapter.d.ts +2 -0
  11. package/dist/extensions/feishu/src/directory/directory-adapter.js +27 -0
  12. package/dist/extensions/feishu/src/directory/directory-adapter.js.map +1 -0
  13. package/dist/extensions/feishu/src/format.d.ts +21 -0
  14. package/dist/extensions/feishu/src/format.js +99 -0
  15. package/dist/extensions/feishu/src/format.js.map +1 -0
  16. package/dist/extensions/feishu/src/index.d.ts +5 -0
  17. package/dist/extensions/feishu/src/index.js +3 -0
  18. package/dist/extensions/feishu/src/outbound/actions.d.ts +51 -0
  19. package/dist/extensions/feishu/src/outbound/actions.js +62 -0
  20. package/dist/extensions/feishu/src/outbound/actions.js.map +1 -0
  21. package/dist/extensions/feishu/src/outbound/media-load.d.ts +12 -0
  22. package/dist/extensions/feishu/src/outbound/media-load.js +125 -0
  23. package/dist/extensions/feishu/src/outbound/media-load.js.map +1 -0
  24. package/dist/extensions/feishu/src/outbound/outbound-adapter.d.ts +2 -0
  25. package/dist/extensions/feishu/src/outbound/outbound-adapter.js +201 -0
  26. package/dist/extensions/feishu/src/outbound/outbound-adapter.js.map +1 -0
  27. package/dist/extensions/feishu/src/plugin.d.ts +70 -0
  28. package/dist/extensions/feishu/src/plugin.js +313 -0
  29. package/dist/extensions/feishu/src/plugin.js.map +1 -0
  30. package/dist/extensions/feishu/src/schema/config-schema.d.ts +215 -0
  31. package/dist/extensions/feishu/src/schema/config-schema.js +198 -0
  32. package/dist/extensions/feishu/src/schema/config-schema.js.map +1 -0
  33. package/dist/extensions/feishu/src/state/accounts.d.ts +38 -0
  34. package/dist/extensions/feishu/src/state/accounts.js +96 -0
  35. package/dist/extensions/feishu/src/state/accounts.js.map +1 -0
  36. package/dist/extensions/feishu/src/state/message-bindings.d.ts +11 -0
  37. package/dist/extensions/feishu/src/state/message-bindings.js +41 -0
  38. package/dist/extensions/feishu/src/state/message-bindings.js.map +1 -0
  39. package/dist/extensions/feishu/src/state/thread-bindings.js +46 -0
  40. package/dist/extensions/feishu/src/state/thread-bindings.js.map +1 -0
  41. package/dist/extensions/feishu/src/status/doctor.d.ts +2 -0
  42. package/dist/extensions/feishu/src/status/doctor.js +38 -0
  43. package/dist/extensions/feishu/src/status/doctor.js.map +1 -0
  44. package/dist/extensions/feishu/src/status/status-adapter.d.ts +3 -0
  45. package/dist/extensions/feishu/src/status/status-adapter.js +45 -0
  46. package/dist/extensions/feishu/src/status/status-adapter.js.map +1 -0
  47. package/dist/extensions/feishu/src/streaming/streaming-adapter.d.ts +3 -0
  48. package/dist/extensions/feishu/src/streaming/streaming-adapter.js +242 -0
  49. package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -0
  50. package/dist/extensions/feishu/src/subagent-hooks.js +52 -0
  51. package/dist/extensions/feishu/src/subagent-hooks.js.map +1 -0
  52. package/dist/extensions/feishu/src/tools/docx/docx-batch-insert.js +95 -0
  53. package/dist/extensions/feishu/src/tools/docx/docx-batch-insert.js.map +1 -0
  54. package/dist/extensions/feishu/src/tools/docx/docx-color-text.js +75 -0
  55. package/dist/extensions/feishu/src/tools/docx/docx-color-text.js.map +1 -0
  56. package/dist/extensions/feishu/src/tools/docx/docx-table-ops.js +173 -0
  57. package/dist/extensions/feishu/src/tools/docx/docx-table-ops.js.map +1 -0
  58. package/dist/extensions/feishu/src/tools/docx/docx-types.js +1 -0
  59. package/dist/extensions/feishu/src/tools/tools.d.ts +5 -0
  60. package/dist/extensions/feishu/src/tools/tools.js +46 -0
  61. package/dist/extensions/feishu/src/tools/tools.js.map +1 -0
  62. package/dist/extensions/feishu/src/transport/client/client.d.ts +6 -0
  63. package/dist/extensions/feishu/src/transport/client/client.js +41 -0
  64. package/dist/extensions/feishu/src/transport/client/client.js.map +1 -0
  65. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.d.ts +13 -0
  66. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.js +104 -0
  67. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.js.map +1 -0
  68. package/dist/extensions/feishu/src/transport/reliability/dedupe.d.ts +7 -0
  69. package/dist/extensions/feishu/src/transport/reliability/dedupe.js +30 -0
  70. package/dist/extensions/feishu/src/transport/reliability/dedupe.js.map +1 -0
  71. package/dist/extensions/feishu/src/transport/socket-mode/monitor.d.ts +19 -0
  72. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js +326 -0
  73. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js.map +1 -0
  74. package/dist/extensions/feishu/src/transport/socket-mode/retry.d.ts +1 -0
  75. package/dist/extensions/feishu/src/transport/socket-mode/retry.js +10 -0
  76. package/dist/extensions/feishu/src/transport/socket-mode/retry.js.map +1 -0
  77. package/dist/extensions/feishu/src/transport/text/mentions.d.ts +1 -0
  78. package/dist/extensions/feishu/src/transport/text/mentions.js +9 -0
  79. package/dist/extensions/feishu/src/transport/text/mentions.js.map +1 -0
  80. package/dist/extensions/feishu/src/transport/webhook/monitor.d.ts +19 -0
  81. package/dist/extensions/feishu/src/transport/webhook/monitor.js +271 -0
  82. package/dist/extensions/feishu/src/transport/webhook/monitor.js.map +1 -0
  83. package/dist/extensions/feishu/src/ui/config-surface.d.ts +2 -0
  84. package/dist/extensions/feishu/src/ui/config-surface.js +6 -0
  85. package/dist/extensions/feishu/src/ui/config-surface.js.map +1 -0
  86. package/dist/extensions/feishu/xopc.extension.json +18 -0
  87. package/dist/extensions/telegram/xopc.extension.json +20 -0
  88. package/dist/extensions/weixin/xopc.extension.json +17 -0
  89. package/dist/gateway/static/root/assets/{agents-Be8iYqc2.js → agents-DouPaDd1.js} +2 -2
  90. package/dist/gateway/static/root/assets/{agents-Be8iYqc2.js.map → agents-DouPaDd1.js.map} +1 -1
  91. package/dist/gateway/static/root/assets/{apps-page-BLNTewgA.js → apps-page-NiD-_oC4.js} +2 -2
  92. package/dist/gateway/static/root/assets/{apps-page-BLNTewgA.js.map → apps-page-NiD-_oC4.js.map} +1 -1
  93. package/dist/gateway/static/root/assets/channels-settings-BdNb4uD6.js +9 -0
  94. package/dist/gateway/static/root/assets/channels-settings-BdNb4uD6.js.map +1 -0
  95. package/dist/gateway/static/root/assets/{cron-page-HoSnHbPX.js → cron-page-VjRx7Jtc.js} +2 -2
  96. package/dist/gateway/static/root/assets/{cron-page-HoSnHbPX.js.map → cron-page-VjRx7Jtc.js.map} +1 -1
  97. package/dist/gateway/static/root/assets/{cron-utils-D1mrWEee.js → cron-utils-CMkZdWey.js} +2 -2
  98. package/dist/gateway/static/root/assets/{cron-utils-D1mrWEee.js.map → cron-utils-CMkZdWey.js.map} +1 -1
  99. package/dist/gateway/static/root/assets/{dist-CowQhLuH.js → dist-D-p_Xi_b.js} +2 -2
  100. package/dist/gateway/static/root/assets/{dist-CowQhLuH.js.map → dist-D-p_Xi_b.js.map} +1 -1
  101. package/dist/gateway/static/root/assets/{extension-debug-page-bjoNo6JH.js → extension-debug-page-B5jGnQgG.js} +2 -2
  102. package/dist/gateway/static/root/assets/{extension-debug-page-bjoNo6JH.js.map → extension-debug-page-B5jGnQgG.js.map} +1 -1
  103. package/dist/gateway/static/root/assets/{extension-page-NU-MTrtq.js → extension-page-CSA88lpx.js} +2 -2
  104. package/dist/gateway/static/root/assets/{extension-page-NU-MTrtq.js.map → extension-page-CSA88lpx.js.map} +1 -1
  105. package/dist/gateway/static/root/assets/{extension-settings-page-_kAL2Nt-.js → extension-settings-page-C9HfcH28.js} +2 -2
  106. package/dist/gateway/static/root/assets/{extension-settings-page-_kAL2Nt-.js.map → extension-settings-page-C9HfcH28.js.map} +1 -1
  107. package/dist/gateway/static/root/assets/index-BQNdJlkw.css +1 -0
  108. package/dist/gateway/static/root/assets/index-C5pFj5w6.js +144 -0
  109. package/dist/gateway/static/root/assets/{index-DoudkP0H.js.map → index-C5pFj5w6.js.map} +1 -1
  110. package/dist/gateway/static/root/assets/{logs-page-BYPAmUPJ.js → logs-page-CrJ87O5K.js} +2 -2
  111. package/dist/gateway/static/root/assets/{logs-page-BYPAmUPJ.js.map → logs-page-CrJ87O5K.js.map} +1 -1
  112. package/dist/gateway/static/root/assets/sessions-page-DJfnuATX.js +2 -0
  113. package/dist/gateway/static/root/assets/sessions-page-DJfnuATX.js.map +1 -0
  114. package/dist/gateway/static/root/assets/{settings-page-BWE5R4rm.js → settings-page-CBcPUTF-.js} +2 -2
  115. package/dist/gateway/static/root/assets/{settings-page-BWE5R4rm.js.map → settings-page-CBcPUTF-.js.map} +1 -1
  116. package/dist/gateway/static/root/assets/{skills-page-CA2NcCUa.js → skills-page-DVSS_5id.js} +2 -2
  117. package/dist/gateway/static/root/assets/{skills-page-CA2NcCUa.js.map → skills-page-DVSS_5id.js.map} +1 -1
  118. package/dist/gateway/static/root/channel-icons/feishu.svg +12 -0
  119. package/dist/gateway/static/root/channel-icons/lark.svg +12 -0
  120. package/dist/gateway/static/root/channel-icons/telegram.svg +1 -0
  121. package/dist/gateway/static/root/channel-icons/wechat.svg +1 -0
  122. package/dist/gateway/static/root/channel-icons/weixin.svg +1 -0
  123. package/dist/gateway/static/root/index.html +2 -2
  124. package/dist/package.js +1 -1
  125. package/dist/src/agent/agent-manager.d.ts +1 -0
  126. package/dist/src/agent/agent-manager.js +1 -0
  127. package/dist/src/agent/agent-manager.js.map +1 -1
  128. package/dist/src/agent/service.js +1 -0
  129. package/dist/src/agent/service.js.map +1 -1
  130. package/dist/src/agent/tools/delegate-tool.d.ts +8 -0
  131. package/dist/src/agent/tools/delegate-tool.js +60 -2
  132. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  133. package/dist/src/agent/tools/factory.d.ts +1 -0
  134. package/dist/src/agent/tools/factory.js +2 -0
  135. package/dist/src/agent/tools/factory.js.map +1 -1
  136. package/dist/src/channels/envelope-timestamp.d.ts +5 -0
  137. package/dist/src/channels/envelope-timestamp.js +10 -1
  138. package/dist/src/channels/envelope-timestamp.js.map +1 -1
  139. package/dist/src/channels/feishu/index.d.ts +5 -0
  140. package/dist/src/channels/feishu/index.js +4 -0
  141. package/dist/src/chat-commands/types.d.ts +1 -1
  142. package/dist/src/extensions/types/hooks.d.ts +46 -1
  143. package/dist/src/extensions/types/hooks.js +3 -0
  144. package/dist/src/extensions/types/hooks.js.map +1 -1
  145. package/dist/src/gateway/service.js +1 -1
  146. package/dist/src/generated/bundled-channel-plugins.d.ts +2 -1
  147. package/dist/src/generated/bundled-channel-plugins.js +8 -2
  148. package/dist/src/generated/bundled-channel-plugins.js.map +1 -1
  149. package/dist/src/session/session-title.js +2 -1
  150. package/dist/src/session/session-title.js.map +1 -1
  151. package/package.json +2 -2
  152. package/dist/gateway/static/root/assets/channels-settings-p-9taPc_.js +0 -9
  153. package/dist/gateway/static/root/assets/channels-settings-p-9taPc_.js.map +0 -1
  154. package/dist/gateway/static/root/assets/index-CbNEU6bw.css +0 -1
  155. package/dist/gateway/static/root/assets/index-DoudkP0H.js +0 -144
  156. package/dist/gateway/static/root/assets/sessions-page-cNZK0pkU.js +0 -2
  157. package/dist/gateway/static/root/assets/sessions-page-cNZK0pkU.js.map +0 -1
@@ -0,0 +1,326 @@
1
+ import { createLogger } from "../../../../../src/utils/logger/index.js";
2
+ import { init_logger } from "../../../../../src/utils/logger.js";
3
+ import { generateSessionKey } from "../../../../../src/chat-commands/session-key.js";
4
+ import { createFeishuClient } from "../client/client.js";
5
+ import { createFeishuDedupe } from "../reliability/dedupe.js";
6
+ import { stripFeishuMentions } from "../text/mentions.js";
7
+ import { computeBackoffMs } from "./retry.js";
8
+ import { getFeishuBindingByMessageId, recordFeishuMessageBinding } from "../../state/message-bindings.js";
9
+ //#region extensions/feishu/src/transport/socket-mode/monitor.ts
10
+ init_logger();
11
+ const log = createLogger("FeishuSocketMode");
12
+ function createFeishuSocketModeMonitor(deps) {
13
+ const { account, config: _config, bus, abortSignal, security } = deps;
14
+ const dedupe = createFeishuDedupe();
15
+ let lastEventAt = 0;
16
+ async function handleReactionEvent(kind, event, api) {
17
+ const msgId = event?.event?.message_id ?? event?.message_id ?? "";
18
+ const emojiType = event?.event?.reaction_type?.emoji_type ?? event?.reaction_type?.emoji_type ?? "";
19
+ const operatorType = event?.event?.operator_type ?? event?.operator_type ?? "";
20
+ const userOpenId = event?.event?.user_id?.open_id ?? event?.user_id?.open_id ?? event?.event?.open_id ?? event?.open_id ?? "";
21
+ if (!msgId || !emojiType) return;
22
+ if (operatorType === "app") return;
23
+ const notifMode = account.reactionNotifications ?? "own";
24
+ if (notifMode === "off") return;
25
+ const binding = getFeishuBindingByMessageId(msgId);
26
+ if (!binding) return;
27
+ const reacted = await fetchFeishuMessageForReaction(api, msgId, 1500);
28
+ if (!reacted && notifMode === "own") return;
29
+ const isBotMessage = reacted?.sender_type === "app" || reacted?.senderType === "app";
30
+ if (notifMode === "own" && !isBotMessage) return;
31
+ const senderId = userOpenId || binding.senderId;
32
+ const preview = reacted ? extractFeishuMessagePreview(reacted) : "";
33
+ await bus.publishInbound({
34
+ channel: "feishu",
35
+ sender_id: senderId,
36
+ chat_id: binding.chatId,
37
+ content: kind === "deleted" ? `撤回了一个表情(${emojiType})${preview ? `,对应消息:「${preview}」` : ""}` : `对一条消息添加了表情(${emojiType})${preview ? `:「${preview}」` : ""}`,
38
+ metadata: {
39
+ sessionKey: binding.sessionKey,
40
+ accountId: binding.accountId,
41
+ isGroup: binding.isGroup,
42
+ threadId: binding.threadId,
43
+ feishuEventType: `im.message.reaction.${kind}_v1`,
44
+ reactedMessageId: msgId,
45
+ emojiType,
46
+ raw: event,
47
+ reactedMessage: reacted ?? null
48
+ }
49
+ });
50
+ }
51
+ async function handleCardAction(event) {
52
+ const ctx = event?.event?.context ?? event?.context ?? {};
53
+ const operator = event?.event?.operator ?? event?.operator ?? {};
54
+ const action = event?.event?.action ?? event?.action ?? {};
55
+ const chatId = (ctx?.chat_id ?? "").trim();
56
+ const senderId = (operator?.open_id ?? "").trim();
57
+ const openMessageId = (ctx?.open_message_id ?? "").trim();
58
+ const binding = openMessageId ? getFeishuBindingByMessageId(openMessageId) : null;
59
+ const isGroup = chatId.startsWith("oc_");
60
+ const sessionKey = binding?.sessionKey ?? generateSessionKey({
61
+ source: "feishu",
62
+ chatId: chatId || senderId,
63
+ senderId: senderId || "unknown",
64
+ isGroup: Boolean(chatId) && isGroup,
65
+ accountId: account.accountId
66
+ });
67
+ const extracted = extractCardActionCommand(action);
68
+ const content = extracted?.trim() || `[card action: ${String(action?.tag ?? "unknown")}]`;
69
+ await bus.publishInbound({
70
+ channel: "feishu",
71
+ sender_id: senderId || "unknown",
72
+ chat_id: chatId || senderId || "unknown",
73
+ content,
74
+ metadata: {
75
+ sessionKey,
76
+ accountId: account.accountId,
77
+ isGroup: Boolean(chatId) && isGroup,
78
+ feishuEventType: "card.action.trigger",
79
+ cardActionText: extracted ?? null,
80
+ cardAction: action,
81
+ cardContext: ctx,
82
+ raw: event
83
+ }
84
+ });
85
+ }
86
+ async function handleMessageRecalled(event) {
87
+ const e = event?.event ?? event ?? {};
88
+ const recalledMessageId = (e?.message_id ?? "").trim();
89
+ const chatId = (e?.chat_id ?? "").trim();
90
+ if (!recalledMessageId) return;
91
+ const binding = getFeishuBindingByMessageId(recalledMessageId);
92
+ if (!binding && !chatId) return;
93
+ const isGroup = (binding?.isGroup ?? chatId.startsWith("oc_")) === true;
94
+ const sessionKey = binding?.sessionKey ?? generateSessionKey({
95
+ source: "feishu",
96
+ chatId: chatId || "unknown",
97
+ senderId: binding?.senderId ?? "unknown",
98
+ isGroup,
99
+ accountId: account.accountId
100
+ });
101
+ await bus.publishInbound({
102
+ channel: "feishu",
103
+ sender_id: binding?.senderId ?? "system",
104
+ chat_id: chatId || binding?.chatId || "unknown",
105
+ content: `撤回了一条消息(${recalledMessageId})`,
106
+ metadata: {
107
+ sessionKey,
108
+ accountId: account.accountId,
109
+ isGroup,
110
+ threadId: binding?.threadId,
111
+ feishuEventType: "im.message.recalled_v1",
112
+ recalledMessageId,
113
+ recallTime: e?.recall_time,
114
+ recallType: e?.recall_type,
115
+ raw: event
116
+ }
117
+ });
118
+ }
119
+ async function handleMessageReceive(event) {
120
+ lastEventAt = Date.now();
121
+ const msg = event?.event?.message ?? event?.message ?? event?.data?.message;
122
+ const sender = event?.event?.sender ?? event?.sender ?? event?.data?.sender;
123
+ const chatId = msg?.chat_id ?? msg?.chatId ?? "";
124
+ const messageId = msg?.message_id ?? msg?.messageId ?? "";
125
+ const chatType = msg?.chat_type ?? msg?.chatType ?? "";
126
+ const senderId = sender?.sender_id?.open_id ?? sender?.sender_id?.user_id ?? sender?.open_id ?? sender?.user_id ?? "";
127
+ const senderName = sender?.sender_id?.name ?? sender?.sender_name ?? void 0;
128
+ if (!chatId || !messageId || !senderId) {
129
+ log.warn({ accountId: account.accountId }, "Feishu inbound missing ids; dropping");
130
+ return;
131
+ }
132
+ if (!dedupe.claim(messageId)) return;
133
+ const isGroup = chatType === "group" || chatType === "chat";
134
+ const text = msg?.content && typeof msg.content === "string" ? safeJsonText(msg.content) : safeJsonText(msg?.body?.content);
135
+ const normalizedText = stripFeishuMentions(text ?? "").trim();
136
+ if (!normalizedText) return;
137
+ const threadId = msg?.thread_id ?? msg?.threadId ?? void 0;
138
+ const sessionKey = generateSessionKey({
139
+ source: "feishu",
140
+ chatId,
141
+ senderId,
142
+ isGroup,
143
+ threadId: typeof threadId === "string" && threadId.trim() ? threadId : void 0,
144
+ accountId: account.accountId
145
+ });
146
+ recordFeishuMessageBinding({
147
+ messageId,
148
+ sessionKey,
149
+ accountId: account.accountId,
150
+ chatId,
151
+ senderId,
152
+ isGroup,
153
+ threadId: typeof threadId === "string" && threadId.trim() ? threadId : void 0
154
+ });
155
+ const securityCtx = {
156
+ accountId: account.accountId,
157
+ chatId,
158
+ senderId,
159
+ senderName,
160
+ isGroup,
161
+ threadId
162
+ };
163
+ const access = security.checkAccess(securityCtx);
164
+ if (access && !access.allowed) {
165
+ log.warn({
166
+ accountId: account.accountId,
167
+ chatId,
168
+ senderId,
169
+ reason: access.reason
170
+ }, "Feishu: message dropped by channel security");
171
+ return;
172
+ }
173
+ if (isGroup && account.requireMention) {
174
+ const rawText = text ?? "";
175
+ if (!rawText.includes("<at ") && !rawText.includes("@")) return;
176
+ }
177
+ await bus.publishInbound({
178
+ channel: "feishu",
179
+ sender_id: senderId,
180
+ chat_id: chatId,
181
+ content: normalizedText,
182
+ metadata: {
183
+ sessionKey,
184
+ accountId: account.accountId,
185
+ messageId,
186
+ threadId,
187
+ isGroup,
188
+ raw: event
189
+ }
190
+ });
191
+ }
192
+ async function run() {
193
+ let attempt = 0;
194
+ const heartbeatTimer = setInterval(() => {
195
+ if (abortSignal.aborted) return;
196
+ if (lastEventAt === 0) return;
197
+ const silentForMs = Date.now() - lastEventAt;
198
+ if (silentForMs > 5 * 6e4) log.warn({
199
+ accountId: account.accountId,
200
+ silentForMs
201
+ }, "Feishu socket mode: no events recently");
202
+ }, 6e4);
203
+ try {
204
+ while (!abortSignal.aborted) {
205
+ attempt++;
206
+ const { wsClient, dispatcher, api } = createFeishuClient(account);
207
+ dispatcher.register({
208
+ "im.message.receive_v1": async (data) => {
209
+ await handleMessageReceive(data);
210
+ },
211
+ "im.message.reaction.created_v1": async (data) => {
212
+ await handleReactionEvent("created", data, api);
213
+ },
214
+ "im.message.reaction.deleted_v1": async (data) => {
215
+ await handleReactionEvent("deleted", data, api);
216
+ },
217
+ "im.message.recalled_v1": async (data) => {
218
+ await handleMessageRecalled(data);
219
+ },
220
+ "card.action.trigger": async (data) => {
221
+ await handleCardAction(data);
222
+ }
223
+ });
224
+ try {
225
+ wsClient.start({ eventDispatcher: dispatcher });
226
+ } catch (err) {
227
+ const delayMs = computeBackoffMs(attempt);
228
+ log.error({
229
+ err,
230
+ accountId: account.accountId,
231
+ delayMs
232
+ }, "Feishu socket mode start failed; retrying");
233
+ await sleep(delayMs, abortSignal);
234
+ continue;
235
+ }
236
+ const exited = await new Promise((resolve) => {
237
+ const onAbort = () => resolve({ ok: true });
238
+ if (abortSignal.aborted) return resolve({ ok: true });
239
+ abortSignal.addEventListener("abort", onAbort, { once: true });
240
+ wsClient?.on?.("error", (err) => resolve({
241
+ ok: false,
242
+ err
243
+ }));
244
+ wsClient?.on?.("close", () => resolve({
245
+ ok: false,
246
+ err: /* @__PURE__ */ new Error("ws closed")
247
+ }));
248
+ });
249
+ try {
250
+ wsClient.stop();
251
+ } catch {}
252
+ if (abortSignal.aborted) return;
253
+ const delayMs = computeBackoffMs(attempt);
254
+ log.warn({
255
+ accountId: account.accountId,
256
+ delayMs,
257
+ err: exited.err ? String(exited.err) : void 0
258
+ }, "Feishu socket mode disconnected; restarting");
259
+ await sleep(delayMs, abortSignal);
260
+ }
261
+ } finally {
262
+ clearInterval(heartbeatTimer);
263
+ }
264
+ }
265
+ return { run };
266
+ }
267
+ async function fetchFeishuMessageForReaction(api, messageId, timeoutMs) {
268
+ const t = messageId.trim();
269
+ if (!t) return null;
270
+ try {
271
+ const result = await Promise.race([api.im.message.get({ path: { message_id: t } }), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("timeout")), timeoutMs))]);
272
+ return result?.data ?? result;
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+ function extractFeishuMessagePreview(msg) {
278
+ const raw = msg?.body?.content ?? msg?.content ?? msg?.message?.content;
279
+ if (typeof raw !== "string" || !raw.trim()) return "";
280
+ try {
281
+ const parsed = JSON.parse(raw);
282
+ const t = typeof parsed?.text === "string" ? parsed.text : typeof parsed?.content === "string" ? parsed.content : "";
283
+ return summarizeText(String(t || ""), 80);
284
+ } catch {
285
+ return summarizeText(raw, 80);
286
+ }
287
+ }
288
+ function summarizeText(text, max) {
289
+ const s = text.replace(/\s+/g, " ").trim();
290
+ if (s.length <= max) return s;
291
+ return s.slice(0, max - 1) + "…";
292
+ }
293
+ function safeJsonText(raw) {
294
+ if (typeof raw !== "string" || !raw.trim()) return void 0;
295
+ try {
296
+ const parsed = JSON.parse(raw);
297
+ const t = typeof parsed?.text === "string" ? parsed.text : typeof parsed?.content === "string" ? parsed.content : typeof parsed?.title === "string" ? parsed.title : void 0;
298
+ return typeof t === "string" ? t : raw;
299
+ } catch {
300
+ return raw;
301
+ }
302
+ }
303
+ function extractCardActionCommand(action) {
304
+ const value = action?.value;
305
+ if (!value || typeof value !== "object") return null;
306
+ const v = value;
307
+ const command = typeof v.command === "string" ? v.command.trim() : "";
308
+ if (command) return command;
309
+ const text = typeof v.text === "string" ? v.text.trim() : "";
310
+ if (text) return text;
311
+ return null;
312
+ }
313
+ function sleep(ms, signal) {
314
+ return new Promise((resolve) => {
315
+ if (signal.aborted) return resolve();
316
+ const t = setTimeout(resolve, ms);
317
+ signal.addEventListener("abort", () => {
318
+ clearTimeout(t);
319
+ resolve();
320
+ }, { once: true });
321
+ });
322
+ }
323
+ //#endregion
324
+ export { createFeishuSocketModeMonitor };
325
+
326
+ //# sourceMappingURL=monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.js","names":[],"sources":["../../../../../../extensions/feishu/src/transport/socket-mode/monitor.ts"],"sourcesContent":["import type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\nimport type { ChannelSecurityContext } from '@xopcai/xopc/channels/plugin-types.js';\nimport { generateSessionKey } from '@xopcai/xopc/chat-commands/session-key.js';\nimport { createLogger } from '@xopcai/xopc/utils/logger.js';\n\nimport type { ResolvedFeishuAccount } from '../../state/accounts.js';\nimport { createFeishuClient } from '../client/client.js';\nimport { createFeishuDedupe } from '../reliability/dedupe.js';\nimport { stripFeishuMentions } from '../text/mentions.js';\nimport { computeBackoffMs } from './retry.js';\nimport { getFeishuBindingByMessageId, recordFeishuMessageBinding } from '../../state/message-bindings.js';\n\nconst log = createLogger('FeishuSocketMode');\n\nexport interface FeishuSocketModeMonitorDeps {\n account: ResolvedFeishuAccount;\n config: Config;\n bus: MessageBus;\n abortSignal: AbortSignal;\n security: {\n checkAccess: (ctx: ChannelSecurityContext) => { allowed: boolean; reason?: string } | undefined;\n };\n}\n\nexport function createFeishuSocketModeMonitor(deps: FeishuSocketModeMonitorDeps) {\n const { account, config: _config, bus, abortSignal, security } = deps;\n const dedupe = createFeishuDedupe();\n let lastEventAt = 0;\n\n async function handleReactionEvent(kind: 'created' | 'deleted', event: any, api: any): Promise<void> {\n const msgId = event?.event?.message_id ?? event?.message_id ?? '';\n const emojiType = event?.event?.reaction_type?.emoji_type ?? event?.reaction_type?.emoji_type ?? '';\n const operatorType = event?.event?.operator_type ?? event?.operator_type ?? '';\n const userOpenId =\n event?.event?.user_id?.open_id ?? event?.user_id?.open_id ?? event?.event?.open_id ?? event?.open_id ?? '';\n if (!msgId || !emojiType) return;\n if (operatorType === 'app') return;\n\n const notifMode = account.reactionNotifications ?? 'own';\n if (notifMode === 'off') {\n return;\n }\n\n const binding = getFeishuBindingByMessageId(msgId);\n if (!binding) return;\n\n const reacted = await fetchFeishuMessageForReaction(api, msgId, 1500);\n if (!reacted && notifMode === 'own') {\n // In own-mode, we must be sure this is a bot message.\n return;\n }\n\n const isBotMessage = reacted?.sender_type === 'app' || reacted?.senderType === 'app';\n if (notifMode === 'own' && !isBotMessage) {\n return;\n }\n\n const senderId = userOpenId || binding.senderId;\n const preview = reacted ? extractFeishuMessagePreview(reacted) : '';\n\n await bus.publishInbound({\n channel: 'feishu',\n sender_id: senderId,\n chat_id: binding.chatId,\n content:\n kind === 'deleted'\n ? `撤回了一个表情(${emojiType})${preview ? `,对应消息:「${preview}」` : ''}`\n : `对一条消息添加了表情(${emojiType})${preview ? `:「${preview}」` : ''}`,\n metadata: {\n sessionKey: binding.sessionKey,\n accountId: binding.accountId,\n isGroup: binding.isGroup,\n threadId: binding.threadId,\n feishuEventType: `im.message.reaction.${kind}_v1`,\n reactedMessageId: msgId,\n emojiType,\n raw: event,\n reactedMessage: reacted ?? null,\n },\n });\n }\n\n async function handleCardAction(event: any): Promise<void> {\n const ctx = event?.event?.context ?? event?.context ?? {};\n const operator = event?.event?.operator ?? event?.operator ?? {};\n const action = event?.event?.action ?? event?.action ?? {};\n const chatId = (ctx?.chat_id ?? '').trim();\n const senderId = (operator?.open_id ?? '').trim();\n const openMessageId = (ctx?.open_message_id ?? '').trim();\n const binding = openMessageId ? getFeishuBindingByMessageId(openMessageId) : null;\n\n const isGroup = chatId.startsWith('oc_');\n const sessionKey =\n binding?.sessionKey ??\n generateSessionKey({\n source: 'feishu',\n chatId: chatId || senderId,\n senderId: senderId || 'unknown',\n isGroup: Boolean(chatId) && isGroup,\n accountId: account.accountId,\n });\n\n const extracted = extractCardActionCommand(action);\n const content =\n extracted?.trim() ||\n `[card action: ${String(action?.tag ?? 'unknown')}]`;\n\n await bus.publishInbound({\n channel: 'feishu',\n sender_id: senderId || 'unknown',\n chat_id: chatId || senderId || 'unknown',\n content,\n metadata: {\n sessionKey,\n accountId: account.accountId,\n isGroup: Boolean(chatId) && isGroup,\n feishuEventType: 'card.action.trigger',\n cardActionText: extracted ?? null,\n cardAction: action,\n cardContext: ctx,\n raw: event,\n },\n });\n }\n\n async function handleMessageRecalled(event: any): Promise<void> {\n const e = event?.event ?? event ?? {};\n const recalledMessageId = (e?.message_id ?? '').trim();\n const chatId = (e?.chat_id ?? '').trim();\n if (!recalledMessageId) return;\n\n const binding = getFeishuBindingByMessageId(recalledMessageId);\n if (!binding && !chatId) return;\n\n const isGroup = (binding?.isGroup ?? chatId.startsWith('oc_')) === true;\n const sessionKey =\n binding?.sessionKey ??\n generateSessionKey({\n source: 'feishu',\n chatId: chatId || 'unknown',\n senderId: binding?.senderId ?? 'unknown',\n isGroup,\n accountId: account.accountId,\n });\n\n await bus.publishInbound({\n channel: 'feishu',\n sender_id: binding?.senderId ?? 'system',\n chat_id: chatId || binding?.chatId || 'unknown',\n content: `撤回了一条消息(${recalledMessageId})`,\n metadata: {\n sessionKey,\n accountId: account.accountId,\n isGroup,\n threadId: binding?.threadId,\n feishuEventType: 'im.message.recalled_v1',\n recalledMessageId,\n recallTime: e?.recall_time,\n recallType: e?.recall_type,\n raw: event,\n },\n });\n }\n\n async function handleMessageReceive(event: any): Promise<void> {\n lastEventAt = Date.now();\n const msg = event?.event?.message ?? event?.message ?? event?.data?.message;\n const sender = event?.event?.sender ?? event?.sender ?? event?.data?.sender;\n const chatId = msg?.chat_id ?? msg?.chatId ?? '';\n const messageId = msg?.message_id ?? msg?.messageId ?? '';\n const chatType = msg?.chat_type ?? msg?.chatType ?? '';\n\n const senderId =\n sender?.sender_id?.open_id ??\n sender?.sender_id?.user_id ??\n sender?.open_id ??\n sender?.user_id ??\n '';\n const senderName = sender?.sender_id?.name ?? sender?.sender_name ?? undefined;\n\n if (!chatId || !messageId || !senderId) {\n log.warn({ accountId: account.accountId }, 'Feishu inbound missing ids; dropping');\n return;\n }\n\n if (!dedupe.claim(messageId)) {\n return;\n }\n\n const isGroup = chatType === 'group' || chatType === 'chat';\n const text =\n msg?.content && typeof msg.content === 'string'\n ? safeJsonText(msg.content)\n : safeJsonText(msg?.body?.content);\n\n const normalizedText = stripFeishuMentions(text ?? '').trim();\n if (!normalizedText) {\n return;\n }\n\n const threadId = msg?.thread_id ?? msg?.threadId ?? undefined;\n\n const sessionKey = generateSessionKey({\n source: 'feishu',\n chatId,\n senderId,\n isGroup,\n threadId: typeof threadId === 'string' && threadId.trim() ? threadId : undefined,\n accountId: account.accountId,\n });\n\n recordFeishuMessageBinding({\n messageId,\n sessionKey,\n accountId: account.accountId,\n chatId,\n senderId,\n isGroup,\n threadId: typeof threadId === 'string' && threadId.trim() ? threadId : undefined,\n });\n\n const securityCtx: ChannelSecurityContext = {\n accountId: account.accountId,\n chatId,\n senderId,\n senderName,\n isGroup,\n threadId,\n };\n\n const access = security.checkAccess(securityCtx);\n if (access && !access.allowed) {\n log.warn(\n { accountId: account.accountId, chatId, senderId, reason: access.reason },\n 'Feishu: message dropped by channel security',\n );\n return;\n }\n\n if (isGroup && account.requireMention) {\n const rawText = text ?? '';\n // Minimal mention gate: require at-tag markup. Later we tighten to bot-identity mention.\n if (!rawText.includes('<at ') && !rawText.includes('@')) {\n return;\n }\n }\n\n await bus.publishInbound({\n channel: 'feishu',\n sender_id: senderId,\n chat_id: chatId,\n content: normalizedText,\n metadata: {\n sessionKey,\n accountId: account.accountId,\n messageId,\n threadId,\n isGroup,\n raw: event,\n },\n });\n }\n\n async function run(): Promise<void> {\n let attempt = 0;\n\n const heartbeatTimer = setInterval(() => {\n if (abortSignal.aborted) return;\n if (lastEventAt === 0) return;\n const silentForMs = Date.now() - lastEventAt;\n if (silentForMs > 5 * 60_000) {\n log.warn({ accountId: account.accountId, silentForMs }, 'Feishu socket mode: no events recently');\n }\n }, 60_000);\n\n try {\n while (!abortSignal.aborted) {\n attempt++;\n const client = createFeishuClient(account);\n const { wsClient, dispatcher, api } = client;\n\n dispatcher.register({\n 'im.message.receive_v1': async (data: any) => {\n await handleMessageReceive(data);\n },\n 'im.message.reaction.created_v1': async (data: any) => {\n await handleReactionEvent('created', data, api);\n },\n 'im.message.reaction.deleted_v1': async (data: any) => {\n await handleReactionEvent('deleted', data, api);\n },\n 'im.message.recalled_v1': async (data: any) => {\n await handleMessageRecalled(data);\n },\n 'card.action.trigger': async (data: any) => {\n await handleCardAction(data);\n },\n });\n\n try {\n wsClient.start({ eventDispatcher: dispatcher });\n } catch (err) {\n const delayMs = computeBackoffMs(attempt);\n log.error({ err, accountId: account.accountId, delayMs }, 'Feishu socket mode start failed; retrying');\n await sleep(delayMs, abortSignal);\n continue;\n }\n\n // Wait until abort or wsClient errors out.\n const exited = await new Promise<{ ok: boolean; err?: unknown }>((resolve) => {\n const onAbort = () => resolve({ ok: true });\n if (abortSignal.aborted) return resolve({ ok: true });\n abortSignal.addEventListener('abort', onAbort, { once: true });\n (wsClient as any)?.on?.('error', (err: unknown) => resolve({ ok: false, err }));\n (wsClient as any)?.on?.('close', () => resolve({ ok: false, err: new Error('ws closed') }));\n });\n\n try {\n wsClient.stop();\n } catch {\n // ignore\n }\n\n if (abortSignal.aborted) {\n return;\n }\n\n const delayMs = computeBackoffMs(attempt);\n log.warn(\n { accountId: account.accountId, delayMs, err: exited.err ? String(exited.err) : undefined },\n 'Feishu socket mode disconnected; restarting',\n );\n await sleep(delayMs, abortSignal);\n }\n } finally {\n clearInterval(heartbeatTimer);\n }\n }\n\n return { run };\n}\n\nasync function fetchFeishuMessageForReaction(api: any, messageId: string, timeoutMs: number): Promise<any | null> {\n const t = messageId.trim();\n if (!t) return null;\n try {\n const result = await Promise.race([\n (api as any).im.message.get({ path: { message_id: t } }),\n new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs)),\n ]);\n return (result as any)?.data ?? result;\n } catch {\n return null;\n }\n}\n\nfunction extractFeishuMessagePreview(msg: any): string {\n const raw = msg?.body?.content ?? msg?.content ?? msg?.message?.content;\n if (typeof raw !== 'string' || !raw.trim()) return '';\n try {\n const parsed = JSON.parse(raw);\n const t =\n typeof parsed?.text === 'string'\n ? parsed.text\n : typeof parsed?.content === 'string'\n ? parsed.content\n : '';\n return summarizeText(String(t || ''), 80);\n } catch {\n return summarizeText(raw, 80);\n }\n}\n\nfunction summarizeText(text: string, max: number): string {\n const s = text.replace(/\\s+/g, ' ').trim();\n if (s.length <= max) return s;\n return s.slice(0, max - 1) + '…';\n}\n\nfunction safeJsonText(raw: unknown): string | undefined {\n if (typeof raw !== 'string' || !raw.trim()) return undefined;\n try {\n const parsed = JSON.parse(raw);\n const t =\n typeof parsed?.text === 'string'\n ? parsed.text\n : typeof parsed?.content === 'string'\n ? parsed.content\n : typeof parsed?.title === 'string'\n ? parsed.title\n : undefined;\n return typeof t === 'string' ? t : raw;\n } catch {\n return raw;\n }\n}\n\nfunction extractCardActionCommand(action: any): string | null {\n const value = action?.value;\n if (!value || typeof value !== 'object') {\n return null;\n }\n const v = value as Record<string, unknown>;\n const command = typeof v.command === 'string' ? v.command.trim() : '';\n if (command) return command;\n const text = typeof v.text === 'string' ? v.text.trim() : '';\n if (text) return text;\n return null;\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) return resolve();\n const t = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(t);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function computeBackoffMs(attempt: number): number;
@@ -0,0 +1,10 @@
1
+ //#region extensions/feishu/src/transport/socket-mode/retry.ts
2
+ function computeBackoffMs(attempt) {
3
+ const base = 500 * 2 ** (Math.max(1, Math.min(attempt, 10)) - 1);
4
+ const jitter = Math.floor(Math.random() * 250);
5
+ return Math.min(3e4, base + jitter);
6
+ }
7
+ //#endregion
8
+ export { computeBackoffMs };
9
+
10
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","names":[],"sources":["../../../../../../extensions/feishu/src/transport/socket-mode/retry.ts"],"sourcesContent":["export function computeBackoffMs(attempt: number): number {\n const a = Math.max(1, Math.min(attempt, 10));\n const base = 500 * 2 ** (a - 1); // 0.5s, 1s, 2s, ...\n const jitter = Math.floor(Math.random() * 250);\n return Math.min(30_000, base + jitter);\n}\n\n"],"mappings":";AAAA,SAAgB,iBAAiB,SAAyB;CAExD,MAAM,OAAO,MAAM,MADT,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,GAAG,CACjB,GAAG;CAC7B,MAAM,SAAS,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI;AAC9C,QAAO,KAAK,IAAI,KAAQ,OAAO,OAAO"}
@@ -0,0 +1 @@
1
+ export declare function stripFeishuMentions(text: string): string;
@@ -0,0 +1,9 @@
1
+ //#region extensions/feishu/src/transport/text/mentions.ts
2
+ const FEISHU_AT_RE = /<at\s+user_id="[^"]*">([^<]*)<\/at>/g;
3
+ function stripFeishuMentions(text) {
4
+ return String(text ?? "").replaceAll(FEISHU_AT_RE, "$1");
5
+ }
6
+ //#endregion
7
+ export { stripFeishuMentions };
8
+
9
+ //# sourceMappingURL=mentions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentions.js","names":[],"sources":["../../../../../../extensions/feishu/src/transport/text/mentions.ts"],"sourcesContent":["const FEISHU_AT_RE = /<at\\s+user_id=\"[^\"]*\">([^<]*)<\\/at>/g;\n\nexport function stripFeishuMentions(text: string): string {\n const raw = String(text ?? '');\n return raw.replaceAll(FEISHU_AT_RE, '$1');\n}\n\n"],"mappings":";AAAA,MAAM,eAAe;AAErB,SAAgB,oBAAoB,MAAsB;AAExD,QADY,OAAO,QAAQ,GACjB,CAAC,WAAW,cAAc,KAAK"}
@@ -0,0 +1,19 @@
1
+ import type { MessageBus } from '@xopcai/xopc/infra/bus/index.js';
2
+ import type { Config } from '@xopcai/xopc/config/schema.js';
3
+ import type { ChannelSecurityContext } from '@xopcai/xopc/channels/plugin-types.js';
4
+ import type { ResolvedFeishuAccount } from '../../state/accounts.js';
5
+ export interface FeishuWebhookMonitorDeps {
6
+ account: ResolvedFeishuAccount;
7
+ config: Config;
8
+ bus: MessageBus;
9
+ abortSignal: AbortSignal;
10
+ security: {
11
+ checkAccess: (ctx: ChannelSecurityContext) => {
12
+ allowed: boolean;
13
+ reason?: string;
14
+ } | undefined;
15
+ };
16
+ }
17
+ export declare function createFeishuWebhookMonitor(deps: FeishuWebhookMonitorDeps): {
18
+ run: () => Promise<void>;
19
+ };