@holon-run/agentinbox 1.0.4 → 1.1.0

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.
@@ -3,12 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FeishuDeliveryAdapter = exports.FeishuUxcClient = exports.DEFAULT_FEISHU_EVENT_TYPES = exports.FEISHU_IM_SCHEMA_URL = exports.FEISHU_OPENAPI_ENDPOINT = void 0;
4
4
  exports.feishuDeliveryOperationsForHandle = feishuDeliveryOperationsForHandle;
5
5
  exports.invokeFeishuDeliveryOperation = invokeFeishuDeliveryOperation;
6
+ exports.feishuFollowTemplateSpec = feishuFollowTemplateSpec;
7
+ exports.expandFeishuFollowTemplate = expandFeishuFollowTemplate;
8
+ exports.feishuSourceOperations = feishuSourceOperations;
9
+ exports.invokeFeishuSourceOperation = invokeFeishuSourceOperation;
6
10
  exports.normalizeFeishuBotEvent = normalizeFeishuBotEvent;
7
11
  exports.parseFeishuSourceConfig = parseFeishuSourceConfig;
8
12
  const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
13
+ const node_fs_1 = require("node:fs");
14
+ const node_path_1 = require("node:path");
15
+ const node_os_1 = require("node:os");
9
16
  exports.FEISHU_OPENAPI_ENDPOINT = "https://open.feishu.cn/open-apis";
10
17
  exports.FEISHU_IM_SCHEMA_URL = "https://raw.githubusercontent.com/holon-run/uxc/main/skills/feishu-openapi-skill/references/feishu-im.openapi.json";
18
+ const FEISHU_BOT_SCHEMA_FILE = (0, node_path_1.join)((0, node_os_1.tmpdir)(), "agentinbox-feishu-bot.openapi.json");
11
19
  exports.DEFAULT_FEISHU_EVENT_TYPES = ["im.message.receive_v1"];
