ylib-wecom-openclaw-plugin 2026.4.29

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 (180) hide show
  1. package/README.md +596 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +99 -0
  4. package/dist/src/accounts.d.ts +57 -0
  5. package/dist/src/accounts.js +247 -0
  6. package/dist/src/agent/api-client.d.ts +95 -0
  7. package/dist/src/agent/api-client.js +425 -0
  8. package/dist/src/agent/handler.d.ts +64 -0
  9. package/dist/src/agent/handler.js +731 -0
  10. package/dist/src/agent/index.d.ts +5 -0
  11. package/dist/src/agent/index.js +21 -0
  12. package/dist/src/agent/webhook.d.ts +25 -0
  13. package/dist/src/agent/webhook.js +294 -0
  14. package/dist/src/agent/xml.d.ts +21 -0
  15. package/dist/src/agent/xml.js +43 -0
  16. package/dist/src/channel.d.ts +5 -0
  17. package/dist/src/channel.js +815 -0
  18. package/dist/src/chat-queue.d.ts +31 -0
  19. package/dist/src/chat-queue.js +53 -0
  20. package/dist/src/config-schema.d.ts +587 -0
  21. package/dist/src/config-schema.js +146 -0
  22. package/dist/src/const.d.ts +128 -0
  23. package/dist/src/const.js +168 -0
  24. package/dist/src/dm-policy.d.ts +29 -0
  25. package/dist/src/dm-policy.js +146 -0
  26. package/dist/src/dynamic-agent.d.ts +37 -0
  27. package/dist/src/dynamic-agent.js +67 -0
  28. package/dist/src/dynamic-routing.d.ts +65 -0
  29. package/dist/src/dynamic-routing.js +62 -0
  30. package/dist/src/endpoint-dispatch.d.ts +54 -0
  31. package/dist/src/endpoint-dispatch.js +967 -0
  32. package/dist/src/endpoint-event-adapter.d.ts +15 -0
  33. package/dist/src/endpoint-event-adapter.js +427 -0
  34. package/dist/src/group-policy.d.ts +30 -0
  35. package/dist/src/group-policy.js +126 -0
  36. package/dist/src/http.d.ts +27 -0
  37. package/dist/src/http.js +168 -0
  38. package/dist/src/im-runtime-telemetry.d.ts +25 -0
  39. package/dist/src/im-runtime-telemetry.js +68 -0
  40. package/dist/src/interface.d.ts +192 -0
  41. package/dist/src/interface.js +5 -0
  42. package/dist/src/markdown-chunk.d.ts +1 -0
  43. package/dist/src/markdown-chunk.js +396 -0
  44. package/dist/src/mcp/index.d.ts +6 -0
  45. package/dist/src/mcp/index.js +28 -0
  46. package/dist/src/mcp/interceptors/biz-error.d.ts +11 -0
  47. package/dist/src/mcp/interceptors/biz-error.js +73 -0
  48. package/dist/src/mcp/interceptors/doc-auth-error.d.ts +10 -0
  49. package/dist/src/mcp/interceptors/doc-auth-error.js +235 -0
  50. package/dist/src/mcp/interceptors/index.d.ts +35 -0
  51. package/dist/src/mcp/interceptors/index.js +143 -0
  52. package/dist/src/mcp/interceptors/msg-media.d.ts +11 -0
  53. package/dist/src/mcp/interceptors/msg-media.js +201 -0
  54. package/dist/src/mcp/interceptors/smartpage-create.d.ts +30 -0
  55. package/dist/src/mcp/interceptors/smartpage-create.js +252 -0
  56. package/dist/src/mcp/interceptors/smartpage-export.d.ts +17 -0
  57. package/dist/src/mcp/interceptors/smartpage-export.js +135 -0
  58. package/dist/src/mcp/interceptors/smartsheet-upload.d.ts +22 -0
  59. package/dist/src/mcp/interceptors/smartsheet-upload.js +388 -0
  60. package/dist/src/mcp/interceptors/types.d.ts +64 -0
  61. package/dist/src/mcp/interceptors/types.js +8 -0
  62. package/dist/src/mcp/schema.d.ts +11 -0
  63. package/dist/src/mcp/schema.js +115 -0
  64. package/dist/src/mcp/tool.d.ts +63 -0
  65. package/dist/src/mcp/tool.js +318 -0
  66. package/dist/src/mcp/transport.d.ts +94 -0
  67. package/dist/src/mcp/transport.js +702 -0
  68. package/dist/src/media-handler.d.ts +55 -0
  69. package/dist/src/media-handler.js +306 -0
  70. package/dist/src/media-uploader.d.ts +142 -0
  71. package/dist/src/media-uploader.js +446 -0
  72. package/dist/src/message-parser.d.ts +104 -0
  73. package/dist/src/message-parser.js +232 -0
  74. package/dist/src/message-sender.d.ts +54 -0
  75. package/dist/src/message-sender.js +210 -0
  76. package/dist/src/monitor.d.ts +69 -0
  77. package/dist/src/monitor.js +1846 -0
  78. package/dist/src/onboarding.d.ts +8 -0
  79. package/dist/src/onboarding.js +248 -0
  80. package/dist/src/openclaw-compat.d.ts +148 -0
  81. package/dist/src/openclaw-compat.js +839 -0
  82. package/dist/src/proactive-markdown-send.d.ts +14 -0
  83. package/dist/src/proactive-markdown-send.js +205 -0
  84. package/dist/src/reqid-store.d.ts +23 -0
  85. package/dist/src/reqid-store.js +136 -0
  86. package/dist/src/runtime.d.ts +2 -0
  87. package/dist/src/runtime.js +7 -0
  88. package/dist/src/shared/command-auth.d.ts +23 -0
  89. package/dist/src/shared/command-auth.js +112 -0
  90. package/dist/src/shared/xml-parser.d.ts +46 -0
  91. package/dist/src/shared/xml-parser.js +228 -0
  92. package/dist/src/state-dir-resolve.d.ts +2 -0
  93. package/dist/src/state-dir-resolve.js +33 -0
  94. package/dist/src/state-manager.d.ts +115 -0
  95. package/dist/src/state-manager.js +413 -0
  96. package/dist/src/target.d.ts +35 -0
  97. package/dist/src/target.js +71 -0
  98. package/dist/src/template-card-manager.d.ts +55 -0
  99. package/dist/src/template-card-manager.js +316 -0
  100. package/dist/src/template-card-parser.d.ts +37 -0
  101. package/dist/src/template-card-parser.js +672 -0
  102. package/dist/src/timeout.d.ts +20 -0
  103. package/dist/src/timeout.js +57 -0
  104. package/dist/src/types/account.d.ts +29 -0
  105. package/dist/src/types/account.js +5 -0
  106. package/dist/src/types/config.d.ts +98 -0
  107. package/dist/src/types/config.js +8 -0
  108. package/dist/src/types/constants.d.ts +42 -0
  109. package/dist/src/types/constants.js +45 -0
  110. package/dist/src/types/index.d.ts +7 -0
  111. package/dist/src/types/index.js +17 -0
  112. package/dist/src/types/message.d.ts +238 -0
  113. package/dist/src/types/message.js +6 -0
  114. package/dist/src/utils.d.ts +148 -0
  115. package/dist/src/utils.js +92 -0
  116. package/dist/src/version.d.ts +2 -0
  117. package/dist/src/version.js +28 -0
  118. package/dist/src/webhook/command-auth.d.ts +47 -0
  119. package/dist/src/webhook/command-auth.js +137 -0
  120. package/dist/src/webhook/gateway.d.ts +36 -0
  121. package/dist/src/webhook/gateway.js +297 -0
  122. package/dist/src/webhook/handler.d.ts +19 -0
  123. package/dist/src/webhook/handler.js +481 -0
  124. package/dist/src/webhook/helpers.d.ts +157 -0
  125. package/dist/src/webhook/helpers.js +936 -0
  126. package/dist/src/webhook/http.d.ts +27 -0
  127. package/dist/src/webhook/http.js +168 -0
  128. package/dist/src/webhook/index.d.ts +11 -0
  129. package/dist/src/webhook/index.js +43 -0
  130. package/dist/src/webhook/media.d.ts +30 -0
  131. package/dist/src/webhook/media.js +152 -0
  132. package/dist/src/webhook/monitor.d.ts +59 -0
  133. package/dist/src/webhook/monitor.js +1672 -0
  134. package/dist/src/webhook/state.d.ts +220 -0
  135. package/dist/src/webhook/state.js +568 -0
  136. package/dist/src/webhook/target.d.ts +41 -0
  137. package/dist/src/webhook/target.js +165 -0
  138. package/dist/src/webhook/types.d.ts +348 -0
  139. package/dist/src/webhook/types.js +36 -0
  140. package/dist/src/webhook/video-frame.d.ts +13 -0
  141. package/dist/src/webhook/video-frame.js +108 -0
  142. package/openclaw.plugin.json +19 -0
  143. package/package.json +96 -0
  144. package/schema.json +534 -0
  145. package/scripts/generate-schema.mjs +33 -0
  146. package/skills/wecom-contact/SKILL.md +162 -0
  147. package/skills/wecom-doc/SKILL.md +162 -0
  148. package/skills/wecom-doc/references/create-doc.md +56 -0
  149. package/skills/wecom-doc/references/edit-doc-content.md +68 -0
  150. package/skills/wecom-doc/references/get-doc-content.md +88 -0
  151. package/skills/wecom-doc/references/smartpage-create.md +125 -0
  152. package/skills/wecom-doc/references/smartpage-export.md +160 -0
  153. package/skills/wecom-meeting/SKILL.md +441 -0
  154. package/skills/wecom-meeting/references/example-full.md +30 -0
  155. package/skills/wecom-meeting/references/example-reminder.md +46 -0
  156. package/skills/wecom-meeting/references/example-security.md +22 -0
  157. package/skills/wecom-meeting/references/response-get-meeting-info.md +148 -0
  158. package/skills/wecom-msg/SKILL.md +157 -0
  159. package/skills/wecom-msg/references/api-get-messages.md +93 -0
  160. package/skills/wecom-msg/references/api-get-msg-chat-list.md +58 -0
  161. package/skills/wecom-msg/references/api-get-msg-media.md +44 -0
  162. package/skills/wecom-msg/references/api-send-message.md +39 -0
  163. package/skills/wecom-preflight/SKILL.md +141 -0
  164. package/skills/wecom-schedule/SKILL.md +161 -0
  165. package/skills/wecom-schedule/references/api-check-availability.md +56 -0
  166. package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
  167. package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
  168. package/skills/wecom-schedule/references/api-update-schedule.md +32 -0
  169. package/skills/wecom-schedule/references/ref-reminders.md +24 -0
  170. package/skills/wecom-send-media/SKILL.md +68 -0
  171. package/skills/wecom-send-template-card/SKILL.md +157 -0
  172. package/skills/wecom-send-template-card/references/api-template-card-types.md +358 -0
  173. package/skills/wecom-smartsheet/SKILL.md +164 -0
  174. package/skills/wecom-smartsheet/references/smartsheet-cell-value-formats.md +163 -0
  175. package/skills/wecom-smartsheet/references/smartsheet-field-types.md +44 -0
  176. package/skills/wecom-smartsheet/references/smartsheet-get-records.md +96 -0
  177. package/skills/wecom-smartsheet/references/webhook-examples.md +185 -0
  178. package/skills/wecom-smartsheet/references/webhook-fallback.md +184 -0
  179. package/skills/wecom-todo/SKILL.md +392 -0
  180. package/skills/wecom-todo/examples/workflows.md +163 -0
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ /**
3
+ * 文档授权错误拦截器
4
+ *
5
+ * 当 category=doc 的 MCP 调用返回 errcode=851013/851014/851008(文档授权错误)时:
6
+ * 1. 通过 aibot_send_biz_msg 命令向用户发送授权引导卡片
7
+ * 2. 拦截 help_message 内容,不将其传递给 LLM
8
+ * 3. 返回简化响应,告知 LLM 授权卡片已发送、无需再做处理
9
+ */
10
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
11
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
12
+ return new (P || (P = Promise))(function (resolve, reject) {
13
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
14
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
15
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
16
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
17
+ });
18
+ };
19
+ var __generator = (this && this.__generator) || function (thisArg, body) {
20
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
21
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
22
+ function verb(n) { return function (v) { return step([n, v]); }; }
23
+ function step(op) {
24
+ if (f) throw new TypeError("Generator is already executing.");
25
+ while (_) try {
26
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
27
+ if (y = 0, t) op = [op[0] & 2, t.value];
28
+ switch (op[0]) {
29
+ case 0: case 1: t = op; break;
30
+ case 4: _.label++; return { value: op[1], done: false };
31
+ case 5: _.label++; y = op[1]; op = [0]; continue;
32
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
33
+ default:
34
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
35
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
36
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
37
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
38
+ if (t[2]) _.ops.pop();
39
+ _.trys.pop(); continue;
40
+ }
41
+ op = body.call(thisArg, _);
42
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
43
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
44
+ }
45
+ };
46
+ exports.__esModule = true;
47
+ exports.docAuthErrorInterceptor = void 0;
48
+ var aibot_node_sdk_1 = require("@wecom/aibot-node-sdk");
49
+ var state_manager_js_1 = require("../../state-manager.js");
50
+ var transport_js_1 = require("../transport.js");
51
+ var timeout_js_1 = require("../../timeout.js");
52
+ var const_js_1 = require("../../const.js");
53
+ // ============================================================================
54
+ // 常量
55
+ // ============================================================================
56
+ /** 需要拦截的文档授权错误码集合 */
57
+ /* https://doc.weixin.qq.com/sheet/e3_AFcARgbdAFwCNU0pwubawRtGzcd6z?scode=AJEAIQdfAAoEpqtRfRAMEA4QYkACk&tab=BB08J2 */
58
+ var DOC_AUTH_ERROR_CODES = new Set([851013, 851014, 851008]);
59
+ /**
60
+ * 业务消息类型枚举(对应 AiBotBizMsgType)
61
+ * - 1: 文档权限
62
+ */
63
+ var AIBOT_BIZ_MSG_TYPE_DOC_READ_AUTH = 1;
64
+ /**
65
+ * 聊天类型枚举(对应 AiBotBizMsgChatType)
66
+ * - 1: 单聊
67
+ * - 2: 群聊
68
+ */
69
+ var AIBOT_BIZ_MSG_CHAT_TYPE_SINGLE = 1;
70
+ var AIBOT_BIZ_MSG_CHAT_TYPE_GROUP = 2;
71
+ // ============================================================================
72
+ // 拦截器实现
73
+ // ============================================================================
74
+ exports.docAuthErrorInterceptor = {
75
+ name: "doc-auth-error",
76
+ /** 仅对 doc 品类生效 */
77
+ match: function (ctx) { return ctx.category === "doc"; },
78
+ /** 检测文档授权错误,发送授权引导卡片并拦截原始响应 */
79
+ afterCall: function (ctx, result) {
80
+ return __awaiter(this, void 0, void 0, function () {
81
+ return __generator(this, function (_a) {
82
+ return [2 /*return*/, interceptDocAuthError(ctx, result)];
83
+ });
84
+ });
85
+ }
86
+ };
87
+ // ============================================================================
88
+ // 内部实现
89
+ // ============================================================================
90
+ /**
91
+ * 从 MCP 返回结果中解析业务 JSON
92
+ */
93
+ function parseBizData(result) {
94
+ if (!result || typeof result !== "object")
95
+ return null;
96
+ var content = result.content;
97
+ if (!Array.isArray(content))
98
+ return null;
99
+ var textItem = content.find(function (c) { return c.type === "text" && typeof c.text === "string"; });
100
+ if (!textItem)
101
+ return null;
102
+ try {
103
+ return JSON.parse(textItem.text);
104
+ }
105
+ catch (_a) {
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * 通过 WSClient 发送 aibot_send_biz_msg 命令
111
+ *
112
+ * 按照 WwOpenWsSendAiBotBizMsgReq proto 定义构造请求体:
113
+ * - chat_id: 会话标识
114
+ * - userid: 发送者 userid
115
+ * - biz_type: 业务消息类型(AiBotBizMsgType)
116
+ * - chat_type: 聊天类型(AiBotBizMsgChatType,1=单聊,2=群聊)
117
+ */
118
+ function sendBizMsg(params) {
119
+ return __awaiter(this, void 0, void 0, function () {
120
+ var accountId, wsClient, reqId, body, err_1;
121
+ return __generator(this, function (_a) {
122
+ switch (_a.label) {
123
+ case 0:
124
+ accountId = params.accountId || transport_js_1.resolveCurrentAccountId();
125
+ wsClient = state_manager_js_1.getWeComWebSocket(accountId);
126
+ if (!wsClient) {
127
+ console.warn("[mcp] doc-auth-error: WSClient \u672A\u8FDE\u63A5 (accountId=\"" + accountId + "\")\uFF0C\u65E0\u6CD5\u53D1\u9001\u6388\u6743\u5361\u7247");
128
+ return [2 /*return*/];
129
+ }
130
+ reqId = aibot_node_sdk_1.generateReqId("biz_msg");
131
+ body = {
132
+ biz_type: AIBOT_BIZ_MSG_TYPE_DOC_READ_AUTH
133
+ };
134
+ if (params.chatId) {
135
+ body.chat_id = params.chatId;
136
+ }
137
+ if (params.userid) {
138
+ body.userid = params.userid;
139
+ }
140
+ if (params.chatType) {
141
+ body.chat_type = params.chatType === "group"
142
+ ? AIBOT_BIZ_MSG_CHAT_TYPE_GROUP
143
+ : AIBOT_BIZ_MSG_CHAT_TYPE_SINGLE;
144
+ }
145
+ console.log("[mcp] doc-auth-error: \u53D1\u9001 aibot_send_biz_msg, body=" + JSON.stringify(body));
146
+ _a.label = 1;
147
+ case 1:
148
+ _a.trys.push([1, 3, , 4]);
149
+ return [4 /*yield*/, timeout_js_1.withTimeout(wsClient.reply({ headers: { req_id: reqId } }, body, const_js_1.AIBOT_SEND_BIZ_MSG_CMD), const_js_1.BIZ_MSG_SEND_TIMEOUT_MS, "aibot_send_biz_msg timed out after " + const_js_1.BIZ_MSG_SEND_TIMEOUT_MS + "ms")];
150
+ case 2:
151
+ _a.sent();
152
+ console.log("[mcp] doc-auth-error: \u6388\u6743\u5361\u7247\u53D1\u9001\u6210\u529F (chatId=\"" + params.chatId + "\", userid=\"" + params.userid + "\", chatType=\"" + params.chatType + "\")");
153
+ return [3 /*break*/, 4];
154
+ case 3:
155
+ err_1 = _a.sent();
156
+ console.error("[mcp] doc-auth-error: \u6388\u6743\u5361\u7247\u53D1\u9001\u5931\u8D25: " + (err_1 instanceof Error ? err_1.message : JSON.stringify(err_1)));
157
+ return [3 /*break*/, 4];
158
+ case 4: return [2 /*return*/];
159
+ }
160
+ });
161
+ });
162
+ }
163
+ /**
164
+ * 拦截文档授权错误
165
+ *
166
+ * 检测 errcode=851013/851014/851008 时:
167
+ * 1. 通过 aibot_send_biz_msg 发送授权引导卡片(携带 chat_id、userid、chat_type)
168
+ * 2. 返回简化响应,告知 LLM 授权卡片已直接发送给用户
169
+ *
170
+ * 数据来源链路(chatId 全程保留原始大小写,单一事实来源):
171
+ *
172
+ * aibot_msg_callback.body.chatid (原始大小写)
173
+ * ↓
174
+ * monitor.ts:processWeComMessageNow
175
+ * → setSessionChatInfo(route.sessionKey, { chatId, chatType }) ← 以 sessionKey 为键
176
+ * ↓
177
+ * OpenClaw core 调用工具工厂,传入 ctx.sessionKey
178
+ * ↓
179
+ * index.ts:registerTool
180
+ * → getSessionChatInfo(ctx.sessionKey) 精确取回 { chatId, chatType }
181
+ * → createWeComMcpTool({ chatId, chatType })
182
+ * ↓
183
+ * tool.ts:handleCall 将其注入 CallContext
184
+ * ↓
185
+ * 本拦截器直接使用 ctx.chatId / ctx.chatType (无需反解 sessionKey、无需兜底)
186
+ *
187
+ * ⚠️ 切勿使用 parseSessionKeyChat(ctx.sessionKey) 反向解析 chatId:
188
+ * OpenClaw core 构建 sessionKey 时会将 peer.id 强制小写化,
189
+ * 企业微信 aibot_send_biz_msg 的 chat_id 是大小写敏感的,
190
+ * 小写后会收到 errcode=93006 (invalid chatid)。
191
+ */
192
+ function interceptDocAuthError(ctx, result) {
193
+ var _a;
194
+ return __awaiter(this, void 0, void 0, function () {
195
+ var bizData, errcode, userid, chatId, chatType, accountId, simplifiedResult;
196
+ return __generator(this, function (_b) {
197
+ switch (_b.label) {
198
+ case 0:
199
+ bizData = parseBizData(result);
200
+ if (!bizData)
201
+ return [2 /*return*/, result];
202
+ errcode = bizData.errcode;
203
+ if (typeof errcode !== "number" || !DOC_AUTH_ERROR_CODES.has(errcode)) {
204
+ return [2 /*return*/, result];
205
+ }
206
+ console.log("[mcp] doc-auth-error: \u68C0\u6D4B\u5230\u6587\u6863\u6388\u6743\u9519\u8BEF errcode=" + errcode + ", " +
207
+ ("method=" + ctx.method + ", category=" + ctx.category));
208
+ userid = ctx.requesterUserId, chatId = ctx.chatId, chatType = ctx.chatType, accountId = ctx.accountId;
209
+ if (!(!chatId || !chatType)) return [3 /*break*/, 1];
210
+ console.warn("[mcp] doc-auth-error: ctx \u7F3A\u5C11 chatId/chatType\uFF08\u53EF\u80FD\u4F1A\u8BDD\u4FE1\u606F\u672A\u8BB0\u5F55\uFF09\uFF0C" +
211
+ ("userid=\"" + userid + "\", chatId=\"" + chatId + "\", chatType=\"" + chatType + "\"\uFF0C\u8DF3\u8FC7\u53D1\u9001\u6388\u6743\u5361\u7247"));
212
+ return [3 /*break*/, 3];
213
+ case 1:
214
+ console.log("[mcp] doc-auth-error: \u53D1\u9001\u6388\u6743\u5361\u7247\u53C2\u6570: userid=\"" + userid + "\", chatId=\"" + chatId + "\", chatType=\"" + chatType + "\"");
215
+ return [4 /*yield*/, sendBizMsg({ chatId: chatId, userid: userid, chatType: chatType, accountId: accountId })];
216
+ case 2:
217
+ _b.sent();
218
+ _b.label = 3;
219
+ case 3:
220
+ simplifiedResult = {
221
+ errcode: errcode,
222
+ errmsg: (_a = bizData.errmsg) !== null && _a !== void 0 ? _a : "authorization error",
223
+ _biz_msg_sent: true,
224
+ _user_hint: "文档授权提示卡片已直接发送给用户,无需再向用户转述任何授权相关的信息。请告知用户:已发送授权引导,请按照提示完成授权后重试。"
225
+ };
226
+ return [2 /*return*/, {
227
+ content: [{
228
+ type: "text",
229
+ text: JSON.stringify(simplifiedResult)
230
+ }]
231
+ }];
232
+ }
233
+ });
234
+ });
235
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * MCP call 拦截器注册表与调度入口
3
+ *
4
+ * 所有 call 拦截器在此注册,按注册顺序执行。
5
+ * 新增拦截器只需:
6
+ * 1. 在 interceptors/ 目录下新建文件,实现 CallInterceptor 接口
7
+ * 2. 在下方 interceptors 数组中注册
8
+ *
9
+ * tool.ts 的 handleCall 无需任何改动。
10
+ */
11
+ import type { CallContext } from "./types.js";
12
+ import type { SendJsonRpcOptions } from "../transport.js";
13
+ export type { CallContext, CallInterceptor, BeforeCallOptions } from "./types.js";
14
+ /** resolveBeforeCall 的返回值 */
15
+ export interface ResolvedBeforeCall {
16
+ /** 合并后的 sendJsonRpc 选项(如超时时间) */
17
+ options?: SendJsonRpcOptions;
18
+ /** 替换后的 args(如从本地文件读取的请求体) */
19
+ args?: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * 收集匹配的 beforeCall 配置,合并后返回
23
+ *
24
+ * 合并策略:
25
+ * - timeoutMs: 取所有拦截器返回值中的最大值
26
+ * - args: 后注册的拦截器覆盖前者(一般同一调用只有一个拦截器会返回 args)
27
+ */
28
+ export declare function resolveBeforeCall(ctx: CallContext): Promise<ResolvedBeforeCall>;
29
+ /**
30
+ * 依次执行匹配的 afterCall 拦截器,管道式传递 result
31
+ *
32
+ * 前一个拦截器的返回值作为下一个拦截器的输入。
33
+ * 拦截器若不需要修改 result,应原样返回。
34
+ */
35
+ export declare function runAfterCall(ctx: CallContext, result: unknown): Promise<unknown>;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * MCP call 拦截器注册表与调度入口
4
+ *
5
+ * 所有 call 拦截器在此注册,按注册顺序执行。
6
+ * 新增拦截器只需:
7
+ * 1. 在 interceptors/ 目录下新建文件,实现 CallInterceptor 接口
8
+ * 2. 在下方 interceptors 数组中注册
9
+ *
10
+ * tool.ts 的 handleCall 无需任何改动。
11
+ */
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (_) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ exports.__esModule = true;
49
+ exports.runAfterCall = exports.resolveBeforeCall = void 0;
50
+ var biz_error_js_1 = require("./biz-error.js");
51
+ var doc_auth_error_js_1 = require("./doc-auth-error.js");
52
+ var msg_media_js_1 = require("./msg-media.js");
53
+ var smartpage_create_js_1 = require("./smartpage-create.js");
54
+ var smartpage_export_js_1 = require("./smartpage-export.js");
55
+ var smartsheet_upload_js_1 = require("./smartsheet-upload.js");
56
+ // ============================================================================
57
+ // 拦截器注册表(按注册顺序执行)
58
+ // ============================================================================
59
+ var interceptors = [
60
+ biz_error_js_1.bizErrorInterceptor,
61
+ doc_auth_error_js_1.docAuthErrorInterceptor,
62
+ msg_media_js_1.mediaInterceptor,
63
+ smartpage_create_js_1.smartpageCreateInterceptor,
64
+ smartpage_export_js_1.smartpageExportInterceptor,
65
+ smartsheet_upload_js_1.smartsheetUploadInterceptor,
66
+ ];
67
+ /**
68
+ * 收集匹配的 beforeCall 配置,合并后返回
69
+ *
70
+ * 合并策略:
71
+ * - timeoutMs: 取所有拦截器返回值中的最大值
72
+ * - args: 后注册的拦截器覆盖前者(一般同一调用只有一个拦截器会返回 args)
73
+ */
74
+ function resolveBeforeCall(ctx) {
75
+ return __awaiter(this, void 0, void 0, function () {
76
+ var mergedTimeoutMs, mergedArgs, _i, interceptors_1, interceptor, opts;
77
+ return __generator(this, function (_a) {
78
+ switch (_a.label) {
79
+ case 0:
80
+ _i = 0, interceptors_1 = interceptors;
81
+ _a.label = 1;
82
+ case 1:
83
+ if (!(_i < interceptors_1.length)) return [3 /*break*/, 4];
84
+ interceptor = interceptors_1[_i];
85
+ if (!interceptor.match(ctx) || !interceptor.beforeCall)
86
+ return [3 /*break*/, 3];
87
+ return [4 /*yield*/, interceptor.beforeCall(ctx)];
88
+ case 2:
89
+ opts = _a.sent();
90
+ if ((opts === null || opts === void 0 ? void 0 : opts.timeoutMs) !== undefined) {
91
+ mergedTimeoutMs = mergedTimeoutMs === undefined
92
+ ? opts.timeoutMs
93
+ : Math.max(mergedTimeoutMs, opts.timeoutMs);
94
+ }
95
+ if ((opts === null || opts === void 0 ? void 0 : opts.args) !== undefined) {
96
+ mergedArgs = opts.args;
97
+ }
98
+ _a.label = 3;
99
+ case 3:
100
+ _i++;
101
+ return [3 /*break*/, 1];
102
+ case 4: return [2 /*return*/, {
103
+ options: mergedTimeoutMs !== undefined ? { timeoutMs: mergedTimeoutMs } : undefined,
104
+ args: mergedArgs
105
+ }];
106
+ }
107
+ });
108
+ });
109
+ }
110
+ exports.resolveBeforeCall = resolveBeforeCall;
111
+ /**
112
+ * 依次执行匹配的 afterCall 拦截器,管道式传递 result
113
+ *
114
+ * 前一个拦截器的返回值作为下一个拦截器的输入。
115
+ * 拦截器若不需要修改 result,应原样返回。
116
+ */
117
+ function runAfterCall(ctx, result) {
118
+ return __awaiter(this, void 0, void 0, function () {
119
+ var current, _i, interceptors_2, interceptor;
120
+ return __generator(this, function (_a) {
121
+ switch (_a.label) {
122
+ case 0:
123
+ current = result;
124
+ _i = 0, interceptors_2 = interceptors;
125
+ _a.label = 1;
126
+ case 1:
127
+ if (!(_i < interceptors_2.length)) return [3 /*break*/, 4];
128
+ interceptor = interceptors_2[_i];
129
+ if (!interceptor.match(ctx) || !interceptor.afterCall)
130
+ return [3 /*break*/, 3];
131
+ return [4 /*yield*/, interceptor.afterCall(ctx, current)];
132
+ case 2:
133
+ current = _a.sent();
134
+ _a.label = 3;
135
+ case 3:
136
+ _i++;
137
+ return [3 /*break*/, 1];
138
+ case 4: return [2 /*return*/, current];
139
+ }
140
+ });
141
+ });
142
+ }
143
+ exports.runAfterCall = runAfterCall;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * get_msg_media 响应拦截器
3
+ *
4
+ * 核心逻辑:
5
+ * 1. beforeCall: 设置延长的超时时间(120s),因为 base64 数据可达 ~27MB
6
+ * 2. afterCall: 从 MCP result 的 content[].text 中提取 base64_data,
7
+ * 解码为 Buffer 并通过 saveMediaBuffer 保存到本地媒体目录,
8
+ * 替换响应中的 base64_data 为 local_path,避免大模型被 base64 数据消耗 token
9
+ */
10
+ import type { CallInterceptor } from "./types.js";
11
+ export declare const mediaInterceptor: CallInterceptor;
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * get_msg_media 响应拦截器
4
+ *
5
+ * 核心逻辑:
6
+ * 1. beforeCall: 设置延长的超时时间(120s),因为 base64 数据可达 ~27MB
7
+ * 2. afterCall: 从 MCP result 的 content[].text 中提取 base64_data,
8
+ * 解码为 Buffer 并通过 saveMediaBuffer 保存到本地媒体目录,
9
+ * 替换响应中的 base64_data 为 local_path,避免大模型被 base64 数据消耗 token
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || function (mod) {
24
+ if (mod && mod.__esModule) return mod;
25
+ var result = {};
26
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
+ __setModuleDefault(result, mod);
28
+ return result;
29
+ };
30
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
31
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
32
+ return new (P || (P = Promise))(function (resolve, reject) {
33
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
34
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
35
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
36
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
37
+ });
38
+ };
39
+ var __generator = (this && this.__generator) || function (thisArg, body) {
40
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
41
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
42
+ function verb(n) { return function (v) { return step([n, v]); }; }
43
+ function step(op) {
44
+ if (f) throw new TypeError("Generator is already executing.");
45
+ while (_) try {
46
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
47
+ if (y = 0, t) op = [op[0] & 2, t.value];
48
+ switch (op[0]) {
49
+ case 0: case 1: t = op; break;
50
+ case 4: _.label++; return { value: op[1], done: false };
51
+ case 5: _.label++; y = op[1]; op = [0]; continue;
52
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
53
+ default:
54
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
55
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
56
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
57
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
58
+ if (t[2]) _.ops.pop();
59
+ _.trys.pop(); continue;
60
+ }
61
+ op = body.call(thisArg, _);
62
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
63
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
64
+ }
65
+ };
66
+ exports.__esModule = true;
67
+ exports.mediaInterceptor = void 0;
68
+ var fs = __importStar(require("node:fs/promises"));
69
+ var path = __importStar(require("node:path"));
70
+ var transport_js_1 = require("../transport.js");
71
+ var runtime_js_1 = require("../../runtime.js");
72
+ var openclaw_compat_js_1 = require("../../openclaw-compat.js");
73
+ // ============================================================================
74
+ // 拦截器实现
75
+ // ============================================================================
76
+ exports.mediaInterceptor = {
77
+ name: "media",
78
+ /** 仅对 get_msg_media 方法生效 */
79
+ match: function (ctx) { return ctx.method === "get_msg_media"; },
80
+ /** 设置延长的超时时间 */
81
+ beforeCall: function () {
82
+ return { timeoutMs: transport_js_1.MEDIA_DOWNLOAD_TIMEOUT_MS };
83
+ },
84
+ /** 拦截响应:base64 → 本地文件 */
85
+ afterCall: function (ctx, result) {
86
+ return __awaiter(this, void 0, void 0, function () {
87
+ return __generator(this, function (_a) {
88
+ return [2 /*return*/, interceptMediaResponse(result)];
89
+ });
90
+ });
91
+ }
92
+ };
93
+ // ============================================================================
94
+ // 内部实现
95
+ // ============================================================================
96
+ /**
97
+ * 拦截 get_msg_media 的 MCP 响应
98
+ *
99
+ * 1. 从 MCP result 的 content[].text 中提取业务 JSON
100
+ * 2. 提取 media_item.base64_data,解码为 Buffer
101
+ * 3. 通过 openclaw SDK 的 saveMediaBuffer 保存到本地媒体目录
102
+ * 4. 替换响应:移除 base64_data,加入 local_path
103
+ *
104
+ * 这样大模型只看到轻量的文件路径信息,不会被 base64 数据消耗 token。
105
+ */
106
+ function interceptMediaResponse(result) {
107
+ var _a, _b;
108
+ return __awaiter(this, void 0, void 0, function () {
109
+ var t0, content, textItem, bizData, mediaItem, base64Data, mediaName, mediaType, mediaId, tParsed, buffer, tDecoded, contentType, tMimeDetected, INBOUND_MAX_BYTES, core, saved, MIME_EXT_PATCH, patchExt, newPath, _c, tSaved, newBizData, tEnd;
110
+ return __generator(this, function (_d) {
111
+ switch (_d.label) {
112
+ case 0:
113
+ t0 = performance.now();
114
+ content = (_a = result) === null || _a === void 0 ? void 0 : _a.content;
115
+ if (!Array.isArray(content))
116
+ return [2 /*return*/, result];
117
+ textItem = content.find(function (c) { return c.type === "text" && typeof c.text === "string"; });
118
+ if (!textItem)
119
+ return [2 /*return*/, result];
120
+ try {
121
+ bizData = JSON.parse(textItem.text);
122
+ }
123
+ catch (_e) {
124
+ // 非 JSON 格式,原样返回
125
+ return [2 /*return*/, result];
126
+ }
127
+ // 3. 校验业务响应:errcode !== 0 或无 media_item 时原样返回
128
+ if (bizData.errcode !== 0)
129
+ return [2 /*return*/, result];
130
+ mediaItem = bizData.media_item;
131
+ if (!mediaItem || typeof mediaItem.base64_data !== "string")
132
+ return [2 /*return*/, result];
133
+ base64Data = mediaItem.base64_data;
134
+ mediaName = mediaItem.name;
135
+ mediaType = mediaItem.type;
136
+ mediaId = mediaItem.media_id;
137
+ tParsed = performance.now();
138
+ buffer = Buffer.from(base64Data, "base64");
139
+ tDecoded = performance.now();
140
+ return [4 /*yield*/, openclaw_compat_js_1.detectMime({ buffer: buffer, filePath: mediaName })];
141
+ case 1:
142
+ contentType = (_b = _d.sent()) !== null && _b !== void 0 ? _b : "application/octet-stream";
143
+ tMimeDetected = performance.now();
144
+ INBOUND_MAX_BYTES = 20 * 1024 * 1024;
145
+ core = runtime_js_1.getWeComRuntime();
146
+ return [4 /*yield*/, core.channel.media.saveMediaBuffer(buffer, contentType, "inbound", INBOUND_MAX_BYTES, // maxBytes: 放宽到 20MB,匹配企业微信文件消息上限
147
+ mediaName)];
148
+ case 2:
149
+ saved = _d.sent();
150
+ MIME_EXT_PATCH = {
151
+ "audio/amr": ".amr"
152
+ };
153
+ patchExt = MIME_EXT_PATCH[contentType];
154
+ if (!(patchExt && !path.extname(saved.path))) return [3 /*break*/, 6];
155
+ newPath = saved.path + patchExt;
156
+ _d.label = 3;
157
+ case 3:
158
+ _d.trys.push([3, 5, , 6]);
159
+ return [4 /*yield*/, fs.rename(saved.path, newPath)];
160
+ case 4:
161
+ _d.sent();
162
+ saved.path = newPath;
163
+ return [3 /*break*/, 6];
164
+ case 5:
165
+ _c = _d.sent();
166
+ return [3 /*break*/, 6];
167
+ case 6:
168
+ tSaved = performance.now();
169
+ newBizData = {
170
+ errcode: 0,
171
+ errmsg: "ok",
172
+ media_item: {
173
+ media_id: mediaId,
174
+ name: mediaName !== null && mediaName !== void 0 ? mediaName : saved.path.split("/").pop(),
175
+ type: mediaType,
176
+ local_path: saved.path,
177
+ size: buffer.length,
178
+ content_type: saved.contentType
179
+ }
180
+ };
181
+ tEnd = performance.now();
182
+ // 耗时日志:各环节耗时(ms)
183
+ console.log("[mcp] get_msg_media \u62E6\u622A\u6210\u529F: media_id=" + (mediaId !== null && mediaId !== void 0 ? mediaId : "unknown") + ", " +
184
+ ("type=" + (mediaType !== null && mediaType !== void 0 ? mediaType : "unknown") + ", size=" + buffer.length + ", saved=" + saved.path + "\n") +
185
+ (" \u23F1 \u8017\u65F6\u660E\u7EC6 (\u603B " + (tEnd - t0).toFixed(1) + "ms):\n") +
186
+ (" \u89E3\u6790\u54CD\u5E94 JSON: " + (tParsed - t0).toFixed(1) + "ms\n") +
187
+ (" base64 \u89E3\u7801: " + (tDecoded - tParsed).toFixed(1) + "ms (" + (base64Data.length / 1024).toFixed(0) + "KB base64 \u2192 " + (buffer.length / 1024).toFixed(0) + "KB buffer)\n") +
188
+ (" MIME \u68C0\u6D4B: " + (tMimeDetected - tDecoded).toFixed(1) + "ms (" + contentType + ")\n") +
189
+ (" saveMediaBuffer: " + (tSaved - tMimeDetected).toFixed(1) + "ms\n") +
190
+ (" \u6784\u9020\u54CD\u5E94: " + (tEnd - tSaved).toFixed(1) + "ms"));
191
+ // 7. 返回修改后的 MCP result 结构
192
+ return [2 /*return*/, {
193
+ content: [{
194
+ type: "text",
195
+ text: JSON.stringify(newBizData)
196
+ }]
197
+ }];
198
+ }
199
+ });
200
+ });
201
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * smartpage_create 请求拦截器
3
+ *
4
+ * 核心逻辑:
5
+ * smartpage_create 的 pages 数组中,每个 page 可能包含 page_filepath 字段
6
+ * (指向本地 markdown 文件),用于避免在命令行传递大段文本内容。
7
+ * 本拦截器在 beforeCall 阶段遍历 pages 数组,逐个读取 page_filepath
8
+ * 指向的本地文件内容,填入 page_content 字段,并移除 page_filepath。
9
+ *
10
+ * 入参约定:
11
+ * wecom_mcp call doc smartpage_create '{
12
+ * "title": "主页标题",
13
+ * "pages": [
14
+ * {"page_title": "页面1", "page_filepath": "/tmp/page1.md", "content_type": "markdown"},
15
+ * {"page_title": "页面2", "page_filepath": "/tmp/page2.md", "content_type": "markdown"}
16
+ * ]
17
+ * }'
18
+ *
19
+ * 拦截器行为:
20
+ * 1. 检测 args.pages 数组
21
+ * 2. 校验文件大小:单文件不超过 10MB,所有文件总计不超过 20MB
22
+ * 3. 遍历每个 page,若存在 page_filepath 则读取本地文件内容
23
+ * 4. 将文件内容填入 page_content 字段,移除 page_filepath
24
+ * 5. 返回修改后的完整 args
25
+ *
26
+ * 传给 MCP Server 的格式:
27
+ * { "title": "...", "pages": [{"page_title": "...", "page_content": "...", "content_type": "..."}] }
28
+ */
29
+ import type { CallInterceptor } from "./types.js";
30
+ export declare const smartpageCreateInterceptor: CallInterceptor;