@lmcl/ailo-mcp-feishu 0.2.3 → 0.2.5

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.
@@ -42,7 +42,7 @@ export class FeishuHandler {
42
42
  get storage() {
43
43
  return this.ctx?.storage ?? null;
44
44
  }
45
- /** 通过 WS 发给 Aido 代打,ctx 未就绪时回退到 console */
45
+ /** 通过 WS 发给 Ailo 代打,ctx 未就绪时回退到 console */
46
46
  _log(level, message, data) {
47
47
  if (this.ctx?.log) {
48
48
  this.ctx.log(level, message, data);
@@ -515,7 +515,7 @@ export class FeishuHandler {
515
515
  * 收到飞书消息时,先调用此函数保存图片/视频/文件,再传给 LLM。
516
516
  * 失败返回 null,不传 ref。
517
517
  */
518
- async saveResourceToLocal(messageId, fileKey, resourceType, aidoType, fileName) {
518
+ async saveResourceToLocal(messageId, fileKey, resourceType, ailoType, fileName) {
519
519
  const workDir = getWorkDir() ?? path.join(os.tmpdir(), "ailo-mcp-feishu-blobs");
520
520
  const now = new Date();
521
521
  const cacheDir = path.join(workDir, "blobs", String(now.getFullYear()), String(now.getMonth() + 1).padStart(2, "0"));
@@ -534,7 +534,7 @@ export class FeishuHandler {
534
534
  catch {
535
535
  // messageResource 可能失败,图片可尝试 image.get
536
536
  }
537
- if (!buffer && resourceType === "image" && aidoType === "image") {
537
+ if (!buffer && resourceType === "image" && ailoType === "image") {
538
538
  try {
539
539
  const res = await this.client.im.v1.image.get({ path: { image_key: fileKey } });
540
540
  if (res?.getReadableStream)
@@ -552,13 +552,13 @@ export class FeishuHandler {
552
552
  /**
553
553
  * 使用飞书 WebSocket 长连接接收事件,无需配置回调地址。
554
554
  * 需在飞书开放平台「事件与回调」中选择「使用长连接接收事件」并保存(本客户端需在线)。
555
- * 必须 await 飞书连接就绪后返回,避免 Aido 在飞书未连接时就认为通道就绪导致消息丢失。
555
+ * 必须 await 飞书连接就绪后返回,避免 Ailo 在飞书未连接时就认为通道就绪导致消息丢失。
556
556
  */
557
557
  async start(ctx) {
558
558
  this.ctx = ctx;
559
559
  this.loadExternalUserLabels();
560
560
  this.fetchBotOpenId();
561
- // MCP stdio 占用 stdout,Lark SDK 日志通过 WS 发给 Aido 代打
561
+ // MCP stdio 占用 stdout,Lark SDK 日志通过 WS 发给 Ailo 代打
562
562
  const sink = (level) => (...args) => {
563
563
  const msg = args.map((a) => (typeof a === "object" ? JSON.stringify(a) : String(a))).join(" ");
564
564
  this._log(level, msg);
@@ -584,7 +584,7 @@ export class FeishuHandler {
584
584
  });
585
585
  // 启动定时清理过期缓存(每小时)
586
586
  const cacheCleanupInterval = setInterval(() => this.cleanExpiredCache(), 60 * 60 * 1000);
587
- // 必须 await 飞书连接就绪后再返回,否则 SDK 会在飞书未连接时就连到 Aido,导致启动窗口期消息丢失
587
+ // 必须 await 飞书连接就绪后再返回,否则 SDK 会在飞书未连接时就连到 Ailo,导致启动窗口期消息丢失
588
588
  const wsClientAny = wsClient;
589
589
  wsClientAny.eventDispatcher = eventDispatcher;
590
590
  eventDispatcher.register({
@@ -668,12 +668,12 @@ export class FeishuHandler {
668
668
  const content = JSON.parse(rawContent || "{}");
669
669
  const fileKey = content[mediaConfig.contentKey];
670
670
  if (fileKey) {
671
- const fileName = content["file_name"] ?? content["fileName"] ?? `${mediaConfig.aidoType}_${fileKey.slice(-12)}.${mediaConfig.aidoType === "image" ? "png" : mediaConfig.aidoType === "video" ? "mp4" : mediaConfig.aidoType === "audio" ? "mp3" : "bin"}`;
672
- const absPath = await this.saveResourceToLocal(messageId, fileKey, mediaConfig.resourceType, mediaConfig.aidoType, fileName);
671
+ const fileName = content["file_name"] ?? content["fileName"] ?? `${mediaConfig.ailoType}_${fileKey.slice(-12)}.${mediaConfig.ailoType === "image" ? "png" : mediaConfig.ailoType === "video" ? "mp4" : mediaConfig.ailoType === "audio" ? "mp3" : "bin"}`;
672
+ const absPath = await this.saveResourceToLocal(messageId, fileKey, mediaConfig.resourceType, mediaConfig.ailoType, fileName);
673
673
  if (absPath)
674
- attachments.push({ type: mediaConfig.aidoType, path: absPath, name: path.basename(absPath) });
674
+ attachments.push({ type: mediaConfig.ailoType, path: absPath, name: path.basename(absPath) });
675
675
  else
676
- text = `[无法获取${mediaConfig.aidoType}资源]`;
676
+ text = `[无法获取${mediaConfig.ailoType}资源]`;
677
677
  }
678
678
  else {
679
679
  text = "[无法解析的媒体消息]";
@@ -702,7 +702,7 @@ export class FeishuHandler {
702
702
  if (msg.parent_id) {
703
703
  text = `[回复消息 ${msg.parent_id}] ${text}`;
704
704
  }
705
- // 未知消息类型(卡片等)时若文本和附件均空,Aido 会拒绝;提供占位说明
705
+ // 未知消息类型(卡片等)时若文本和附件均空,Ailo 会拒绝;提供占位说明
706
706
  if (!text.trim() && attachments.length === 0) {
707
707
  text = messageType ? `[${messageType} 类型消息,暂不支持解析]` : "[未知类型消息]";
708
708
  }
@@ -878,6 +878,7 @@ export class FeishuHandler {
878
878
  },
879
879
  });
880
880
  await wsClientAny.reConnect(true);
881
+ this.ctx?.reportHealth("connected");
881
882
  }
882
883
  /**
883
884
  * 根据文件名/扩展名推断飞书 im.file.create 的 file_type。
@@ -1,9 +1,9 @@
1
1
  export const STALE_MESSAGE_THRESHOLD_MS = 5 * 60 * 1000;
2
2
  export const NEGATIVE_CACHE_TTL = 5 * 60 * 1000;
3
3
  export const MEDIA_MESSAGE_CONFIG = {
4
- image: { resourceType: "image", aidoType: "image", contentKey: "image_key" },
5
- file: { resourceType: "file", aidoType: "file", contentKey: "file_key" },
6
- audio: { resourceType: "audio", aidoType: "audio", contentKey: "file_key" },
7
- media: { resourceType: "video", aidoType: "video", contentKey: "file_key" },
8
- video: { resourceType: "video", aidoType: "video", contentKey: "file_key" },
4
+ image: { resourceType: "image", ailoType: "image", contentKey: "image_key" },
5
+ file: { resourceType: "file", ailoType: "file", contentKey: "file_key" },
6
+ audio: { resourceType: "audio", ailoType: "audio", contentKey: "file_key" },
7
+ media: { resourceType: "video", ailoType: "video", contentKey: "file_key" },
8
+ video: { resourceType: "video", ailoType: "video", contentKey: "file_key" },
9
9
  };
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ if (!FEISHU_APP_ID || !FEISHU_APP_SECRET) {
11
11
  }
12
12
  const handler = new FeishuHandler({ appId: FEISHU_APP_ID, appSecret: FEISHU_APP_SECRET });
13
13
  handler.setOnBotRemovedFromGroup(() => { });
14
- function feishuBuildChannelPrompt() {
14
+ function feishuBuildChannelInstructions() {
15
15
  return `ID 格式:ou_xxx 是飞书用户 ID,oc_xxx 是群组 ID。
16
16
 
17
17
  @提及:@提及格式为 @显示名(ou_xxx)。使用此格式可触发飞书强提醒。
@@ -27,6 +27,6 @@ runMcpChannel({
27
27
  handler,
28
28
  displayName: "飞书",
29
29
  defaultRequiresResponse: true,
30
- buildChannelPrompt: feishuBuildChannelPrompt,
30
+ buildChannelInstructions: feishuBuildChannelInstructions,
31
31
  mcpServer,
32
32
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmcl/ailo-mcp-feishu",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Ailo 飞书/Lark 通道 MCP",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "@larksuiteoapi/node-sdk": "^1.56.1",
22
- "@lmcl/ailo-mcp-sdk": "^0.2.3",
22
+ "@lmcl/ailo-mcp-sdk": "file:../ailo-mcp-sdk",
23
23
  "@modelcontextprotocol/sdk": "^1.26.0",
24
24
  "dotenv": "^16.4.5",
25
25
  "form-data": "^4.0.0",