20
+ const DEFAULT_CONTEXT_BOUND_SECONDS = 7 * 24 * 60 * 60;
12
21
  class FeishuUxcClient {
13
22
  client;
14
23
  constructor(client = new uxc_daemon_client_1.UxcDaemonClient({ env: process.env })) {
@@ -18,13 +27,13 @@ class FeishuUxcClient {
18
27
  await this.client.call({
19
28
  endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
20
29
  operation: "post:/im/v1/messages",
21
- payload: {
30
+ payload: compactPayload({
22
31
  receive_id_type: "chat_id",
23
32
  receive_id: input.chatId,
24
33
  msg_type: input.msgType,
25
34
  content: input.content,
26
- uuid: input.uuid ?? null,
27
- },
35
+ uuid: input.uuid,
36
+ }),
28
37
  options: {
29
38
  auth: input.auth,
30
39
  schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
@@ -35,18 +44,127 @@ class FeishuUxcClient {
35
44
  await this.client.call({
36
45
  endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
37
46
  operation: "post:/im/v1/messages/{message_id}/reply",
38
- payload: {
47
+ payload: compactPayload({
39
48
  message_id: input.messageId,
40
49
  msg_type: input.msgType,
41
50
  content: input.content,
42
- reply_in_thread: input.replyInThread ?? null,
43
- uuid: input.uuid ?? null,
51
+ reply_in_thread: input.replyInThread,
52
+ uuid: input.uuid,
53
+ }),
54
+ options: {
55
+ auth: input.auth,
56
+ schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
57
+ },
58
+ });
59
+ }
60
+ async uploadFile(input) {
61
+ const response = await this.client.call({
62
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
63
+ operation: "post:/im/v1/files",
64
+ payload: {
65
+ file_type: input.fileType ?? "stream",
66
+ file_name: input.fileName,
67
+ file: input.filePath,
68
+ },
69
+ options: {
70
+ auth: input.auth,
71
+ schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
72
+ },
73
+ });
74
+ const fileKey = findStringField(response.data, "file_key");
75
+ if (!fileKey) {
76
+ throw new Error("Feishu file upload response did not include file_key");
77
+ }
78
+ return fileKey;
79
+ }
80
+ async getMessage(input) {
81
+ const response = await this.client.call({
82
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
83
+ operation: "get:/im/v1/messages/{message_id}",
84
+ payload: {
85
+ message_id: input.messageId,
86
+ },
87
+ options: {
88
+ auth: input.auth,
89
+ schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
90
+ },
91
+ });
92
+ return response.data;
93
+ }
94
+ async listMessages(input) {
95
+ const payload = {
96
+ container_id_type: "chat",
97
+ container_id: input.chatId,
98
+ page_size: input.pageSize ?? 20,
99
+ sort_type: input.sort ?? "ByCreateTimeAsc",
100
+ card_msg_content_type: "raw_card_content",
101
+ };
102
+ if (input.startTime) {
103
+ payload.start_time = input.startTime;
104
+ }
105
+ if (input.endTime) {
106
+ payload.end_time = input.endTime;
107
+ }
108
+ if (input.pageToken) {
109
+ payload.page_token = input.pageToken;
110
+ }
111
+ const response = await this.client.call({
112
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
113
+ operation: "get:/im/v1/messages",
114
+ payload,
115
+ options: {
116
+ auth: input.auth,
117
+ schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
118
+ },
119
+ });
120
+ return response.data;
121
+ }
122
+ async getBotOpenId(input) {
123
+ const response = await this.client.call({
124
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
125
+ operation: "get:/bot/v3/info",
126
+ payload: {},
127
+ options: {
128
+ auth: input.auth,
129
+ schema_url: ensureFeishuBotSchemaFile(),
130
+ refresh_schema: true,
131
+ no_cache: true,
132
+ },
133
+ });
134
+ const openId = findStringField(response.data, "open_id");
135
+ if (!openId) {
136
+ throw new Error("Feishu bot info response did not include bot.open_id");
137
+ }
138
+ return openId;
139
+ }
140
+ async listChats(input) {
141
+ const response = await this.client.call({
142
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
143
+ operation: "get:/im/v1/chats",
144
+ payload: compactPayload({
145
+ page_size: input.pageSize ?? 20,
146
+ page_token: input.pageToken,
147
+ }),
148
+ options: {
149
+ auth: input.auth,
150
+ schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
151
+ },
152
+ });
153
+ return response.data;
154
+ }
155
+ async getChat(input) {
156
+ const response = await this.client.call({
157
+ endpoint: input.endpoint ?? exports.FEISHU_OPENAPI_ENDPOINT,
158
+ operation: "get:/im/v1/chats/{chat_id}",
159
+ payload: {
160
+ chat_id: input.chatId,
44
161
  },
45
162
  options: {
46
163
  auth: input.auth,
47
164
  schema_url: input.schemaUrl ?? exports.FEISHU_IM_SCHEMA_URL,
48
165
  },
49
166
  });
167
+ return response.data;
50
168
  }
51
169
  }
52
170
  exports.FeishuUxcClient = FeishuUxcClient;
@@ -64,7 +182,9 @@ function feishuDeliveryOperationsForHandle(handle) {
64
182
  if (handle.surface !== "message_reply" && handle.surface !== "chat_message") {
65
183
  return [];
66
184
  }
67
- return [{
185
+ const commonProperties = commonDeliveryInputSchemaProperties();
186
+ return [
187
+ {
68
188
  name: "send_text",
69
189
  title: handle.surface === "message_reply" ? "Reply With Text" : "Send Text Message",
70
190
  inputSchema: {
@@ -73,29 +193,129 @@ function feishuDeliveryOperationsForHandle(handle) {
73
193
  required: ["text"],
74
194
  properties: {
75
195
  text: { type: "string", minLength: 1 },
76
- endpoint: { type: "string", minLength: 1 },
77
- schemaUrl: { type: "string", minLength: 1 },
78
- schema_url: { type: "string", minLength: 1 },
79
- uxcAuth: { type: "string", minLength: 1 },
80
- auth: { type: "string", minLength: 1 },
81
- replyInThread: { type: "boolean" },
82
- reply_in_thread: { type: "boolean" },
83
- uuid: { type: "string", minLength: 1 },
196
+ ...commonProperties,
84
197
  },
85
198
  },
86
199
  canonicalTextAlias: true,
87
- }];
200
+ },
201
+ {
202
+ name: "send_post",
203
+ title: handle.surface === "message_reply" ? "Reply With Rich Text" : "Send Rich Text Message",
204
+ inputSchema: {
205
+ type: "object",
206
+ additionalProperties: false,
207
+ anyOf: [
208
+ { required: ["blocks"] },
209
+ { required: ["paragraphs"] },
210
+ { required: ["content"] },
211
+ ],
212
+ properties: {
213
+ title: { type: "string" },
214
+ locale: { type: "string", minLength: 1 },
215
+ blocks: {
216
+ type: "array",
217
+ items: { type: "object", additionalProperties: true },
218
+ },
219
+ paragraphs: {
220
+ type: "array",
221
+ items: {
222
+ type: "array",
223
+ items: { type: "object", additionalProperties: true },
224
+ },
225
+ },
226
+ content: {
227
+ anyOf: [
228
+ { type: "string", minLength: 1 },
229
+ { type: "object", additionalProperties: true },
230
+ ],
231
+ },
232
+ ...commonProperties,
233
+ },
234
+ },
235
+ },
236
+ {
237
+ name: "send_file",
238
+ title: handle.surface === "message_reply" ? "Reply With File" : "Send File Message",
239
+ inputSchema: {
240
+ type: "object",
241
+ additionalProperties: false,
242
+ anyOf: [
243
+ { required: ["fileKey"] },
244
+ { required: ["file_key"] },
245
+ ],
246
+ properties: {
247
+ fileKey: { type: "string", minLength: 1 },
248
+ file_key: { type: "string", minLength: 1 },
249
+ ...commonProperties,
250
+ },
251
+ },
252
+ },
253
+ {
254
+ name: "send_file_from_path",
255
+ title: handle.surface === "message_reply" ? "Upload And Reply With File" : "Upload And Send File",
256
+ inputSchema: {
257
+ type: "object",
258
+ additionalProperties: false,
259
+ anyOf: [
260
+ { required: ["filePath"] },
261
+ { required: ["file_path"] },
262
+ ],
263
+ properties: {
264
+ filePath: { type: "string", minLength: 1 },
265
+ file_path: { type: "string", minLength: 1 },
266
+ fileName: { type: "string", minLength: 1 },
267
+ file_name: { type: "string", minLength: 1 },
268
+ fileType: { type: "string", minLength: 1 },
269
+ file_type: { type: "string", minLength: 1 },
270
+ ...commonProperties,
271
+ },
272
+ },
273
+ },
274
+ ];
88
275
  }
89
276
  async function invokeFeishuDeliveryOperation(handle, operation, input, client = new FeishuUxcClient()) {
90
- if (operation !== "send_text") {
91
- throw new Error(`unknown Feishu delivery operation: ${operation}`);
277
+ const config = parseDeliveryConfig(input);
278
+ if (operation === "send_text") {
279
+ const text = asString(input.text);
280
+ if (!text || text.trim().length === 0) {
281
+ throw new Error("send_text requires input.text");
282
+ }
283
+ await sendFeishuMessage(handle, normalizeDeliveryMessage({ text }), config, client);
284
+ return { status: "sent", note: sentNote(handle, "text") };
92
285
  }
93
- const text = asString(input.text);
94
- if (!text || text.trim().length === 0) {
95
- throw new Error("send_text requires input.text");
286
+ if (operation === "send_post") {
287
+ const message = normalizeFeishuPostMessage(input);
288
+ await sendFeishuMessage(handle, message, config, client);
289
+ return { status: "sent", note: sentNote(handle, "rich text") };
96
290
  }
97
- const config = parseDeliveryConfig(input);
98
- const message = normalizeDeliveryMessage({ text });
291
+ if (operation === "send_file") {
292
+ const fileKey = asString(input.fileKey) ?? asString(input.file_key);
293
+ if (!fileKey) {
294
+ throw new Error("send_file requires input.fileKey");
295
+ }
296
+ await sendFeishuMessage(handle, normalizeFeishuFileMessage(fileKey), config, client);
297
+ return { status: "sent", note: sentNote(handle, "file") };
298
+ }
299
+ if (operation === "send_file_from_path") {
300
+ const filePath = asString(input.filePath) ?? asString(input.file_path);
301
+ if (!filePath) {
302
+ throw new Error("send_file_from_path requires input.filePath");
303
+ }
304
+ const fileName = asString(input.fileName) ?? asString(input.file_name) ?? basename(filePath);
305
+ const fileKey = await client.uploadFile({
306
+ endpoint: config.endpoint,
307
+ schemaUrl: config.schemaUrl,
308
+ auth: config.auth,
309
+ filePath,
310
+ fileName,
311
+ fileType: asString(input.fileType) ?? asString(input.file_type) ?? "stream",
312
+ });
313
+ await sendFeishuMessage(handle, normalizeFeishuFileMessage(fileKey), config, client);
314
+ return { status: "sent", note: `${sentNote(handle, "file")} after upload` };
315
+ }
316
+ throw new Error(`unknown Feishu delivery operation: ${operation}`);
317
+ }
318
+ async function sendFeishuMessage(handle, message, config, client) {
99
319
  if (handle.surface === "message_reply") {
100
320
  await client.replyToMessage({
101
321
  endpoint: config.endpoint,
@@ -107,7 +327,7 @@ async function invokeFeishuDeliveryOperation(handle, operation, input, client =
107
327
  replyInThread: config.replyInThread,
108
328
  uuid: config.uuid,
109
329
  });
110
- return { status: "sent", note: "sent Feishu message reply" };
330
+ return;
111
331
  }
112
332
  if (handle.surface === "chat_message") {
113
333
  await client.sendChatMessage({
@@ -119,25 +339,375 @@ async function invokeFeishuDeliveryOperation(handle, operation, input, client =
119
339
  content: message.content,
120
340
  uuid: config.uuid,
121
341
  });
122
- return { status: "sent", note: "sent Feishu chat message" };
342
+ return;
123
343
  }
124
344
  throw new Error(`deliver send only supports canonical Feishu text surfaces; use deliver invoke for ${handle.surface}`);
125
345
  }
346
+ function sentNote(handle, kind) {
347
+ return handle.surface === "message_reply"
348
+ ? `sent Feishu ${kind} message reply`
349
+ : `sent Feishu ${kind} chat message`;
350
+ }
351
+ function feishuFollowTemplateSpec() {
352
+ return [
353
+ {
354
+ templateId: "feishu.chat",
355
+ providerOrKind: "feishu",
356
+ label: "Feishu Chat",
357
+ description: "Follow messages from one Feishu/Lark chat.",
358
+ argsSchema: [
359
+ { name: "chatId", type: "string", required: true, description: "Feishu chat ID." },
360
+ ],
361
+ },
362
+ {
363
+ templateId: "feishu.mention",
364
+ providerOrKind: "feishu",
365
+ label: "Feishu Mention",
366
+ description: "Follow messages in one Feishu/Lark chat that mention a user or bot.",
367
+ argsSchema: [
368
+ { name: "chatId", type: "string", required: true, description: "Feishu chat ID." },
369
+ { name: "openId", type: "string", required: true, description: "Mentioned user or bot open_id." },
370
+ ],
371
+ },
372
+ {
373
+ templateId: "feishu.mentions",
374
+ providerOrKind: "feishu",
375
+ label: "Feishu Mentions",
376
+ description: "Follow messages across visible Feishu/Lark chats that mention a user or bot.",
377
+ argsSchema: [
378
+ { name: "openId", type: "string", required: false, description: "Mentioned user or bot open_id. Defaults to the configured app bot open_id." },
379
+ ],
380
+ },
381
+ ];
382
+ }
383
+ async function expandFeishuFollowTemplate(input, client = new FeishuUxcClient()) {
384
+ if (input.template !== "chat" && input.template !== "mention" && input.template !== "mentions") {
385
+ return null;
386
+ }
387
+ const args = input.args ?? {};
388
+ const chatId = asString(args.chatId);
389
+ if (input.template !== "mentions" && !chatId) {
390
+ throw new Error(`follow template feishu.${input.template} requires argument chatId`);
391
+ }
392
+ const config = parseFeishuSourceConfig(input.source);
393
+ const sourceKey = `feishu:${config.uxcAuth ?? input.source.configRef ?? "default"}:messages`;
394
+ const filter = chatId ? { metadata: { chatId } } : {};
395
+ let trackedResourceRef = chatId ? `chat:${chatId}` : "mentions";
396
+ if (input.template === "mention" || input.template === "mentions") {
397
+ const openId = asString(args.openId) ?? await resolveFeishuMentionOpenId(input, config, client);
398
+ if (!openId) {
399
+ throw new Error(`follow template feishu.${input.template} requires argument openId`);
400
+ }
401
+ filter.expr = `contains(metadata.mentionOpenIds, ${JSON.stringify(openId)})`;
402
+ trackedResourceRef = chatId ? `chat:${chatId}:mention:${openId}` : `mention:${openId}`;
403
+ }
404
+ return {
405
+ templateId: `feishu.${input.template}`,
406
+ sources: [
407
+ {
408
+ logicalName: "messages",
409
+ sourceType: "feishu_bot",
410
+ sourceKey,
411
+ configRef: input.source.configRef ?? null,
412
+ config: feishuFollowSourceConfig(input.source),
413
+ },
414
+ ],
415
+ subscriptions: [
416
+ {
417
+ sourceLogicalName: "messages",
418
+ filter,
419
+ trackedResourceRef,
420
+ cleanupPolicy: { mode: "manual" },
421
+ },
422
+ ],
423
+ };
424
+ }
425
+ async function resolveFeishuMentionOpenId(input, config, client) {
426
+ if (input.template !== "mentions") {
427
+ return null;
428
+ }
429
+ return client.getBotOpenId({
430
+ endpoint: config.endpoint,
431
+ auth: config.uxcAuth ?? input.source.configRef ?? undefined,
432
+ });
433
+ }
434
+ function feishuSourceOperations() {
435
+ return [
436
+ {
437
+ name: "get_message_context",
438
+ title: "Get Message Context",
439
+ inputSchema: {
440
+ type: "object",
441
+ additionalProperties: false,
442
+ required: ["messageId"],
443
+ properties: {
444
+ messageId: { type: "string", minLength: 1 },
445
+ chatId: { type: "string", minLength: 1 },
446
+ windowBefore: { type: "number", minimum: 0 },
447
+ windowAfter: { type: "number", minimum: 0 },
448
+ },
449
+ },
450
+ outputSchema: {
451
+ type: "object",
452
+ additionalProperties: true,
453
+ required: ["anchorMessage", "chatWindowMessages", "threadMessages", "warnings", "deliveryHandle"],
454
+ },
455
+ },
456
+ {
457
+ name: "list_recent_messages",
458
+ title: "List Recent Messages",
459
+ inputSchema: {
460
+ type: "object",
461
+ additionalProperties: false,
462
+ required: ["chatId"],
463
+ properties: {
464
+ chatId: { type: "string", minLength: 1 },
465
+ limit: { type: "number", minimum: 1, maximum: 50 },
466
+ before: { type: "string", minLength: 1 },
467
+ pageToken: { type: "string", minLength: 1 },
468
+ },
469
+ },
470
+ outputSchema: {
471
+ type: "object",
472
+ additionalProperties: true,
473
+ required: ["chatId", "messages", "warnings"],
474
+ },
475
+ },
476
+ {
477
+ name: "list_chats",
478
+ title: "List Chats",
479
+ inputSchema: {
480
+ type: "object",
481
+ additionalProperties: false,
482
+ properties: {
483
+ limit: { type: "number", minimum: 1, maximum: 50 },
484
+ pageToken: { type: "string", minLength: 1 },
485
+ },
486
+ },
487
+ outputSchema: {
488
+ type: "object",
489
+ additionalProperties: true,
490
+ required: ["chats", "warnings"],
491
+ },
492
+ },
493
+ {
494
+ name: "search_chats",
495
+ title: "Search Chats",
496
+ inputSchema: {
497
+ type: "object",
498
+ additionalProperties: false,
499
+ required: ["query"],
500
+ properties: {
501
+ query: { type: "string", minLength: 1 },
502
+ limit: { type: "number", minimum: 1, maximum: 50 },
503
+ pageToken: { type: "string", minLength: 1 },
504
+ },
505
+ },
506
+ outputSchema: {
507
+ type: "object",
508
+ additionalProperties: true,
509
+ required: ["chats", "warnings"],
510
+ },
511
+ },
512
+ {
513
+ name: "get_chat",
514
+ title: "Get Chat",
515
+ inputSchema: {
516
+ type: "object",
517
+ additionalProperties: false,
518
+ required: ["chatId"],
519
+ properties: {
520
+ chatId: { type: "string", minLength: 1 },
521
+ },
522
+ },
523
+ outputSchema: {
524
+ type: "object",
525
+ additionalProperties: true,
526
+ required: ["chat", "warnings"],
527
+ },
528
+ },
529
+ ];
530
+ }
531
+ async function invokeFeishuSourceOperation(source, operation, input, client = new FeishuUxcClient()) {
532
+ if (operation === "list_recent_messages") {
533
+ return listRecentFeishuMessages(source, input, client);
534
+ }
535
+ if (operation === "list_chats") {
536
+ return listFeishuChats(source, input, client);
537
+ }
538
+ if (operation === "search_chats") {
539
+ return searchFeishuChats(source, input, client);
540
+ }
541
+ if (operation === "get_chat") {
542
+ return getFeishuChat(source, input, client);
543
+ }
544
+ if (operation !== "get_message_context") {
545
+ throw new Error(`unknown Feishu source operation: ${operation}`);
546
+ }
547
+ const messageId = asString(input.messageId);
548
+ if (!messageId) {
549
+ throw new Error("get_message_context requires input.messageId");
550
+ }
551
+ const config = parseFeishuSourceConfig(source);
552
+ const anchorRaw = await client.getMessage({
553
+ endpoint: config.endpoint,
554
+ schemaUrl: config.schemaUrl,
555
+ auth: config.uxcAuth,
556
+ messageId,
557
+ });
558
+ const anchorMessage = normalizeFeishuMessage(anchorRaw);
559
+ const chatId = asString(input.chatId) ?? anchorMessage?.chatId ?? null;
560
+ const warnings = [];
561
+ let chatWindowMessages = [];
562
+ if (chatId) {
563
+ const windowBefore = positiveInteger(input.windowBefore) ?? 5;
564
+ const windowAfter = positiveInteger(input.windowAfter) ?? 5;
565
+ try {
566
+ chatWindowMessages = await fetchFeishuChatWindow({
567
+ client,
568
+ config,
569
+ chatId,
570
+ anchorMessage,
571
+ windowBefore,
572
+ windowAfter,
573
+ });
574
+ }
575
+ catch (error) {
576
+ warnings.push({
577
+ code: "chat_window_unavailable",
578
+ message: error instanceof Error ? error.message : String(error),
579
+ });
580
+ }
581
+ }
582
+ return {
583
+ anchorMessage,
584
+ chatWindowMessages,
585
+ threadMessages: [],
586
+ warnings,
587
+ deliveryHandle: {
588
+ provider: "feishu",
589
+ surface: "message_reply",
590
+ targetRef: messageId,
591
+ threadRef: anchorMessage?.threadId ?? anchorMessage?.parentId ?? null,
592
+ replyMode: "reply",
593
+ },
594
+ };
595
+ }
596
+ async function listFeishuChats(source, input, client) {
597
+ const config = parseFeishuSourceConfig(source);
598
+ const raw = await withFeishuChatDiscoveryError("list_chats", () => client.listChats({
599
+ endpoint: config.endpoint,
600
+ schemaUrl: config.schemaUrl,
601
+ auth: config.uxcAuth,
602
+ pageSize: boundedPositiveInteger(input.limit, 20, 50),
603
+ pageToken: asString(input.pageToken) ?? undefined,
604
+ }));
605
+ return feishuChatsResult(raw);
606
+ }
607
+ async function searchFeishuChats(source, input, client) {
608
+ const query = asString(input.query)?.trim();
609
+ if (!query) {
610
+ throw new Error("search_chats requires input.query");
611
+ }
612
+ const config = parseFeishuSourceConfig(source);
613
+ const limit = boundedPositiveInteger(input.limit, 20, 50);
614
+ const raw = await withFeishuChatDiscoveryError("search_chats", () => client.listChats({
615
+ endpoint: config.endpoint,
616
+ schemaUrl: config.schemaUrl,
617
+ auth: config.uxcAuth,
618
+ pageSize: 50,
619
+ pageToken: asString(input.pageToken) ?? undefined,
620
+ }));
621
+ return feishuChatsResult(raw, searchFeishuChatResults(normalizeFeishuChats(raw), query).slice(0, limit));
622
+ }
623
+ async function getFeishuChat(source, input, client) {
624
+ const chatId = asString(input.chatId);
625
+ if (!chatId) {
626
+ throw new Error("get_chat requires input.chatId");
627
+ }
628
+ const config = parseFeishuSourceConfig(source);
629
+ const raw = await withFeishuChatDiscoveryError("get_chat", () => client.getChat({
630
+ endpoint: config.endpoint,
631
+ schemaUrl: config.schemaUrl,
632
+ auth: config.uxcAuth,
633
+ chatId,
634
+ }));
635
+ return {
636
+ chat: normalizeFeishuChat(raw),
637
+ warnings: [],
638
+ };
639
+ }
640
+ async function withFeishuChatDiscoveryError(operation, run) {
641
+ try {
642
+ return await run();
643
+ }
644
+ catch (error) {
645
+ const message = error instanceof Error ? error.message : String(error);
646
+ throw new Error(`${operation} failed; ensure the configured Feishu UXC auth can read visible chats: ${message}`, { cause: error });
647
+ }
648
+ }
649
+ function feishuChatsResult(raw, chats = normalizeFeishuChats(raw)) {
650
+ const pageToken = feishuResponsePageToken(raw);
651
+ const hasMore = feishuResponseHasMore(raw);
652
+ return {
653
+ chats,
654
+ warnings: [],
655
+ ...(pageToken ? { pageToken } : {}),
656
+ ...(hasMore != null ? { hasMore } : {}),
657
+ };
658
+ }
659
+ function searchFeishuChatResults(chats, query) {
660
+ const needle = query.trim().toLowerCase();
661
+ if (!needle) {
662
+ return [];
663
+ }
664
+ return chats.filter((chat) => {
665
+ const fields = [chat.chatId, chat.name, chat.description, chat.type]
666
+ .filter((value) => Boolean(value))
667
+ .map((value) => value.toLowerCase());
668
+ return fields.some((value) => value.includes(needle));
669
+ });
670
+ }
671
+ async function listRecentFeishuMessages(source, input, client) {
672
+ const chatId = asString(input.chatId);
673
+ if (!chatId) {
674
+ throw new Error("list_recent_messages requires input.chatId");
675
+ }
676
+ const config = parseFeishuSourceConfig(source);
677
+ const limit = boundedPositiveInteger(input.limit, 20, 50);
678
+ const raw = await client.listMessages({
679
+ endpoint: config.endpoint,
680
+ schemaUrl: config.schemaUrl,
681
+ auth: config.uxcAuth,
682
+ chatId,
683
+ endTime: asString(input.before) ?? undefined,
684
+ pageToken: asString(input.pageToken) ?? undefined,
685
+ pageSize: limit,
686
+ sort: "ByCreateTimeDesc",
687
+ });
688
+ return {
689
+ chatId,
690
+ messages: normalizeFeishuMessages(raw),
691
+ warnings: [],
692
+ ...(feishuResponsePageToken(raw) ? { pageToken: feishuResponsePageToken(raw) } : {}),
693
+ ...(feishuResponseHasMore(raw) != null ? { hasMore: feishuResponseHasMore(raw) } : {}),
694
+ };
695
+ }
126
696
  function normalizeFeishuBotEvent(source, config, raw) {
127
697
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
128
698
  return null;
129
699
  }
130
700
  const payload = raw;
131
701
  const header = asRecord(payload.header);
132
- const eventType = asString(payload.event_type) ?? asString(header.event_type);
702
+ const eventType = asString(payload.event_type) ?? asString(payload.type) ?? asString(header.event_type);
133
703
  if (!eventType || !(config.eventTypes ?? exports.DEFAULT_FEISHU_EVENT_TYPES).includes(eventType)) {
134
704
  return null;
135
705
  }
136
706
  const event = asRecord(payload.event);
137
- const message = nonEmptyRecord(payload.message) ?? nonEmptyRecord(event.message) ?? {};
138
- const sender = nonEmptyRecord(payload.sender) ?? nonEmptyRecord(event.sender) ?? {};
707
+ const message = nonEmptyRecord(payload.message) ?? nonEmptyRecord(event.message) ?? flatFeishuMessage(payload);
708
+ const sender = nonEmptyRecord(payload.sender) ?? nonEmptyRecord(event.sender) ?? flatFeishuSender(payload);
139
709
  const messageId = asString(message.message_id);
140
- const eventId = asString(header.event_id) ?? messageId;
710
+ const eventId = asString(payload.event_id) ?? asString(header.event_id) ?? messageId;
141
711
  const chatId = asString(message.chat_id);
142
712
  if (!eventId || !messageId || !chatId) {
143
713
  return null;
@@ -148,7 +718,7 @@ function normalizeFeishuBotEvent(source, config, raw) {
148
718
  const mentions = extractMentionNames(message.mentions);
149
719
  const mentionOpenIds = extractMentionOpenIds(message.mentions);
150
720
  const messageType = asString(message.message_type) ?? "unknown";
151
- const senderId = asString(asRecord(sender.sender_id).open_id);
721
+ const senderId = asString(asRecord(sender.sender_id).open_id) ?? asString(sender.sender_id);
152
722
  const senderType = asString(sender.sender_type);
153
723
  const content = stringifyFeishuMessageContent(messageType, asString(message.content), message.mentions);
154
724
  const threadId = asString(message.thread_id) ?? asString(message.root_id);
@@ -191,6 +761,26 @@ function normalizeFeishuBotEvent(source, config, raw) {
191
761
  },
192
762
  };
193
763
  }
764
+ function flatFeishuMessage(payload) {
765
+ return {
766
+ message_id: payload.message_id,
767
+ chat_id: payload.chat_id,
768
+ chat_type: payload.chat_type,
769
+ message_type: payload.message_type ?? payload.msg_type,
770
+ content: payload.content,
771
+ create_time: payload.create_time,
772
+ mentions: payload.mentions,
773
+ thread_id: payload.thread_id,
774
+ root_id: payload.root_id,
775
+ parent_id: payload.parent_id,
776
+ };
777
+ }
778
+ function flatFeishuSender(payload) {
779
+ return {
780
+ sender_id: payload.sender_id,
781
+ sender_type: payload.sender_type,
782
+ };
783
+ }
194
784
  function parseFeishuSourceConfig(source) {
195
785
  const config = source.config ?? {};
196
786
  return {
@@ -231,6 +821,351 @@ function normalizeDeliveryMessage(payload) {
231
821
  content: JSON.stringify({ text: JSON.stringify(payload) }),
232
822
  };
233
823
  }
824
+ function commonDeliveryInputSchemaProperties() {
825
+ return {
826
+ endpoint: { type: "string", minLength: 1 },
827
+ schemaUrl: { type: "string", minLength: 1 },
828
+ schema_url: { type: "string", minLength: 1 },
829
+ uxcAuth: { type: "string", minLength: 1 },
830
+ auth: { type: "string", minLength: 1 },
831
+ replyInThread: { type: "boolean" },
832
+ reply_in_thread: { type: "boolean" },
833
+ uuid: { type: "string", minLength: 1 },
834
+ };
835
+ }
836
+ function normalizeFeishuFileMessage(fileKey) {
837
+ return {
838
+ msgType: "file",
839
+ content: JSON.stringify({ file_key: fileKey }),
840
+ };
841
+ }
842
+ function normalizeFeishuPostMessage(input) {
843
+ const rawContent = input.content;
844
+ if (typeof rawContent === "string" && rawContent.trim().length > 0) {
845
+ return { msgType: "post", content: rawContent };
846
+ }
847
+ if (rawContent && typeof rawContent === "object" && !Array.isArray(rawContent)) {
848
+ return { msgType: "post", content: JSON.stringify(rawContent) };
849
+ }
850
+ const paragraphs = normalizePostParagraphs(input);
851
+ if (paragraphs.length === 0) {
852
+ throw new Error("send_post requires input.blocks, input.paragraphs, or input.content");
853
+ }
854
+ const locale = asString(input.locale) ?? "zh_cn";
855
+ return {
856
+ msgType: "post",
857
+ content: JSON.stringify({
858
+ [locale]: {
859
+ title: asString(input.title) ?? "",
860
+ content: paragraphs,
861
+ },
862
+ }),
863
+ };
864
+ }
865
+ function normalizePostParagraphs(input) {
866
+ if (Array.isArray(input.paragraphs)) {
867
+ return input.paragraphs
868
+ .map((paragraph) => Array.isArray(paragraph) ? normalizePostBlocks(paragraph) : [])
869
+ .filter((paragraph) => paragraph.length > 0);
870
+ }
871
+ if (Array.isArray(input.blocks)) {
872
+ const paragraph = normalizePostBlocks(input.blocks);
873
+ return paragraph.length > 0 ? [paragraph] : [];
874
+ }
875
+ return [];
876
+ }
877
+ function normalizePostBlocks(blocks) {
878
+ return blocks.map((block) => normalizePostBlock(asRecord(block))).filter((block) => block !== null);
879
+ }
880
+ function normalizePostBlock(block) {
881
+ const type = asString(block.type) ?? "text";
882
+ if (type === "text") {
883
+ const text = asString(block.text);
884
+ if (!text) {
885
+ return null;
886
+ }
887
+ return { tag: "text", text };
888
+ }
889
+ if (type === "link") {
890
+ const text = asString(block.text);
891
+ const url = asString(block.url) ?? asString(block.href);
892
+ if (!text || !url) {
893
+ return null;
894
+ }
895
+ return { tag: "a", text, href: url };
896
+ }
897
+ if (type === "mention") {
898
+ const openId = asString(block.openId) ?? asString(block.open_id) ?? asString(block.userId) ?? asString(block.user_id);
899
+ if (!openId) {
900
+ return null;
901
+ }
902
+ return {
903
+ tag: "at",
904
+ user_id: openId,
905
+ ...(asString(block.name) ? { user_name: asString(block.name) } : {}),
906
+ };
907
+ }
908
+ return null;
909
+ }
910
+ function basename(filePath) {
911
+ const trimmed = filePath.replace(/\/+$/, "");
912
+ const parts = trimmed.split("/");
913
+ return parts[parts.length - 1] || "upload.bin";
914
+ }
915
+ function findStringField(value, field) {
916
+ if (!value || typeof value !== "object") {
917
+ return null;
918
+ }
919
+ const record = value;
920
+ const direct = asString(record[field]);
921
+ if (direct) {
922
+ return direct;
923
+ }
924
+ for (const child of Object.values(record)) {
925
+ const found = findStringField(child, field);
926
+ if (found) {
927
+ return found;
928
+ }
929
+ }
930
+ return null;
931
+ }
932
+ function feishuFollowSourceConfig(source) {
933
+ const config = parseFeishuSourceConfig(source);
934
+ return {
935
+ ...(config.endpoint && config.endpoint !== exports.FEISHU_OPENAPI_ENDPOINT ? { endpoint: config.endpoint } : {}),
936
+ ...(config.schemaUrl && config.schemaUrl !== exports.FEISHU_IM_SCHEMA_URL ? { schemaUrl: config.schemaUrl } : {}),
937
+ ...(config.uxcAuth ? { uxcAuth: config.uxcAuth } : {}),
938
+ ...(config.eventTypes ? { eventTypes: config.eventTypes } : {}),
939
+ };
940
+ }
941
+ function normalizeFeishuMessage(raw) {
942
+ const message = firstFeishuMessage(raw);
943
+ if (!message) {
944
+ return null;
945
+ }
946
+ const sender = asRecord(message.sender);
947
+ const messageType = asString(message.msg_type) ?? asString(message.message_type) ?? "unknown";
948
+ return {
949
+ messageId: asString(message.message_id) ?? "",
950
+ chatId: asString(message.chat_id),
951
+ chatType: asString(message.chat_type),
952
+ messageType,
953
+ senderOpenId: senderOpenId(sender),
954
+ senderType: asString(sender.sender_type) ?? asString(asRecord(sender.id).user_id) ?? null,
955
+ mentions: extractMentionNames(message.mentions),
956
+ mentionOpenIds: extractMentionOpenIds(message.mentions),
957
+ content: stringifyFeishuMessageContent(messageType, messageContentString(message), message.mentions),
958
+ createdAt: fromUnixMillisString(asString(message.create_time)),
959
+ threadId: asString(message.thread_id) ?? asString(message.root_id),
960
+ parentId: asString(message.parent_id),
961
+ raw: message,
962
+ };
963
+ }
964
+ function senderOpenId(sender) {
965
+ const nested = asString(asRecord(sender.id).open_id) ?? asString(asRecord(sender.sender_id).open_id);
966
+ if (nested) {
967
+ return nested;
968
+ }
969
+ const id = asString(sender.id) ?? asString(sender.sender_id);
970
+ const idType = asString(sender.id_type);
971
+ return id && (!idType || idType === "open_id") ? id : null;
972
+ }
973
+ function normalizeFeishuMessages(raw) {
974
+ const items = feishuMessageItems(raw);
975
+ return items
976
+ .map((item) => normalizeFeishuMessage(item))
977
+ .filter((item) => item !== null);
978
+ }
979
+ function normalizeFeishuChat(raw) {
980
+ const chat = firstFeishuChat(raw);
981
+ if (!chat) {
982
+ return null;
983
+ }
984
+ return {
985
+ chatId: asString(chat.chat_id) ?? "",
986
+ name: asString(chat.name) ?? asString(chat.chat_name),
987
+ description: asString(chat.description),
988
+ type: asString(chat.chat_type) ?? asString(chat.chat_mode) ?? asString(chat.type),
989
+ memberCount: asNumber(chat.member_count) ?? asNumber(chat.members_count) ?? asNumber(chat.user_count),
990
+ isBotMember: asBoolean(chat.is_bot_member) ?? asBoolean(chat.bot_member) ?? asBoolean(chat.is_member),
991
+ raw: chat,
992
+ };
993
+ }
994
+ function normalizeFeishuChats(raw) {
995
+ return feishuChatItems(raw)
996
+ .map((item) => normalizeFeishuChat(item))
997
+ .filter((item) => item !== null);
998
+ }
999
+ async function fetchFeishuChatWindow(input) {
1000
+ const anchorSeconds = feishuMessageCreateSeconds(input.anchorMessage);
1001
+ const pageSize = Math.max(1, Math.min(input.windowBefore + input.windowAfter + 1, 50));
1002
+ if (!anchorSeconds) {
1003
+ const fallbackRaw = await input.client.listMessages({
1004
+ endpoint: input.config.endpoint,
1005
+ schemaUrl: input.config.schemaUrl,
1006
+ auth: input.config.uxcAuth,
1007
+ chatId: input.chatId,
1008
+ pageSize,
1009
+ });
1010
+ return normalizeFeishuMessages(fallbackRaw);
1011
+ }
1012
+ const beforeRaw = input.windowBefore > 0
1013
+ ? await input.client.listMessages({
1014
+ endpoint: input.config.endpoint,
1015
+ schemaUrl: input.config.schemaUrl,
1016
+ auth: input.config.uxcAuth,
1017
+ chatId: input.chatId,
1018
+ startTime: String(Math.max(0, anchorSeconds - DEFAULT_CONTEXT_BOUND_SECONDS)),
1019
+ endTime: String(anchorSeconds + 1),
1020
+ pageSize: Math.max(1, Math.min(input.windowBefore + 1, 50)),
1021
+ sort: "ByCreateTimeDesc",
1022
+ })
1023
+ : null;
1024
+ const afterRaw = input.windowAfter > 0
1025
+ ? await input.client.listMessages({
1026
+ endpoint: input.config.endpoint,
1027
+ schemaUrl: input.config.schemaUrl,
1028
+ auth: input.config.uxcAuth,
1029
+ chatId: input.chatId,
1030
+ startTime: String(anchorSeconds),
1031
+ endTime: String(anchorSeconds + DEFAULT_CONTEXT_BOUND_SECONDS),
1032
+ pageSize: Math.max(1, Math.min(input.windowAfter + 1, 50)),
1033
+ sort: "ByCreateTimeAsc",
1034
+ })
1035
+ : null;
1036
+ return mergeFeishuMessages([
1037
+ ...(beforeRaw ? normalizeFeishuMessages(beforeRaw) : []),
1038
+ ...(input.anchorMessage ? [input.anchorMessage] : []),
1039
+ ...(afterRaw ? normalizeFeishuMessages(afterRaw) : []),
1040
+ ]);
1041
+ }
1042
+ function mergeFeishuMessages(messages) {
1043
+ const byId = new Map();
1044
+ for (const message of messages) {
1045
+ if (message.messageId) {
1046
+ byId.set(message.messageId, message);
1047
+ }
1048
+ }
1049
+ return Array.from(byId.values()).sort((left, right) => {
1050
+ const leftTime = feishuMessageCreateMillis(left) ?? 0;
1051
+ const rightTime = feishuMessageCreateMillis(right) ?? 0;
1052
+ return leftTime - rightTime;
1053
+ });
1054
+ }
1055
+ function feishuMessageCreateSeconds(message) {
1056
+ const millis = feishuMessageCreateMillis(message);
1057
+ return millis == null ? null : Math.floor(millis / 1000);
1058
+ }
1059
+ function feishuMessageCreateMillis(message) {
1060
+ if (!message) {
1061
+ return null;
1062
+ }
1063
+ const rawMillis = Number(asString(message.raw.create_time));
1064
+ if (Number.isFinite(rawMillis)) {
1065
+ return rawMillis;
1066
+ }
1067
+ if (message.createdAt) {
1068
+ const parsed = Date.parse(message.createdAt);
1069
+ return Number.isFinite(parsed) ? parsed : null;
1070
+ }
1071
+ return null;
1072
+ }
1073
+ function firstFeishuMessage(raw) {
1074
+ const items = feishuMessageItems(raw);
1075
+ if (items[0]) {
1076
+ return items[0];
1077
+ }
1078
+ const record = asRecord(raw);
1079
+ if (asString(record.message_id)) {
1080
+ return record;
1081
+ }
1082
+ return null;
1083
+ }
1084
+ function feishuMessageItems(raw) {
1085
+ const record = asRecord(raw);
1086
+ const data = asRecord(record.data);
1087
+ const nestedData = asRecord(data.data);
1088
+ const items = Array.isArray(nestedData.items)
1089
+ ? nestedData.items
1090
+ : Array.isArray(data.items)
1091
+ ? data.items
1092
+ : Array.isArray(record.items)
1093
+ ? record.items
1094
+ : Array.isArray(raw)
1095
+ ? raw
1096
+ : [];
1097
+ return items.map((item) => asRecord(item)).filter((item) => Object.keys(item).length > 0);
1098
+ }
1099
+ function firstFeishuChat(raw) {
1100
+ const items = feishuChatItems(raw);
1101
+ if (items[0]) {
1102
+ return items[0];
1103
+ }
1104
+ const record = asRecord(raw);
1105
+ const data = asRecord(record.data);
1106
+ const nestedData = asRecord(data.data);
1107
+ if (asString(nestedData.chat_id)) {
1108
+ return nestedData;
1109
+ }
1110
+ if (asString(data.chat_id)) {
1111
+ return data;
1112
+ }
1113
+ if (asString(record.chat_id)) {
1114
+ return record;
1115
+ }
1116
+ return null;
1117
+ }
1118
+ function feishuChatItems(raw) {
1119
+ const record = asRecord(raw);
1120
+ const data = asRecord(record.data);
1121
+ const nestedData = asRecord(data.data);
1122
+ const items = Array.isArray(nestedData.items)
1123
+ ? nestedData.items
1124
+ : Array.isArray(data.items)
1125
+ ? data.items
1126
+ : Array.isArray(record.items)
1127
+ ? record.items
1128
+ : Array.isArray(raw)
1129
+ ? raw
1130
+ : [];
1131
+ return items.map((item) => asRecord(item)).filter((item) => asString(item.chat_id));
1132
+ }
1133
+ function messageContentString(message) {
1134
+ const body = asRecord(message.body);
1135
+ return asString(body.content) ?? asString(message.content);
1136
+ }
1137
+ function positiveInteger(value) {
1138
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
1139
+ return null;
1140
+ }
1141
+ return value;
1142
+ }
1143
+ function boundedPositiveInteger(value, fallback, max) {
1144
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
1145
+ return fallback;
1146
+ }
1147
+ return Math.min(value, max);
1148
+ }
1149
+ function feishuResponsePageToken(raw) {
1150
+ const record = asRecord(raw);
1151
+ const data = asRecord(record.data);
1152
+ const nestedData = asRecord(data.data);
1153
+ return asString(nestedData.page_token)
1154
+ ?? asString(data.page_token)
1155
+ ?? asString(record.page_token);
1156
+ }
1157
+ function feishuResponseHasMore(raw) {
1158
+ const record = asRecord(raw);
1159
+ const data = asRecord(record.data);
1160
+ const nestedData = asRecord(data.data);
1161
+ if (typeof nestedData.has_more === "boolean") {
1162
+ return nestedData.has_more;
1163
+ }
1164
+ if (typeof data.has_more === "boolean") {
1165
+ return data.has_more;
1166
+ }
1167
+ return typeof record.has_more === "boolean" ? record.has_more : null;
1168
+ }
234
1169
  function extractMentionNames(raw) {
235
1170
  if (!Array.isArray(raw)) {
236
1171
  return [];
@@ -269,7 +1204,8 @@ function stringifyFeishuMessageContent(messageType, rawContent, mentions) {
269
1204
  return rawContent;
270
1205
  }
271
1206
  if (messageType === "text") {
272
- return asString(asRecord(parsed).text) ?? rawContent;
1207
+ const text = asString(asRecord(parsed).text) ?? rawContent;
1208
+ return replaceMentionKeys(text, mentions);
273
1209
  }
274
1210
  if (messageType === "post") {
275
1211
  const mentionMap = buildMentionMap(mentions);
@@ -303,6 +1239,13 @@ function buildMentionMap(raw) {
303
1239
  }
304
1240
  return map;
305
1241
  }
1242
+ function replaceMentionKeys(text, mentions) {
1243
+ let result = text;
1244
+ for (const [key, name] of buildMentionMap(mentions)) {
1245
+ result = result.split(key).join(name);
1246
+ }
1247
+ return result;
1248
+ }
306
1249
  function flattenPostContent(raw, mentionMap) {
307
1250
  const parsed = asRecord(raw);
308
1251
  const localeEntries = Object.entries(parsed);
@@ -369,9 +1312,18 @@ function asRecord(value) {
369
1312
  }
370
1313
  return value;
371
1314
  }
1315
+ function compactPayload(input) {
1316
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
1317
+ }
372
1318
  function asString(value) {
373
1319
  return typeof value === "string" ? value : null;
374
1320
  }
1321
+ function asNumber(value) {
1322
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1323
+ }
1324
+ function asBoolean(value) {
1325
+ return typeof value === "boolean" ? value : null;
1326
+ }
375
1327
  function asStringArray(value) {
376
1328
  if (!Array.isArray(value)) {
377
1329
  return null;
@@ -380,3 +1332,50 @@ function asStringArray(value) {
380
1332
  .map((item) => asString(item))
381
1333
  .filter((item) => Boolean(item));
382
1334
  }
1335
+ function ensureFeishuBotSchemaFile() {
1336
+ const body = JSON.stringify(FEISHU_BOT_OPENAPI_SCHEMA);
1337
+ (0, node_fs_1.writeFileSync)(FEISHU_BOT_SCHEMA_FILE, body, "utf8");
1338
+ return FEISHU_BOT_SCHEMA_FILE;
1339
+ }
1340
+ const FEISHU_BOT_OPENAPI_SCHEMA = {
1341
+ openapi: "3.0.3",
1342
+ info: {
1343
+ title: "Feishu Bot Info",
1344
+ version: "1.0.0",
1345
+ },
1346
+ servers: [
1347
+ {
1348
+ url: exports.FEISHU_OPENAPI_ENDPOINT,
1349
+ },
1350
+ ],
1351
+ // Feishu's docs site does not currently expose this as a stable
1352
+ // machine-readable OpenAPI document to UXC, so keep the tiny operation
1353
+ // schema needed for bot self-identification close to the adapter.
1354
+ paths: {
1355
+ "/bot/v3/info": {
1356
+ get: {
1357
+ operationId: "get:/bot/v3/info",
1358
+ summary: "Get bot information for the current app",
1359
+ responses: {
1360
+ "200": {
1361
+ description: "OK",
1362
+ },
1363
+ },
1364
+ security: [
1365
+ {
1366
+ bearerAuth: [],
1367
+ },
1368
+ ],
1369
+ },
1370
+ },
1371
+ },
1372
+ components: {
1373
+ securitySchemes: {
1374
+ bearerAuth: {
1375
+ type: "http",
1376
+ scheme: "bearer",
1377
+ bearerFormat: "tenant_access_token",
1378
+ },
1379
+ },
1380
+ },
1381
+ };