@xopcai/xopc 0.0.14 → 0.0.16

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 (161) 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-C2blSFQk.js → agents-Dy5cGVVQ.js} +2 -2
  90. package/dist/gateway/static/root/assets/{agents-C2blSFQk.js.map → agents-Dy5cGVVQ.js.map} +1 -1
  91. package/dist/gateway/static/root/assets/{apps-page-Dg7InQ41.js → apps-page-BOpDR0Lz.js} +2 -2
  92. package/dist/gateway/static/root/assets/{apps-page-Dg7InQ41.js.map → apps-page-BOpDR0Lz.js.map} +1 -1
  93. package/dist/gateway/static/root/assets/channels-settings-CrCesccB.js +9 -0
  94. package/dist/gateway/static/root/assets/channels-settings-CrCesccB.js.map +1 -0
  95. package/dist/gateway/static/root/assets/{cron-page-B-7O4_QQ.js → cron-page-B_XY0gPt.js} +2 -2
  96. package/dist/gateway/static/root/assets/{cron-page-B-7O4_QQ.js.map → cron-page-B_XY0gPt.js.map} +1 -1
  97. package/dist/gateway/static/root/assets/{cron-utils-DvRPM814.js → cron-utils-BYdnLwhl.js} +2 -2
  98. package/dist/gateway/static/root/assets/{cron-utils-DvRPM814.js.map → cron-utils-BYdnLwhl.js.map} +1 -1
  99. package/dist/gateway/static/root/assets/{dist-DYzyRkRh.js → dist-DvaA5uNp.js} +2 -2
  100. package/dist/gateway/static/root/assets/{dist-DYzyRkRh.js.map → dist-DvaA5uNp.js.map} +1 -1
  101. package/dist/gateway/static/root/assets/{extension-debug-page-DV-NwlaY.js → extension-debug-page-CPSk7gFW.js} +2 -2
  102. package/dist/gateway/static/root/assets/{extension-debug-page-DV-NwlaY.js.map → extension-debug-page-CPSk7gFW.js.map} +1 -1
  103. package/dist/gateway/static/root/assets/{extension-page-UUKLJwpo.js → extension-page-COdbk9I6.js} +2 -2
  104. package/dist/gateway/static/root/assets/{extension-page-UUKLJwpo.js.map → extension-page-COdbk9I6.js.map} +1 -1
  105. package/dist/gateway/static/root/assets/{extension-settings-page-R4VlbZOj.js → extension-settings-page-BlEz2Ily.js} +2 -2
  106. package/dist/gateway/static/root/assets/{extension-settings-page-R4VlbZOj.js.map → extension-settings-page-BlEz2Ily.js.map} +1 -1
  107. package/dist/gateway/static/root/assets/index-BQNdJlkw.css +1 -0
  108. package/dist/gateway/static/root/assets/index-tm9ZY35l.js +144 -0
  109. package/dist/gateway/static/root/assets/{index-BCVqNi5T.js.map → index-tm9ZY35l.js.map} +1 -1
  110. package/dist/gateway/static/root/assets/{logs-page-BgUDjMge.js → logs-page-LSa0jmLO.js} +2 -2
  111. package/dist/gateway/static/root/assets/{logs-page-BgUDjMge.js.map → logs-page-LSa0jmLO.js.map} +1 -1
  112. package/dist/gateway/static/root/assets/sessions-page-cn2fi_V3.js +2 -0
  113. package/dist/gateway/static/root/assets/sessions-page-cn2fi_V3.js.map +1 -0
  114. package/dist/gateway/static/root/assets/{settings-page-Doa_lzdW.js → settings-page-CyHd5szQ.js} +2 -2
  115. package/dist/gateway/static/root/assets/{settings-page-Doa_lzdW.js.map → settings-page-CyHd5szQ.js.map} +1 -1
  116. package/dist/gateway/static/root/assets/{skills-page-BdNqg2NG.js → skills-page-irjxwW9u.js} +2 -2
  117. package/dist/gateway/static/root/assets/{skills-page-BdNqg2NG.js.map → skills-page-irjxwW9u.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 +3 -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/providers/env-keys.js +1 -0
  150. package/dist/src/providers/env-keys.js.map +1 -1
  151. package/dist/src/providers/index.js +5 -0
  152. package/dist/src/providers/index.js.map +1 -1
  153. package/dist/src/session/session-title.js +2 -1
  154. package/dist/src/session/session-title.js.map +1 -1
  155. package/package.json +2 -2
  156. package/dist/gateway/static/root/assets/channels-settings-CHOL7G_P.js +0 -9
  157. package/dist/gateway/static/root/assets/channels-settings-CHOL7G_P.js.map +0 -1
  158. package/dist/gateway/static/root/assets/index-BCVqNi5T.js +0 -144
  159. package/dist/gateway/static/root/assets/index-XbYityMf.css +0 -1
  160. package/dist/gateway/static/root/assets/sessions-page-L2VUocA4.js +0 -2
  161. package/dist/gateway/static/root/assets/sessions-page-L2VUocA4.js.map +0 -1
@@ -0,0 +1,271 @@
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 { createFeishuLarkSdkPinoLogger } from "../client/lark-sdk-logger.js";
5
+ import { createFeishuDedupe } from "../reliability/dedupe.js";
6
+ import { stripFeishuMentions } from "../text/mentions.js";
7
+ import { recordFeishuMessageBinding } from "../../state/message-bindings.js";
8
+ import crypto from "node:crypto";
9
+ import lark from "@larksuiteoapi/node-sdk";
10
+ import * as http from "node:http";
11
+ //#region extensions/feishu/src/transport/webhook/monitor.ts
12
+ init_logger();
13
+ const log = createLogger("FeishuWebhook");
14
+ const MAX_BODY_BYTES = 256 * 1024;
15
+ const BODY_TIMEOUT_MS = 2e3;
16
+ const RATE_LIMIT_WINDOW_MS = 6e4;
17
+ const RATE_LIMIT_MAX = 120;
18
+ const RATE_LIMIT_MAX_KEYS = 4096;
19
+ const rateState = /* @__PURE__ */ new Map();
20
+ function pruneRateState(now) {
21
+ for (const [k, v] of rateState.entries()) if (now - v.windowStart > RATE_LIMIT_WINDOW_MS) rateState.delete(k);
22
+ if (rateState.size <= RATE_LIMIT_MAX_KEYS) return;
23
+ const over = rateState.size - RATE_LIMIT_MAX_KEYS;
24
+ let i = 0;
25
+ for (const k of rateState.keys()) {
26
+ rateState.delete(k);
27
+ i++;
28
+ if (i >= over) break;
29
+ }
30
+ }
31
+ function isRateLimited(key, now) {
32
+ pruneRateState(now);
33
+ const cur = rateState.get(key);
34
+ if (!cur || now - cur.windowStart > RATE_LIMIT_WINDOW_MS) {
35
+ rateState.set(key, {
36
+ windowStart: now,
37
+ count: 1
38
+ });
39
+ return false;
40
+ }
41
+ cur.count += 1;
42
+ return cur.count > RATE_LIMIT_MAX;
43
+ }
44
+ function safeEqual(a, b) {
45
+ if (a.length !== b.length) return false;
46
+ try {
47
+ return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+ function signatureValid(headers, rawBody, encryptKey) {
53
+ const timestampHeader = headers["x-lark-request-timestamp"];
54
+ const nonceHeader = headers["x-lark-request-nonce"];
55
+ const signatureHeader = headers["x-lark-signature"];
56
+ const timestamp = Array.isArray(timestampHeader) ? timestampHeader[0] : timestampHeader;
57
+ const nonce = Array.isArray(nonceHeader) ? nonceHeader[0] : nonceHeader;
58
+ const signature = Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader;
59
+ if (!timestamp || !nonce || !signature) return false;
60
+ if (typeof signature !== "string" || signature.length < 32) return false;
61
+ return safeEqual(crypto.createHash("sha256").update(timestamp + nonce + encryptKey + rawBody).digest("hex"), signature);
62
+ }
63
+ function readBody(req) {
64
+ return new Promise((resolve) => {
65
+ let done = false;
66
+ let bytes = 0;
67
+ const chunks = [];
68
+ const t = setTimeout(() => {
69
+ if (done) return;
70
+ done = true;
71
+ resolve({
72
+ ok: false,
73
+ status: 408,
74
+ msg: "Request body timeout"
75
+ });
76
+ req.destroy();
77
+ }, BODY_TIMEOUT_MS);
78
+ req.on("data", (chunk) => {
79
+ if (done) return;
80
+ bytes += chunk.length;
81
+ if (bytes > MAX_BODY_BYTES) {
82
+ done = true;
83
+ clearTimeout(t);
84
+ resolve({
85
+ ok: false,
86
+ status: 413,
87
+ msg: "Payload too large"
88
+ });
89
+ req.destroy();
90
+ return;
91
+ }
92
+ chunks.push(chunk);
93
+ });
94
+ req.on("end", () => {
95
+ if (done) return;
96
+ done = true;
97
+ clearTimeout(t);
98
+ resolve({
99
+ ok: true,
100
+ body: Buffer.concat(chunks).toString("utf8")
101
+ });
102
+ });
103
+ req.on("error", () => {
104
+ if (done) return;
105
+ done = true;
106
+ clearTimeout(t);
107
+ resolve({
108
+ ok: false,
109
+ status: 400,
110
+ msg: "Bad Request"
111
+ });
112
+ });
113
+ });
114
+ }
115
+ function json(res, status, obj) {
116
+ res.statusCode = status;
117
+ res.setHeader("content-type", "application/json; charset=utf-8");
118
+ res.end(JSON.stringify(obj));
119
+ }
120
+ function text(res, status, body) {
121
+ res.statusCode = status;
122
+ res.setHeader("content-type", "text/plain; charset=utf-8");
123
+ res.end(body);
124
+ }
125
+ function createFeishuWebhookMonitor(deps) {
126
+ const { account, bus, abortSignal, security } = deps;
127
+ const dedupe = createFeishuDedupe();
128
+ const encryptKey = (account.encryptKey ?? "").trim();
129
+ const verificationToken = (account.verificationToken ?? "").trim();
130
+ if (!encryptKey) throw new Error("Feishu webhook mode requires encryptKey");
131
+ if (!verificationToken) throw new Error("Feishu webhook mode requires verificationToken");
132
+ const host = (account.webhookHost ?? "127.0.0.1").trim() || "127.0.0.1";
133
+ const port = account.webhookPort ?? 3e3;
134
+ const path = (account.webhookPath ?? "/feishu/events").trim() || "/feishu/events";
135
+ const l = lark;
136
+ const sdkLogger = createFeishuLarkSdkPinoLogger(account.accountId);
137
+ async function handleMessageReceive(event) {
138
+ const msg = event?.event?.message ?? event?.message ?? event?.data?.message;
139
+ const sender = event?.event?.sender ?? event?.sender ?? event?.data?.sender;
140
+ const chatId = msg?.chat_id ?? msg?.chatId ?? "";
141
+ const messageId = msg?.message_id ?? msg?.messageId ?? "";
142
+ const chatType = msg?.chat_type ?? msg?.chatType ?? "";
143
+ const senderId = sender?.sender_id?.open_id ?? sender?.sender_id?.user_id ?? sender?.open_id ?? sender?.user_id ?? "";
144
+ const senderName = sender?.sender_id?.name ?? sender?.sender_name ?? void 0;
145
+ if (!chatId || !messageId || !senderId) return;
146
+ if (!dedupe.claim(messageId)) return;
147
+ const isGroup = chatType === "group" || chatType === "chat";
148
+ const normalizedText = stripFeishuMentions((msg?.content && typeof msg.content === "string" ? safeJsonText(msg.content) : safeJsonText(msg?.body?.content)) ?? "").trim();
149
+ if (!normalizedText) return;
150
+ const threadId = msg?.thread_id ?? msg?.threadId ?? void 0;
151
+ const sessionKey = generateSessionKey({
152
+ source: "feishu",
153
+ chatId,
154
+ senderId,
155
+ isGroup,
156
+ threadId: typeof threadId === "string" && threadId.trim() ? threadId : void 0,
157
+ accountId: account.accountId
158
+ });
159
+ recordFeishuMessageBinding({
160
+ messageId,
161
+ sessionKey,
162
+ accountId: account.accountId,
163
+ chatId,
164
+ senderId,
165
+ isGroup,
166
+ threadId: typeof threadId === "string" && threadId.trim() ? threadId : void 0
167
+ });
168
+ const access = security.checkAccess({
169
+ accountId: account.accountId,
170
+ chatId,
171
+ senderId,
172
+ senderName,
173
+ isGroup,
174
+ threadId
175
+ });
176
+ if (access && !access.allowed) return;
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
+ feishuEventType: "im.message.receive_v1",
189
+ raw: event
190
+ }
191
+ });
192
+ }
193
+ const dispatcher = new l.EventDispatcher({
194
+ verifyChallenge: false,
195
+ encryptKey,
196
+ verificationToken,
197
+ logger: sdkLogger,
198
+ loggerLevel: l.LoggerLevel.info
199
+ });
200
+ dispatcher.register({ "im.message.receive_v1": async (data) => await handleMessageReceive(data) });
201
+ const server = http.createServer();
202
+ async function handleRequest(req, res) {
203
+ if (req.method !== "POST") return text(res, 405, "Method Not Allowed");
204
+ if ((req.url ?? "").split("?")[0] !== path) return text(res, 404, "Not Found");
205
+ const now = Date.now();
206
+ if (isRateLimited(`${account.accountId}:${path}:${req.socket.remoteAddress ?? "unknown"}`, now)) return text(res, 429, "Too Many Requests");
207
+ if (!String(req.headers["content-type"] ?? "").toLowerCase().includes("application/json")) return text(res, 415, "Unsupported Media Type");
208
+ const body = await readBody(req);
209
+ if (body.ok === false) return text(res, body.status, body.msg);
210
+ if (!signatureValid(req.headers, body.body, encryptKey)) return text(res, 401, "Invalid signature");
211
+ let payload;
212
+ try {
213
+ payload = JSON.parse(body.body);
214
+ } catch {
215
+ return text(res, 400, "Invalid JSON");
216
+ }
217
+ const token = payload?.token ?? payload?.header?.token ?? payload?.event?.token;
218
+ if (typeof token === "string" && token.trim() && token.trim() !== verificationToken) return text(res, 401, "Invalid verification token");
219
+ const { isChallenge, challenge } = lark.generateChallenge(payload, { encryptKey });
220
+ if (isChallenge) return json(res, 200, challenge);
221
+ const envelope = Object.assign(Object.create({ headers: req.headers }), payload);
222
+ const out = await dispatcher.invoke(envelope, { needCheck: false });
223
+ if (!res.headersSent) return json(res, 200, out);
224
+ }
225
+ async function run() {
226
+ await new Promise((resolve, reject) => {
227
+ server.on("request", (req, res) => {
228
+ handleRequest(req, res).catch((err) => {
229
+ log.error({
230
+ err,
231
+ accountId: account.accountId
232
+ }, "Feishu webhook handler error");
233
+ if (!res.headersSent) text(res, 500, "Internal Server Error");
234
+ });
235
+ });
236
+ server.on("error", (err) => reject(err));
237
+ server.listen(port, host, () => resolve());
238
+ });
239
+ log.info({
240
+ accountId: account.accountId,
241
+ host,
242
+ port,
243
+ path
244
+ }, "Feishu webhook server listening");
245
+ await new Promise((resolve) => {
246
+ if (abortSignal.aborted) {
247
+ server.close();
248
+ return resolve();
249
+ }
250
+ abortSignal.addEventListener("abort", () => {
251
+ server.close();
252
+ resolve();
253
+ }, { once: true });
254
+ });
255
+ }
256
+ return { run };
257
+ }
258
+ function safeJsonText(raw) {
259
+ if (typeof raw !== "string" || !raw.trim()) return void 0;
260
+ try {
261
+ const parsed = JSON.parse(raw);
262
+ const t = typeof parsed?.text === "string" ? parsed.text : typeof parsed?.content === "string" ? parsed.content : typeof parsed?.title === "string" ? parsed.title : void 0;
263
+ return typeof t === "string" ? t : raw;
264
+ } catch {
265
+ return raw;
266
+ }
267
+ }
268
+ //#endregion
269
+ export { createFeishuWebhookMonitor };
270
+
271
+ //# sourceMappingURL=monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.js","names":[],"sources":["../../../../../../extensions/feishu/src/transport/webhook/monitor.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport * as http from 'node:http';\n\nimport lark from '@larksuiteoapi/node-sdk';\n\nimport 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 { createFeishuDedupe } from '../reliability/dedupe.js';\nimport { stripFeishuMentions } from '../text/mentions.js';\nimport { recordFeishuMessageBinding } from '../../state/message-bindings.js';\nimport { createFeishuLarkSdkPinoLogger } from '../client/lark-sdk-logger.js';\n\nconst log = createLogger('FeishuWebhook');\n\nconst MAX_BODY_BYTES = 256 * 1024;\nconst BODY_TIMEOUT_MS = 2_000;\nconst RATE_LIMIT_WINDOW_MS = 60_000;\nconst RATE_LIMIT_MAX = 120;\nconst RATE_LIMIT_MAX_KEYS = 4096;\n\ntype RateKeyState = { windowStart: number; count: number };\nconst rateState = new Map<string, RateKeyState>();\n\nfunction pruneRateState(now: number) {\n for (const [k, v] of rateState.entries()) {\n if (now - v.windowStart > RATE_LIMIT_WINDOW_MS) {\n rateState.delete(k);\n }\n }\n if (rateState.size <= RATE_LIMIT_MAX_KEYS) return;\n const over = rateState.size - RATE_LIMIT_MAX_KEYS;\n let i = 0;\n for (const k of rateState.keys()) {\n rateState.delete(k);\n i++;\n if (i >= over) break;\n }\n}\n\nfunction isRateLimited(key: string, now: number): boolean {\n pruneRateState(now);\n const cur = rateState.get(key);\n if (!cur || now - cur.windowStart > RATE_LIMIT_WINDOW_MS) {\n rateState.set(key, { windowStart: now, count: 1 });\n return false;\n }\n cur.count += 1;\n return cur.count > RATE_LIMIT_MAX;\n}\n\nfunction safeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n try {\n return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));\n } catch {\n return false;\n }\n}\n\nfunction signatureValid(headers: http.IncomingHttpHeaders, rawBody: string, encryptKey: string): boolean {\n const timestampHeader = headers['x-lark-request-timestamp'];\n const nonceHeader = headers['x-lark-request-nonce'];\n const signatureHeader = headers['x-lark-signature'];\n const timestamp = Array.isArray(timestampHeader) ? timestampHeader[0] : timestampHeader;\n const nonce = Array.isArray(nonceHeader) ? nonceHeader[0] : nonceHeader;\n const signature = Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader;\n if (!timestamp || !nonce || !signature) return false;\n if (typeof signature !== 'string' || signature.length < 32) return false;\n const computed = crypto.createHash('sha256').update(timestamp + nonce + encryptKey + rawBody).digest('hex');\n return safeEqual(computed, signature);\n}\n\nfunction readBody(\n req: http.IncomingMessage,\n): Promise<{ ok: true; body: string } | { ok: false; status: number; msg: string }> {\n return new Promise((resolve) => {\n let done = false;\n let bytes = 0;\n const chunks: Buffer[] = [];\n const t = setTimeout(() => {\n if (done) return;\n done = true;\n resolve({ ok: false, status: 408, msg: 'Request body timeout' });\n req.destroy();\n }, BODY_TIMEOUT_MS);\n\n req.on('data', (chunk: Buffer) => {\n if (done) return;\n bytes += chunk.length;\n if (bytes > MAX_BODY_BYTES) {\n done = true;\n clearTimeout(t);\n resolve({ ok: false, status: 413, msg: 'Payload too large' });\n req.destroy();\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n if (done) return;\n done = true;\n clearTimeout(t);\n resolve({ ok: true, body: Buffer.concat(chunks).toString('utf8') });\n });\n req.on('error', () => {\n if (done) return;\n done = true;\n clearTimeout(t);\n resolve({ ok: false, status: 400, msg: 'Bad Request' });\n });\n });\n}\n\nfunction json(res: http.ServerResponse, status: number, obj: unknown) {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json; charset=utf-8');\n res.end(JSON.stringify(obj));\n}\n\nfunction text(res: http.ServerResponse, status: number, body: string) {\n res.statusCode = status;\n res.setHeader('content-type', 'text/plain; charset=utf-8');\n res.end(body);\n}\n\nexport interface FeishuWebhookMonitorDeps {\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 createFeishuWebhookMonitor(deps: FeishuWebhookMonitorDeps) {\n const { account, bus, abortSignal, security } = deps;\n const dedupe = createFeishuDedupe();\n\n const encryptKey = (account.encryptKey ?? '').trim();\n const verificationToken = (account.verificationToken ?? '').trim();\n if (!encryptKey) throw new Error('Feishu webhook mode requires encryptKey');\n if (!verificationToken) throw new Error('Feishu webhook mode requires verificationToken');\n\n const host = (account.webhookHost ?? '127.0.0.1').trim() || '127.0.0.1';\n const port = account.webhookPort ?? 3000;\n const path = (account.webhookPath ?? '/feishu/events').trim() || '/feishu/events';\n const l = lark as any;\n const sdkLogger = createFeishuLarkSdkPinoLogger(account.accountId);\n\n async function handleMessageReceive(event: any): Promise<void> {\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) return;\n if (!dedupe.claim(messageId)) return;\n\n const isGroup = chatType === 'group' || chatType === 'chat';\n const textRaw =\n msg?.content && typeof msg.content === 'string'\n ? safeJsonText(msg.content)\n : safeJsonText(msg?.body?.content);\n const normalizedText = stripFeishuMentions(textRaw ?? '').trim();\n if (!normalizedText) return;\n\n const threadId = msg?.thread_id ?? msg?.threadId ?? undefined;\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 access = security.checkAccess({\n accountId: account.accountId,\n chatId,\n senderId,\n senderName,\n isGroup,\n threadId,\n });\n if (access && !access.allowed) return;\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 feishuEventType: 'im.message.receive_v1',\n raw: event,\n },\n });\n }\n\n const dispatcher = new l.EventDispatcher({\n verifyChallenge: false,\n encryptKey,\n verificationToken,\n logger: sdkLogger,\n loggerLevel: l.LoggerLevel.info,\n } as any);\n dispatcher.register({\n 'im.message.receive_v1': async (data: any) => await handleMessageReceive(data),\n });\n\n const server = http.createServer();\n\n async function handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {\n if (req.method !== 'POST') return text(res, 405, 'Method Not Allowed');\n if ((req.url ?? '').split('?')[0] !== path) return text(res, 404, 'Not Found');\n\n const now = Date.now();\n const rateKey = `${account.accountId}:${path}:${req.socket.remoteAddress ?? 'unknown'}`;\n if (isRateLimited(rateKey, now)) return text(res, 429, 'Too Many Requests');\n\n const contentType = String(req.headers['content-type'] ?? '');\n if (!contentType.toLowerCase().includes('application/json')) return text(res, 415, 'Unsupported Media Type');\n\n const body = await readBody(req);\n if (body.ok === false) return text(res, body.status, body.msg);\n\n if (!signatureValid(req.headers, body.body, encryptKey)) return text(res, 401, 'Invalid signature');\n\n let payload: any;\n try {\n payload = JSON.parse(body.body);\n } catch {\n return text(res, 400, 'Invalid JSON');\n }\n\n const token = payload?.token ?? payload?.header?.token ?? payload?.event?.token;\n if (typeof token === 'string' && token.trim() && token.trim() !== verificationToken) {\n return text(res, 401, 'Invalid verification token');\n }\n\n const { isChallenge, challenge } = (lark as any).generateChallenge(payload, { encryptKey });\n if (isChallenge) return json(res, 200, challenge);\n\n const envelope = Object.assign(Object.create({ headers: req.headers }), payload);\n const out = await dispatcher.invoke(envelope, { needCheck: false });\n if (!res.headersSent) return json(res, 200, out);\n }\n\n async function run(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.on('request', (req, res) => {\n void handleRequest(req, res).catch((err) => {\n log.error({ err, accountId: account.accountId }, 'Feishu webhook handler error');\n if (!res.headersSent) text(res, 500, 'Internal Server Error');\n });\n });\n server.on('error', (err) => reject(err));\n server.listen(port, host, () => resolve());\n });\n\n log.info({ accountId: account.accountId, host, port, path }, 'Feishu webhook server listening');\n\n await new Promise<void>((resolve) => {\n if (abortSignal.aborted) {\n server.close();\n return resolve();\n }\n abortSignal.addEventListener(\n 'abort',\n () => {\n server.close();\n resolve();\n },\n { once: true },\n );\n });\n }\n\n return { run };\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\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ChannelConfigSurfaceAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';
2
+ export declare const feishuConfigSurface: ChannelConfigSurfaceAdapter;
@@ -0,0 +1,6 @@
1
+ //#region extensions/feishu/src/ui/config-surface.ts
2
+ const feishuConfigSurface = { buildConfigSurface: (cfg) => cfg.channels?.feishu ?? {} };
3
+ //#endregion
4
+ export { feishuConfigSurface };
5
+
6
+ //# sourceMappingURL=config-surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-surface.js","names":[],"sources":["../../../../../extensions/feishu/src/ui/config-surface.ts"],"sourcesContent":["import type { ChannelConfigSurfaceAdapter } from '@xopcai/xopc/channels/plugins/types.adapters.js';\nimport type { Config } from '@xopcai/xopc/config/schema.js';\n\nexport const feishuConfigSurface: ChannelConfigSurfaceAdapter = {\n // Note: unlike providers (which intentionally return masked values),\n // channel config is returned as plain text in /api/config so the Web UI can\n // render it like Telegram: hidden by default, reveal on demand.\n buildConfigSurface: (cfg: Config) => (cfg.channels?.feishu as Record<string, unknown>) ?? {},\n};\n\n"],"mappings":";AAGA,MAAa,sBAAmD,EAI9D,qBAAqB,QAAiB,IAAI,UAAU,UAAsC,EAAE,EAC7F"}
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "feishu-tools",
3
+ "name": "Feishu Tools",
4
+ "description": "Feishu/Lark tools (doc/wiki/drive/perm/bitable) for xopc agents. Activates when the Feishu channel is configured.",
5
+ "version": "0.0.14",
6
+ "kind": "tool",
7
+ "main": "src/extension.ts",
8
+ "channels": ["feishu", "lark"],
9
+ "activation": {
10
+ "onChannels": ["feishu", "lark"],
11
+ "onCapabilities": ["tool"]
12
+ },
13
+ "setup": {
14
+ "providers": [],
15
+ "requiresRuntime": true
16
+ }
17
+ }
18
+
@@ -0,0 +1,20 @@
1
+ {
2
+ "id": "telegram",
3
+ "name": "Telegram Channel",
4
+ "description": "Bundled Telegram Bot channel (private workspace sources; ships inside @xopcai/xopc dist/)",
5
+ "version": "0.0.16",
6
+ "kind": "channel",
7
+ "main": "src/index.ts",
8
+ "channels": ["telegram"],
9
+ "channelEnvVars": {
10
+ "telegram": ["TELEGRAM_BOT_TOKEN"]
11
+ },
12
+ "activation": {
13
+ "onChannels": ["telegram"],
14
+ "onCapabilities": ["channel"]
15
+ },
16
+ "setup": {
17
+ "providers": [],
18
+ "requiresRuntime": true
19
+ }
20
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "weixin",
3
+ "name": "WeChat (Weixin) Channel",
4
+ "description": "WeChat ilink channel (private workspace sources; ships inside @xopcai/xopc dist/)",
5
+ "version": "0.1.0",
6
+ "kind": "channel",
7
+ "main": "src/index.ts",
8
+ "channels": ["weixin"],
9
+ "activation": {
10
+ "onChannels": ["weixin"],
11
+ "onCapabilities": ["channel"]
12
+ },
13
+ "setup": {
14
+ "providers": [],
15
+ "requiresRuntime": true
16
+ }
17
+ }