@yanhaidao/wecom 2.3.150 → 2.3.180

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 (43) hide show
  1. package/README.md +238 -385
  2. package/SKILLS_CAL.md +895 -0
  3. package/SKILLS_DOC.md +2136 -0
  4. package/changelog/v2.3.16.md +11 -0
  5. package/changelog/v2.3.18.md +22 -0
  6. package/index.ts +39 -3
  7. package/package.json +2 -3
  8. package/src/agent/handler.event-filter.test.ts +11 -0
  9. package/src/agent/handler.ts +732 -643
  10. package/src/app/account-runtime.ts +46 -20
  11. package/src/app/index.ts +19 -1
  12. package/src/capability/calendar/SKILLS_CHECKLIST.md +251 -0
  13. package/src/capability/calendar/client.ts +815 -0
  14. package/src/capability/calendar/index.ts +3 -0
  15. package/src/capability/calendar/schema.ts +417 -0
  16. package/src/capability/calendar/tool.ts +417 -0
  17. package/src/capability/calendar/types.ts +309 -0
  18. package/src/capability/doc/client.ts +567 -62
  19. package/src/capability/doc/schema.ts +419 -318
  20. package/src/capability/doc/tool.ts +1510 -1178
  21. package/src/capability/doc/types.ts +130 -14
  22. package/src/capability/mcp/index.ts +10 -0
  23. package/src/capability/mcp/schema.ts +107 -0
  24. package/src/capability/mcp/tool.ts +170 -0
  25. package/src/capability/mcp/transport.ts +394 -0
  26. package/src/channel.ts +70 -28
  27. package/src/config/schema.ts +71 -102
  28. package/src/outbound.test.ts +91 -14
  29. package/src/outbound.ts +143 -30
  30. package/src/runtime/reply-orchestrator.test.ts +35 -2
  31. package/src/runtime/reply-orchestrator.ts +14 -2
  32. package/src/runtime/session-manager.ts +20 -6
  33. package/src/runtime/source-registry.ts +165 -0
  34. package/src/target.ts +7 -4
  35. package/src/transport/bot-ws/inbound.test.ts +46 -0
  36. package/src/transport/bot-ws/inbound.ts +23 -5
  37. package/src/transport/bot-ws/media.ts +269 -0
  38. package/src/transport/bot-ws/reply.test.ts +85 -17
  39. package/src/transport/bot-ws/reply.ts +109 -21
  40. package/src/transport/bot-ws/sdk-adapter.test.ts +64 -1
  41. package/src/transport/bot-ws/sdk-adapter.ts +88 -12
  42. package/.claude/settings.local.json +0 -11
  43. package/docs/update-content-fix.md +0 -135
@@ -1,1290 +1,1622 @@
1
1
  import fs from "node:fs";
2
2
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
3
- import { wecomDocToolSchema } from "./schema.js";
4
- import { WecomDocClient } from "./client.js";
3
+ import { isWecomAgentSource } from "../../runtime/source-registry.js";
5
4
  import type { ResolvedAgentAccount } from "../../types/index.js";
6
5
  import { resolveAgentAccountOrUndefined } from "../bot/fallback-delivery.js";
7
-
6
+ import { WecomDocClient } from "./client.js";
7
+ import { wecomDocToolSchema } from "./schema.js";
8
8
  import { UpdateRequest } from "./types.js";
9
9
 
10
10
  function readString(value: unknown): string {
11
- const trimmed = String(value ?? "").trim();
12
- return trimmed || "";
11
+ const trimmed = String(value ?? "").trim();
12
+ return trimmed || "";
13
13
  }
14
14
 
15
15
  function mapDocTypeLabel(docType: number): string {
16
- if (docType === 10) return "智能表格";
17
- return docType === 4 ? "表格" : "文档";
16
+ if (docType === 10) return "智能表格";
17
+ return docType === 4 ? "表格" : "文档";
18
18
  }
19
19
 
20
20
  function summarizeDocInfo(info: any = {}) {
21
- const docName = readString(info.doc_name) || "未命名文档";
22
- const docType = mapDocTypeLabel(Number(info.doc_type));
23
- return `${docType}"${docName}"信息已获取`;
21
+ const docName = readString(info.doc_name) || "未命名文档";
22
+ const docType = mapDocTypeLabel(Number(info.doc_type));
23
+ return `${docType}"${docName}"信息已获取`;
24
24
  }
25
25
 
26
26
  function summarizeDocAuth(result: any = {}) {
27
- return `权限信息已获取:通知成员 ${result.docMembers?.length ?? 0},协作者 ${result.coAuthList?.length ?? 0}`;
27
+ return `权限信息已获取:通知成员 ${result.docMembers?.length ?? 0},协作者 ${result.coAuthList?.length ?? 0}`;
28
28
  }
29
29
 
30
30
  function readBooleanFlag(value: unknown): boolean | null {
31
- return typeof value === "boolean" ? value : null;
31
+ return typeof value === "boolean" ? value : null;
32
32
  }
33
33
 
34
34
  function formatDocMemberRef(value: any) {
35
- if (!value || typeof value !== "object" || Array.isArray(value)) return "";
36
- const userid = readString(value.userid ?? value.userId);
37
- if (userid) return `userid:${userid}`;
38
- const partyid = readString(value.partyid);
39
- if (partyid) return `partyid:${partyid}`;
40
- const tagid = readString(value.tagid);
41
- if (tagid) return `tagid:${tagid}`;
42
- return "";
35
+ if (!value || typeof value !== "object" || Array.isArray(value)) return "";
36
+ const userid = readString(value.userid ?? value.userId);
37
+ if (userid) return `userid:${userid}`;
38
+ const partyid = readString(value.partyid);
39
+ if (partyid) return `partyid:${partyid}`;
40
+ const tagid = readString(value.tagid);
41
+ if (tagid) return `tagid:${tagid}`;
42
+ return "";
43
43
  }
44
44
 
45
45
  function mapDocMemberList(values: any) {
46
- return Array.isArray(values)
47
- ? values.map((item) => formatDocMemberRef(item)).filter(Boolean)
48
- : [];
46
+ return Array.isArray(values)
47
+ ? values.map((item) => formatDocMemberRef(item)).filter(Boolean)
48
+ : [];
49
49
  }
50
50
 
51
- function describeFlagState(value: boolean | null, enabledLabel: string, disabledLabel: string, unknownLabel = "未知") {
52
- if (value === true) return enabledLabel;
53
- if (value === false) return disabledLabel;
54
- return unknownLabel;
51
+ function describeFlagState(
52
+ value: boolean | null,
53
+ enabledLabel: string,
54
+ disabledLabel: string,
55
+ unknownLabel = "未知",
56
+ ) {
57
+ if (value === true) return enabledLabel;
58
+ if (value === false) return disabledLabel;
59
+ return unknownLabel;
55
60
  }
56
61
 
57
62
  function buildDocAuthDiagnosis(result: any = {}, requesterSenderId = "") {
58
- const accessRule = result.accessRule && typeof result.accessRule === "object" ? result.accessRule : {};
59
- const viewers = mapDocMemberList(result.docMembers);
60
- const collaborators = mapDocMemberList(result.coAuthList);
61
- const requester = readString(requesterSenderId);
62
- const requesterViewerRef = requester ? `userid:${requester}` : "";
63
- const requesterIsViewer = requesterViewerRef ? viewers.includes(requesterViewerRef) : false;
64
- const requesterIsCollaborator = requesterViewerRef ? collaborators.includes(requesterViewerRef) : false;
65
- const internalAccessEnabled = readBooleanFlag(accessRule.enable_corp_internal);
66
- const externalAccessEnabled = readBooleanFlag(accessRule.enable_corp_external);
67
- const externalShareAllowed = typeof accessRule.ban_share_external === "boolean"
68
- ? !accessRule.ban_share_external
69
- : null;
70
- const likelyAnonymousLinkFailure = internalAccessEnabled === true && externalAccessEnabled === false;
71
- const findings = [
72
- `企业内访问:${describeFlagState(internalAccessEnabled, "开启", "关闭")}`,
73
- `企业外访问:${describeFlagState(externalAccessEnabled, "开启", "关闭")}`,
74
- `外部分享:${describeFlagState(externalShareAllowed, "允许", "禁止")}`,
75
- `查看成员:${viewers.length}`,
76
- `协作者:${collaborators.length}`,
77
- ];
78
- const recommendations: string[] = [];
79
- if (likelyAnonymousLinkFailure) {
80
- recommendations.push('当前更像是仅企业内可访问;匿名浏览器或未登录企业微信环境通常会显示"文档不存在"。');
81
- }
82
- if (requester) {
83
- if (requesterIsCollaborator) {
84
- recommendations.push(`当前请求人 ${requester} 已在协作者列表中。`);
85
- } else if (requesterIsViewer) {
86
- recommendations.push(`当前请求人 ${requester} 已在查看成员列表中,但还不是协作者。`);
87
- } else {
88
- recommendations.push(`当前请求人 ${requester} 不在查看成员或协作者列表中。`);
89
- }
63
+ const accessRule =
64
+ result.accessRule && typeof result.accessRule === "object" ? result.accessRule : {};
65
+ const viewers = mapDocMemberList(result.docMembers);
66
+ const collaborators = mapDocMemberList(result.coAuthList);
67
+ const requester = readString(requesterSenderId);
68
+ const requesterViewerRef = requester ? `userid:${requester}` : "";
69
+ const requesterIsViewer = requesterViewerRef ? viewers.includes(requesterViewerRef) : false;
70
+ const requesterIsCollaborator = requesterViewerRef
71
+ ? collaborators.includes(requesterViewerRef)
72
+ : false;
73
+ const internalAccessEnabled = readBooleanFlag(accessRule.enable_corp_internal);
74
+ const externalAccessEnabled = readBooleanFlag(accessRule.enable_corp_external);
75
+ const externalShareAllowed =
76
+ typeof accessRule.ban_share_external === "boolean" ? !accessRule.ban_share_external : null;
77
+ const likelyAnonymousLinkFailure =
78
+ internalAccessEnabled === true && externalAccessEnabled === false;
79
+ const findings = [
80
+ `企业内访问:${describeFlagState(internalAccessEnabled, "开启", "关闭")}`,
81
+ `企业外访问:${describeFlagState(externalAccessEnabled, "开启", "关闭")}`,
82
+ `外部分享:${describeFlagState(externalShareAllowed, "允许", "禁止")}`,
83
+ `查看成员:${viewers.length}`,
84
+ `协作者:${collaborators.length}`,
85
+ ];
86
+ const recommendations: string[] = [];
87
+ if (likelyAnonymousLinkFailure) {
88
+ recommendations.push(
89
+ '当前更像是仅企业内可访问;匿名浏览器或未登录企业微信环境通常会显示"文档不存在"。',
90
+ );
91
+ }
92
+ if (requester) {
93
+ if (requesterIsCollaborator) {
94
+ recommendations.push(`当前请求人 ${requester} 已在协作者列表中。`);
95
+ } else if (requesterIsViewer) {
96
+ recommendations.push(`当前请求人 ${requester} 已在查看成员列表中,但还不是协作者。`);
97
+ } else {
98
+ recommendations.push(`当前请求人 ${requester} 不在查看成员或协作者列表中。`);
90
99
  }
91
- return {
92
- internalAccessEnabled,
93
- externalAccessEnabled,
94
- externalShareAllowed,
95
- viewerCount: viewers.length,
96
- collaboratorCount: collaborators.length,
97
- viewers,
98
- collaborators,
99
- requesterSenderId: requester || undefined,
100
- requesterRole: requesterIsCollaborator ? "collaborator" : requesterIsViewer ? "viewer" : requester ? "none" : "unknown",
101
- likelyAnonymousLinkFailure,
102
- findings,
103
- recommendations,
104
- };
100
+ }
101
+ return {
102
+ internalAccessEnabled,
103
+ externalAccessEnabled,
104
+ externalShareAllowed,
105
+ viewerCount: viewers.length,
106
+ collaboratorCount: collaborators.length,
107
+ viewers,
108
+ collaborators,
109
+ requesterSenderId: requester || undefined,
110
+ requesterRole: requesterIsCollaborator
111
+ ? "collaborator"
112
+ : requesterIsViewer
113
+ ? "viewer"
114
+ : requester
115
+ ? "none"
116
+ : "unknown",
117
+ likelyAnonymousLinkFailure,
118
+ findings,
119
+ recommendations,
120
+ };
105
121
  }
106
122
 
107
123
  function summarizeDocAuthDiagnosis(diagnosis: any = {}) {
108
- const parts = Array.isArray(diagnosis.findings) ? diagnosis.findings : [];
109
- return parts.length > 0 ? `文档权限诊断:${parts.join(",")}` : "文档权限诊断已完成";
124
+ const parts = Array.isArray(diagnosis.findings) ? diagnosis.findings : [];
125
+ return parts.length > 0 ? `文档权限诊断:${parts.join(",")}` : "文档权限诊断已完成";
110
126
  }
111
127
 
112
128
  function buildDocIdUsageHint(docId?: string) {
113
- const normalizedDocId = readString(docId);
114
- if (!normalizedDocId) return "";
115
- return `后续权限、分享和诊断操作请使用真实 docId:${normalizedDocId};不要直接使用分享链接路径中的片段。`;
129
+ const normalizedDocId = readString(docId);
130
+ if (!normalizedDocId) return "";
131
+ return `后续权限、分享和诊断操作请使用真实 docId:${normalizedDocId};不要直接使用分享链接路径中的片段。`;
116
132
  }
117
133
 
118
134
  function safeParseJson(text: string) {
119
- try {
120
- return JSON.parse(text);
121
- } catch {
122
- return null;
123
- }
135
+ try {
136
+ return JSON.parse(text);
137
+ } catch {
138
+ return null;
139
+ }
124
140
  }
125
141
 
126
142
  function extractEmbeddedJson(html: string, variableName: string) {
127
- const source = String(html ?? "");
128
- if (!source) return null;
129
- const marker = `window.${variableName}=`;
130
- const start = source.indexOf(marker);
131
- if (start < 0) return null;
132
- const valueStart = start + marker.length;
133
- const end = source.indexOf(";</script>", valueStart);
134
- if (end < 0) return null;
135
- return safeParseJson(source.slice(valueStart, end));
143
+ const source = String(html ?? "");
144
+ if (!source) return null;
145
+ const marker = `window.${variableName}=`;
146
+ const start = source.indexOf(marker);
147
+ if (start < 0) return null;
148
+ const valueStart = start + marker.length;
149
+ const end = source.indexOf(";</script>", valueStart);
150
+ if (end < 0) return null;
151
+ return safeParseJson(source.slice(valueStart, end));
136
152
  }
137
153
 
138
- function buildShareLinkDiagnosis(params: { shareUrl: string; finalUrl: string; status: number; contentType: string; basicClientVars: any }) {
139
- const { shareUrl, finalUrl, status, contentType, basicClientVars } = params;
140
- const parsedUrl = new URL(finalUrl || shareUrl);
141
- const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
142
- const pathResourceType = readString(pathSegments[0]);
143
- const pathResourceId = readString(pathSegments[1]);
144
- const shareCode = readString(parsedUrl.searchParams.get("scode"));
145
- const userInfo = basicClientVars?.userInfo && typeof basicClientVars.userInfo === "object"
146
- ? basicClientVars.userInfo
147
- : {};
148
- const docInfo = basicClientVars?.docInfo && typeof basicClientVars.docInfo === "object"
149
- ? basicClientVars.docInfo
150
- : {};
151
- const padInfo = docInfo?.padInfo && typeof docInfo.padInfo === "object"
152
- ? docInfo.padInfo
153
- : {};
154
- const ownerInfo = docInfo?.ownerInfo && typeof docInfo.ownerInfo === "object"
155
- ? docInfo.ownerInfo
156
- : {};
157
- const shareInfo = docInfo?.shareInfo && typeof docInfo.shareInfo === "object"
158
- ? docInfo.shareInfo
159
- : {};
160
- const aclInfo = docInfo?.aclInfo && typeof docInfo.aclInfo === "object"
161
- ? docInfo.aclInfo
162
- : {};
163
- const userType = readString(userInfo.userType);
164
- const padType = readString(padInfo.padType);
165
- const padId = readString(padInfo.padId);
166
- const padTitle = readString(padInfo.padTitle);
167
- const isGuest = userType === "guest" || Number(userInfo.loginType) === 0;
168
- const isBlankPage = padType === "blankpage";
169
- const likelyUnavailableToGuest = isGuest && isBlankPage && !padTitle;
170
- const findings = [
171
- `HTTP ${String(status || "")}`.trim(),
172
- `内容类型:${readString(contentType) || "未知"}`,
173
- `访问身份:${userType || "未知"}`,
174
- `页面类型:${padType || "未知"}`,
175
- `路径资源:${pathResourceType || "未知"} / ${pathResourceId || "未知"}`,
176
- ];
177
- const recommendations: string[] = [];
178
- if (likelyUnavailableToGuest) {
179
- recommendations.push('当前链接对 guest/未登录企业微信环境返回 blankpage,外部访问会表现为打不开或像"文档不存在"。');
180
- }
181
- if (shareCode) {
182
- recommendations.push(`当前链接带有分享码 scode=${shareCode}。如分享码过期或未生效,外部访问会失败。`);
183
- }
184
- if (pathResourceId && padId && pathResourceId !== padId) {
185
- recommendations.push(`链接路径中的资源标识与页面 padId 不一致:path=${pathResourceId},padId=${padId}。`);
186
- }
187
- if (pathResourceId && padId && pathResourceId === padId) {
188
- recommendations.push("链接路径资源标识与页面 padId 一致,但这仍不等同于 Wedoc API 可用的真实 docId。");
189
- }
190
- return {
191
- shareUrl,
192
- finalUrl,
193
- httpStatus: status,
194
- contentType: readString(contentType) || undefined,
195
- pathResourceType: pathResourceType || undefined,
196
- pathResourceId: pathResourceId || undefined,
197
- shareCode: shareCode || undefined,
198
- userType: userType || undefined,
199
- isGuest,
200
- padId: padId || undefined,
201
- padType: padType || undefined,
202
- padTitle: padTitle || undefined,
203
- ownerId: readString(ownerInfo.ownerId) || undefined,
204
- hasShareInfo: Object.keys(shareInfo).length > 0,
205
- hasAclInfo: Object.keys(aclInfo).length > 0,
206
- likelyUnavailableToGuest,
207
- findings,
208
- recommendations,
209
- };
154
+ function buildShareLinkDiagnosis(params: {
155
+ shareUrl: string;
156
+ finalUrl: string;
157
+ status: number;
158
+ contentType: string;
159
+ basicClientVars: any;
160
+ }) {
161
+ const { shareUrl, finalUrl, status, contentType, basicClientVars } = params;
162
+ const parsedUrl = new URL(finalUrl || shareUrl);
163
+ const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
164
+ const pathResourceType = readString(pathSegments[0]);
165
+ const pathResourceId = readString(pathSegments[1]);
166
+ const shareCode = readString(parsedUrl.searchParams.get("scode"));
167
+ const userInfo =
168
+ basicClientVars?.userInfo && typeof basicClientVars.userInfo === "object"
169
+ ? basicClientVars.userInfo
170
+ : {};
171
+ const docInfo =
172
+ basicClientVars?.docInfo && typeof basicClientVars.docInfo === "object"
173
+ ? basicClientVars.docInfo
174
+ : {};
175
+ const padInfo = docInfo?.padInfo && typeof docInfo.padInfo === "object" ? docInfo.padInfo : {};
176
+ const ownerInfo =
177
+ docInfo?.ownerInfo && typeof docInfo.ownerInfo === "object" ? docInfo.ownerInfo : {};
178
+ const shareInfo =
179
+ docInfo?.shareInfo && typeof docInfo.shareInfo === "object" ? docInfo.shareInfo : {};
180
+ const aclInfo = docInfo?.aclInfo && typeof docInfo.aclInfo === "object" ? docInfo.aclInfo : {};
181
+ const userType = readString(userInfo.userType);
182
+ const padType = readString(padInfo.padType);
183
+ const padId = readString(padInfo.padId);
184
+ const padTitle = readString(padInfo.padTitle);
185
+ const isGuest = userType === "guest" || Number(userInfo.loginType) === 0;
186
+ const isBlankPage = padType === "blankpage";
187
+ const likelyUnavailableToGuest = isGuest && isBlankPage && !padTitle;
188
+ const findings = [
189
+ `HTTP ${String(status || "")}`.trim(),
190
+ `内容类型:${readString(contentType) || "未知"}`,
191
+ `访问身份:${userType || "未知"}`,
192
+ `页面类型:${padType || "未知"}`,
193
+ `路径资源:${pathResourceType || "未知"} / ${pathResourceId || "未知"}`,
194
+ ];
195
+ const recommendations: string[] = [];
196
+ if (likelyUnavailableToGuest) {
197
+ recommendations.push(
198
+ '当前链接对 guest/未登录企业微信环境返回 blankpage,外部访问会表现为打不开或像"文档不存在"。',
199
+ );
200
+ }
201
+ if (shareCode) {
202
+ recommendations.push(
203
+ `当前链接带有分享码 scode=${shareCode}。如分享码过期或未生效,外部访问会失败。`,
204
+ );
205
+ }
206
+ if (pathResourceId && padId && pathResourceId !== padId) {
207
+ recommendations.push(
208
+ `链接路径中的资源标识与页面 padId 不一致:path=${pathResourceId},padId=${padId}。`,
209
+ );
210
+ }
211
+ if (pathResourceId && padId && pathResourceId === padId) {
212
+ recommendations.push(
213
+ "链接路径资源标识与页面 padId 一致,但这仍不等同于 Wedoc API 可用的真实 docId。",
214
+ );
215
+ }
216
+ return {
217
+ shareUrl,
218
+ finalUrl,
219
+ httpStatus: status,
220
+ contentType: readString(contentType) || undefined,
221
+ pathResourceType: pathResourceType || undefined,
222
+ pathResourceId: pathResourceId || undefined,
223
+ shareCode: shareCode || undefined,
224
+ userType: userType || undefined,
225
+ isGuest,
226
+ padId: padId || undefined,
227
+ padType: padType || undefined,
228
+ padTitle: padTitle || undefined,
229
+ ownerId: readString(ownerInfo.ownerId) || undefined,
230
+ hasShareInfo: Object.keys(shareInfo).length > 0,
231
+ hasAclInfo: Object.keys(aclInfo).length > 0,
232
+ likelyUnavailableToGuest,
233
+ findings,
234
+ recommendations,
235
+ };
210
236
  }
211
237
 
212
238
  async function inspectWecomShareLink(params: { shareUrl: string }) {
213
- const { shareUrl } = params;
214
- const normalizedUrl = readString(shareUrl);
215
- if (!normalizedUrl) throw new Error("shareUrl required");
216
- let parsed;
217
- try {
218
- parsed = new URL(normalizedUrl);
219
- } catch {
220
- throw new Error("shareUrl must be a valid URL");
221
- }
222
- // To protect URLs containing underscores from markdown italic corruption in output, we ensure we return exactly what we got or wrap it later.
239
+ const { shareUrl } = params;
240
+ const normalizedUrl = readString(shareUrl);
241
+ if (!normalizedUrl) throw new Error("shareUrl required");
242
+ let parsed;
243
+ try {
244
+ parsed = new URL(normalizedUrl);
245
+ } catch {
246
+ throw new Error("shareUrl must be a valid URL");
247
+ }
248
+ // To protect URLs containing underscores from markdown italic corruption in output, we ensure we return exactly what we got or wrap it later.
223
249
 
224
- const response = await fetch(parsed.toString(), {
225
- headers: {
226
- "user-agent": "OpenClaw-Wechat/1.0",
227
- accept: "text/html,application/xhtml+xml",
228
- },
229
- });
230
- const contentType = response.headers?.get("content-type") || "";
231
- const html = await response.text();
232
- const basicClientVars = extractEmbeddedJson(html, "basicClientVars");
233
- const diagnosis = buildShareLinkDiagnosis({
234
- shareUrl: normalizedUrl,
235
- finalUrl: response.url || parsed.toString(),
236
- status: response.status,
237
- contentType,
238
- basicClientVars,
239
- });
240
- return {
241
- raw: {
242
- httpStatus: response.status,
243
- // Markdown italic protection for URLs
244
- finalUrl: `\u00A0${response.url || parsed.toString()}\u00A0`.trim(),
245
- contentType,
246
- basicClientVars,
247
- },
248
- diagnosis,
249
- };
250
+ const response = await fetch(parsed.toString(), {
251
+ headers: {
252
+ "user-agent": "OpenClaw-Wechat/1.0",
253
+ accept: "text/html,application/xhtml+xml",
254
+ },
255
+ });
256
+ const contentType = response.headers?.get("content-type") || "";
257
+ const html = await response.text();
258
+ const basicClientVars = extractEmbeddedJson(html, "basicClientVars");
259
+ const diagnosis = buildShareLinkDiagnosis({
260
+ shareUrl: normalizedUrl,
261
+ finalUrl: response.url || parsed.toString(),
262
+ status: response.status,
263
+ contentType,
264
+ basicClientVars,
265
+ });
266
+ return {
267
+ raw: {
268
+ httpStatus: response.status,
269
+ // Markdown italic protection for URLs
270
+ finalUrl: `\u00A0${response.url || parsed.toString()}\u00A0`.trim(),
271
+ contentType,
272
+ basicClientVars,
273
+ },
274
+ diagnosis,
275
+ };
250
276
  }
251
277
 
252
278
  function summarizeShareLinkDiagnosis(diagnosis: any = {}) {
253
- const parts = Array.isArray(diagnosis.findings) ? diagnosis.findings : [];
254
- return parts.length > 0 ? `分享链接校验:${parts.join(",")}` : "分享链接校验已完成";
279
+ const parts = Array.isArray(diagnosis.findings) ? diagnosis.findings : [];
280
+ return parts.length > 0 ? `分享链接校验:${parts.join(",")}` : "分享链接校验已完成";
255
281
  }
256
282
 
257
283
  function summarizeSheetProperties(result: any = {}) {
258
- return `表格属性已获取:工作表 ${result.properties?.length ?? 0}`;
284
+ return `表格属性已获取:工作表 ${result.properties?.length ?? 0}`;
259
285
  }
260
286
 
261
287
  function summarizeDocAccess(result: any = {}) {
262
- const parts = [];
263
- if (result.addedViewerCount) parts.push(`新增查看成员 ${result.addedViewerCount}`);
264
- if (result.addedCollaboratorCount) parts.push(`新增协作者 ${result.addedCollaboratorCount}`);
265
- if (result.removedViewerCount) parts.push(`移除查看成员 ${result.removedViewerCount}`);
266
- if (result.removedCollaboratorCount) parts.push(`移除协作者 ${result.removedCollaboratorCount}`);
267
- return parts.length > 0 ? `文档权限已更新:${parts.join(",")}` : "文档权限已更新";
288
+ const parts = [];
289
+ if (result.addedViewerCount) parts.push(`新增查看成员 ${result.addedViewerCount}`);
290
+ if (result.addedCollaboratorCount) parts.push(`新增协作者 ${result.addedCollaboratorCount}`);
291
+ if (result.removedViewerCount) parts.push(`移除查看成员 ${result.removedViewerCount}`);
292
+ if (result.removedCollaboratorCount) parts.push(`移除协作者 ${result.removedCollaboratorCount}`);
293
+ return parts.length > 0 ? `文档权限已更新:${parts.join(",")}` : "文档权限已更新";
268
294
  }
269
295
 
270
296
  function summarizeFormInfo(result: any = {}) {
271
- const title = readString(result.formInfo?.form_title) || "未命名收集表";
272
- return `收集表"${title}"信息已获取`;
297
+ const title = readString(result.formInfo?.form_title) || "未命名收集表";
298
+ return `收集表"${title}"信息已获取`;
273
299
  }
274
300
 
275
301
  function summarizeFormAnswer(result: any = {}) {
276
- return `收集表答案已获取:字段 ${result.answerList?.length ?? 0}`;
302
+ return `收集表答案已获取:字段 ${result.answerList?.length ?? 0}`;
277
303
  }
278
304
 
279
305
  function summarizeFormStatistic(result: any = {}) {
280
- return `收集表统计已获取:请求 ${result.items?.length ?? 0},成功 ${result.successCount ?? 0}`;
306
+ return `收集表统计已获取:请求 ${result.items?.length ?? 0},成功 ${result.successCount ?? 0}`;
281
307
  }
282
308
 
283
309
  function summarizeAdvancedAccount(result: any = {}, action: string) {
284
- if (action === "assign") return `高级功能账号分配任务已提交,jobid: ${result.jobid || "未知"}`;
285
- if (action === "cancel") return `高级功能账号取消任务已提交,jobid: ${result.jobid || "未知"}`;
286
- return `高级功能账号列表已获取:${result.userList?.length ?? 0} 个`;
310
+ if (action === "assign") return `高级功能账号分配任务已提交,jobid: ${result.jobid || "未知"}`;
311
+ if (action === "cancel") return `高级功能账号取消任务已提交,jobid: ${result.jobid || "未知"}`;
312
+ return `高级功能账号列表已获取:${result.userList?.length ?? 0} 个`;
287
313
  }
288
314
 
289
315
  function readMemberUserId(value: any) {
290
- if (typeof value === "string" || typeof value === "number") {
291
- return readString(value);
292
- }
293
- if (!value || typeof value !== "object" || Array.isArray(value)) return "";
294
- return readString(value.userid ?? value.userId);
316
+ if (typeof value === "string" || typeof value === "number") {
317
+ return readString(value);
318
+ }
319
+ if (!value || typeof value !== "object" || Array.isArray(value)) return "";
320
+ return readString(value.userid ?? value.userId);
295
321
  }
296
322
 
297
323
  function hasMemberUserId(values: any, requesterSenderId: string) {
298
- const normalizedRequesterSenderId = readString(requesterSenderId);
299
- if (!normalizedRequesterSenderId) return false;
300
- return Array.isArray(values) && values.some((item) => readMemberUserId(item) === normalizedRequesterSenderId);
324
+ const normalizedRequesterSenderId = readString(requesterSenderId);
325
+ if (!normalizedRequesterSenderId) return false;
326
+ return (
327
+ Array.isArray(values) &&
328
+ values.some((item) => readMemberUserId(item) === normalizedRequesterSenderId)
329
+ );
301
330
  }
302
331
 
303
- function resolveCreateCollaborators(params: {
304
- toolContext: any;
305
- requestParams: any;
306
- }) {
307
- const { toolContext, requestParams } = params;
308
- const explicitCollaborators = Array.isArray(requestParams?.collaborators) ? [...requestParams.collaborators] : [];
309
- const requesterSenderId = readString(toolContext?.senderId || toolContext?.requesterSenderId); // align with OpenClaw standard `senderId`
310
- if (!requesterSenderId) return explicitCollaborators;
311
- // By default, let's always auto-grant requester
312
- if (hasMemberUserId(explicitCollaborators, requesterSenderId)) return explicitCollaborators;
313
- if (hasMemberUserId(requestParams?.viewers, requesterSenderId)) return explicitCollaborators;
314
- explicitCollaborators.push(requesterSenderId);
315
- return explicitCollaborators;
332
+ function resolveCreateCollaborators(params: { toolContext: any; requestParams: any }) {
333
+ const { toolContext, requestParams } = params;
334
+ const explicitCollaborators = Array.isArray(requestParams?.collaborators)
335
+ ? [...requestParams.collaborators]
336
+ : [];
337
+ const requesterSenderId = readString(toolContext?.senderId || toolContext?.requesterSenderId); // align with OpenClaw standard `senderId`
338
+ if (!requesterSenderId) return explicitCollaborators;
339
+ // By default, let's always auto-grant requester
340
+ if (hasMemberUserId(explicitCollaborators, requesterSenderId)) return explicitCollaborators;
341
+ if (hasMemberUserId(requestParams?.viewers, requesterSenderId)) return explicitCollaborators;
342
+ explicitCollaborators.push(requesterSenderId);
343
+ return explicitCollaborators;
316
344
  }
317
345
 
318
346
  function buildToolResult(payload: any) {
319
- // To avoid formatting issues with URLs having underscores rendering as markdown Italics
320
- if (payload.url) payload.url = `<${payload.url}>`;
321
- if (payload.diagnosis?.finalUrl) payload.diagnosis.finalUrl = `<${payload.diagnosis.finalUrl}>`;
322
- if (payload.diagnosis?.shareUrl) payload.diagnosis.shareUrl = `<${payload.diagnosis.shareUrl}>`;
323
- return {
324
- content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }],
325
- details: payload,
326
- };
347
+ // To avoid formatting issues with URLs having underscores rendering as markdown Italics
348
+ if (payload.url) payload.url = `<${payload.url}>`;
349
+ if (payload.diagnosis?.finalUrl) payload.diagnosis.finalUrl = `<${payload.diagnosis.finalUrl}>`;
350
+ if (payload.diagnosis?.shareUrl) payload.diagnosis.shareUrl = `<${payload.diagnosis.shareUrl}>`;
351
+ return {
352
+ content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }],
353
+ details: payload,
354
+ };
327
355
  }
328
356
 
329
357
  export function registerWecomDocTools(api: OpenClawPluginApi) {
330
- if (typeof api?.registerTool !== "function") return;
331
- const docClient = new WecomDocClient();
358
+ if (typeof api?.registerTool !== "function") return;
359
+ const docClient = new WecomDocClient();
332
360
 
333
- api.registerTool((toolContext: any) => ({
334
- name: "wecom_doc",
335
- label: "WeCom Doc",
336
- description: "企业微信文档工具。支持文档/表格/收集表完整CRUD操作、查看/协作者权限配置、属性查询以及分享打不开可用性诊断功能。",
337
- parameters: wecomDocToolSchema,
338
- async execute(_toolCallId, params: any) {
339
- try {
340
- let accountId = params.accountId || toolContext?.accountId || "default";
341
- const account = resolveAgentAccountOrUndefined(api.config, accountId);
342
- if (!account || !account.configured) {
343
- throw new Error(`WeCom account ${accountId} not configured for Doc API requirements`);
344
- }
361
+ api.registerTool((toolContext: any) => {
362
+ if (
363
+ toolContext?.messageChannel !== "wecom" ||
364
+ !isWecomAgentSource({
365
+ accountId: toolContext?.agentAccountId || toolContext?.accountId,
366
+ sessionKey: toolContext?.sessionKey,
367
+ sessionId: toolContext?.sessionId,
368
+ })
369
+ ) {
370
+ return null;
371
+ }
345
372
 
346
- const action = params.action;
347
- switch (action) {
348
- case "create": {
349
- const explicitCollaborators = Array.isArray(params.collaborators) ? [...params.collaborators] : [];
350
- const result = await docClient.createDoc({
351
- agent: account,
352
- docName: params.docName,
353
- docType: params.docType,
354
- spaceId: params.spaceId,
355
- fatherId: params.fatherId,
356
- adminUsers: params.adminUsers,
357
- });
373
+ return {
374
+ name: "wecom_doc",
375
+ label: "WeCom Doc",
376
+ description:
377
+ "企业微信文档工具。支持文档/表格/收集表完整CRUD操作、查看/协作者权限配置、属性查询以及分享打不开可用性诊断功能。",
378
+ parameters: wecomDocToolSchema,
379
+ async execute(_toolCallId, params: any) {
380
+ try {
381
+ let accountId =
382
+ params.accountId || toolContext?.agentAccountId || toolContext?.accountId || "default";
383
+ const account = resolveAgentAccountOrUndefined(api.config, accountId);
384
+ if (!account || !account.configured) {
385
+ throw new Error(`WeCom account ${accountId} not configured for Doc API requirements`);
386
+ }
358
387
 
359
- // Auto-set security rules for better default permissions (internal users can edit)
360
- try {
361
- await docClient.setDocJoinRule({
362
- agent: account,
363
- docId: result.docId,
364
- request: {
365
- enable_corp_internal: true,
366
- corp_internal_auth: 2, // 2 = edit permission
367
- enable_corp_external: false,
368
- ban_share_external: false,
369
- },
370
- });
371
- } catch (err) {
372
- // Non-fatal: document created, just default permissions may be read-only
373
- }
388
+ const action = params.action;
389
+ switch (action) {
390
+ case "create": {
391
+ const explicitCollaborators = Array.isArray(params.collaborators)
392
+ ? [...params.collaborators]
393
+ : [];
394
+ const result = await docClient.createDoc({
395
+ agent: account,
396
+ docName: params.docName,
397
+ docType: params.docType,
398
+ spaceId: params.spaceId,
399
+ fatherId: params.fatherId,
400
+ adminUsers: params.adminUsers,
401
+ });
374
402
 
375
- // Handle initial content (title/body separation) if provided
376
- // Supports: string (text) or {type: "text"|"image", content/url: string}
377
- let contentResult: any = null;
378
- if (Array.isArray(params.init_content) && params.init_content.length > 0) {
379
- try {
380
- // Helper: check if content item is an image
381
- const isImageItem = (item: any): boolean => {
382
- if (typeof item === "object" && item !== null) {
383
- return item.type === "image" || (item.url && !item.content);
384
- }
385
- if (typeof item === "string") {
386
- // Detect image URLs
387
- return item.startsWith("http") &&
388
- (item.includes(".png") || item.includes(".jpg") ||
389
- item.includes(".jpeg") || item.includes(".gif") ||
390
- item.includes("qpic.cn") || item.includes("weixin.qq.com"));
391
- }
392
- return false;
393
- };
403
+ // Auto-set security rules for better default permissions (internal users can edit)
404
+ try {
405
+ await docClient.setDocJoinRule({
406
+ agent: account,
407
+ docId: result.docId,
408
+ request: {
409
+ enable_corp_internal: true,
410
+ corp_internal_auth: 2, // 2 = edit permission
411
+ enable_corp_external: false,
412
+ ban_share_external: false,
413
+ },
414
+ });
415
+ } catch (err) {
416
+ // Non-fatal: document created, just default permissions may be read-only
417
+ }
394
418
 
395
- // Helper: get image URL from content item
396
- const getImageUrl = (item: any): string => {
397
- if (typeof item === "object" && item !== null) {
398
- return item.url || item.content || "";
399
- }
400
- return String(item);
401
- };
419
+ // Handle initial content (title/body separation) if provided
420
+ // Supports: string (text) or {type: "text"|"image", content/url: string}
421
+ let contentResult: any = null;
422
+ if (Array.isArray(params.init_content) && params.init_content.length > 0) {
423
+ try {
424
+ // Helper: check if content item is an image
425
+ const isImageItem = (item: any): boolean => {
426
+ if (typeof item === "object" && item !== null) {
427
+ return item.type === "image" || (item.url && !item.content);
428
+ }
429
+ if (typeof item === "string") {
430
+ // Detect image URLs
431
+ return (
432
+ item.startsWith("http") &&
433
+ (item.includes(".png") ||
434
+ item.includes(".jpg") ||
435
+ item.includes(".jpeg") ||
436
+ item.includes(".gif") ||
437
+ item.includes("qpic.cn") ||
438
+ item.includes("weixin.qq.com"))
439
+ );
440
+ }
441
+ return false;
442
+ };
402
443
 
403
- // Helper: download image and convert to base64
404
- const downloadImageAsBase64 = async (url: string): Promise<string> => {
405
- const response = await fetch(url);
406
- if (!response.ok) {
407
- throw new Error(`Failed to download image: ${url}`);
408
- }
409
- const arrayBuffer = await response.arrayBuffer();
410
- return Buffer.from(arrayBuffer).toString("base64");
411
- };
444
+ // Helper: get image URL from content item
445
+ const getImageUrl = (item: any): string => {
446
+ if (typeof item === "object" && item !== null) {
447
+ return item.url || item.content || "";
448
+ }
449
+ return String(item);
450
+ };
412
451
 
413
- // Helper: get text from content item
414
- const getText = (item: any): string => {
415
- if (typeof item === "object" && item !== null) {
416
- return item.content || item.text || "";
417
- }
418
- return String(item);
419
- };
452
+ // Helper: get text from content item
453
+ const getText = (item: any): string => {
454
+ if (typeof item === "object" && item !== null) {
455
+ return item.content || item.text || "";
456
+ }
457
+ return String(item);
458
+ };
420
459
 
421
- // Step 1: Insert first paragraph (title) at index 0
422
- if (params.init_content[0]) {
423
- const firstItem = params.init_content[0];
424
- if (isImageItem(firstItem)) {
425
- // First item is image - upload first, then insert at index 0
426
- const imgUrl = getImageUrl(firstItem);
460
+ // Helper: download image from URL or read local file and convert to base64
461
+ const getImageAsBase64 = async (imageSource: string): Promise<string> => {
462
+ // Check if it's a local file path or remote URL
463
+ const isLocalFile =
464
+ !imageSource.startsWith("http") && !imageSource.startsWith("data:");
427
465
 
428
- try {
429
- // Upload image to WeCom to get proper image_id
430
- const base64 = await downloadImageAsBase64(imgUrl);
431
- const uploadResult = await docClient.uploadDocImage({
432
- agent: account,
433
- docId: result.docId,
434
- base64_content: base64,
435
- });
466
+ if (isLocalFile) {
467
+ // Local file path - read using Node.js fs
468
+ const fs = await import("fs");
469
+ const path = await import("path");
436
470
 
437
- // Insert image using uploaded URL
438
- // Note: version is optional, API handles concurrency
439
- await docClient.updateDocContent({
440
- agent: account,
441
- docId: result.docId,
442
- requests: [{
443
- insert_image: {
444
- image_id: uploadResult.url,
445
- location: { index: 0 },
446
- width: uploadResult.width,
447
- height: uploadResult.height
448
- }
449
- }]
450
- });
451
- } catch (uploadErr) {
452
- console.error(`Failed to upload first image ${imgUrl}:`, uploadErr);
453
- throw new Error(`First image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
454
- }
455
- } else {
456
- const titleText = getText(firstItem);
457
- await docClient.updateDocContent({
458
- agent: account,
459
- docId: result.docId,
460
- requests: [{
461
- insert_text: {
462
- text: titleText,
463
- location: { index: 0 }
464
- }
465
- }]
466
- });
471
+ // Resolve to absolute path if relative
472
+ const absolutePath = path.isAbsolute(imageSource)
473
+ ? imageSource
474
+ : path.join(process.cwd(), imageSource);
467
475
 
468
- // Apply Title Styling (Bold)
469
- if (titleText.length > 0) {
470
- await docClient.updateDocContent({
471
- agent: account,
472
- docId: result.docId,
473
- requests: [{
474
- update_text_property: {
475
- text_property: { bold: true },
476
- ranges: [{ start_index: 0, length: titleText.length }]
477
- }
478
- }]
479
- });
480
- }
481
- }
482
- }
476
+ if (!fs.existsSync(absolutePath)) {
477
+ throw new Error(`Local image file not found: ${absolutePath}`);
478
+ }
483
479
 
484
- // Step 2: For subsequent items, append with proper paragraph handling
485
- // Per API spec: must get latest version and index before each batch_update
486
- for (let i = 1; i < params.init_content.length; i++) {
487
- const item = params.init_content[i];
480
+ const fileBuffer = fs.readFileSync(absolutePath);
481
+ return fileBuffer.toString("base64");
482
+ } else if (imageSource.startsWith("data:")) {
483
+ // Data URL (base64 already)
484
+ const matches = imageSource.match(
485
+ /^data:image\/(?:png|jpeg|gif|webp);base64,(.*)$/,
486
+ );
487
+ if (matches && matches[1]) {
488
+ return matches[1];
489
+ }
490
+ throw new Error("Invalid data URL format");
491
+ } else {
492
+ // Remote URL - download first
493
+ console.log(
494
+ `[wecom-doc] Downloading remote image: ${imageSource.substring(0, 50)}...`,
495
+ );
496
+ const response = await fetch(imageSource);
497
+ if (!response.ok) {
498
+ throw new Error(
499
+ `Failed to download image: ${response.status} ${response.statusText}`,
500
+ );
501
+ }
502
+ const arrayBuffer = await response.arrayBuffer();
503
+ return Buffer.from(arrayBuffer).toString("base64");
504
+ }
505
+ };
488
506
 
489
- // Refresh content to get latest document structure and version
490
- // API requires: version difference ≤ 100 from latest
491
- const currentContent = await docClient.getDocContent({
492
- agent: account,
493
- docId: result.docId,
494
- });
507
+ // Step 1: Insert first paragraph (title) at index 0
508
+ if (params.init_content[0]) {
509
+ const firstItem = params.init_content[0];
510
+ if (isImageItem(firstItem)) {
511
+ // First item is image - upload first, then insert at index 0
512
+ const imgUrl = getImageUrl(firstItem);
495
513
 
496
- // Get the end index of the document
497
- const docEndIndex = currentContent.document.end;
498
- const currentVersion = currentContent.version;
514
+ try {
515
+ // Upload image to WeCom to get proper image_id
516
+ const base64 = await getImageAsBase64(imgUrl);
517
+ const uploadResult = await docClient.uploadDocImage({
518
+ agent: account,
519
+ docId: result.docId,
520
+ base64_content: base64,
521
+ });
522
+ console.log(
523
+ `[wecom-doc] Title image uploaded: ${uploadResult.width}x${uploadResult.height}`,
524
+ );
499
525
 
500
- if (isImageItem(item)) {
501
- // Insert image: upload first, then create paragraph, then insert image
502
- const imgUrl = getImageUrl(item);
526
+ // Insert image using uploaded URL
527
+ // Note: version is optional, API handles concurrency
528
+ await docClient.updateDocContent({
529
+ agent: account,
530
+ docId: result.docId,
531
+ requests: [
532
+ {
533
+ insert_image: {
534
+ image_id: uploadResult.url,
535
+ location: { index: 0 },
536
+ width: uploadResult.width,
537
+ height: uploadResult.height,
538
+ },
539
+ },
540
+ ],
541
+ });
542
+ } catch (uploadErr) {
543
+ console.error(`Failed to upload first image ${imgUrl}:`, uploadErr);
544
+ throw new Error(
545
+ `First image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`,
546
+ );
547
+ }
548
+ } else {
549
+ const titleText = getText(firstItem);
550
+ await docClient.updateDocContent({
551
+ agent: account,
552
+ docId: result.docId,
553
+ requests: [
554
+ {
555
+ insert_text: {
556
+ text: titleText,
557
+ location: { index: 0 },
558
+ },
559
+ },
560
+ ],
561
+ });
503
562
 
504
- try {
505
- // Step 1: Download and upload image to WeCom
506
- const base64 = await downloadImageAsBase64(imgUrl);
507
- const uploadResult = await docClient.uploadDocImage({
508
- agent: account,
509
- docId: result.docId,
510
- base64_content: base64,
511
- });
563
+ // Apply Title Styling (Bold)
564
+ if (titleText.length > 0) {
565
+ await docClient.updateDocContent({
566
+ agent: account,
567
+ docId: result.docId,
568
+ requests: [
569
+ {
570
+ update_text_property: {
571
+ text_property: { bold: true },
572
+ ranges: [{ start_index: 0, length: titleText.length }],
573
+ },
574
+ },
575
+ ],
576
+ });
577
+ }
578
+ }
579
+ }
512
580
 
513
- // Step 2: Create new paragraph and insert image in one batch (2 operations ≤ 30)
514
- // Per API spec: all indices are based on the same document snapshot
515
- // insert_paragraph at docEndIndex creates a new paragraph
516
- // insert_image at docEndIndex + 1 inserts into the newly created paragraph
517
- await docClient.updateDocContent({
518
- agent: account,
519
- docId: result.docId,
520
- version: currentVersion, // Pass version for concurrency control
521
- requests: [
522
- {
523
- insert_paragraph: {
524
- location: { index: docEndIndex }
525
- }
526
- },
527
- {
528
- insert_image: {
529
- image_id: uploadResult.url,
530
- location: { index: docEndIndex + 1 },
531
- width: uploadResult.width,
532
- height: uploadResult.height
533
- }
534
- }
535
- ]
536
- });
537
- } catch (uploadErr) {
538
- console.error(`Failed to upload image ${imgUrl}:`, uploadErr);
539
- throw new Error(`Image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
540
- }
541
- } else {
542
- const text = getText(item);
543
- if (!text) continue;
581
+ // Step 2: For subsequent items, append with proper paragraph handling
582
+ // Per API spec: must get latest version and index before each batch_update
583
+ for (let i = 1; i < params.init_content.length; i++) {
584
+ const item = params.init_content[i];
544
585
 
545
- // Insert text: create paragraph and insert text in one batch (2 operations ≤ 30)
546
- // Per API spec: all indices are based on the same document snapshot
547
- // insert_paragraph at docEndIndex creates a new paragraph
548
- // insert_text at docEndIndex + 1 inserts into the newly created paragraph
549
- await docClient.updateDocContent({
550
- agent: account,
551
- docId: result.docId,
552
- version: currentVersion, // Pass version for concurrency control
553
- requests: [
554
- {
555
- insert_paragraph: {
556
- location: { index: docEndIndex }
557
- }
558
- },
559
- {
560
- insert_text: {
561
- text: text,
562
- location: { index: docEndIndex + 1 }
563
- }
564
- }
565
- ]
566
- });
567
- }
568
- }
569
- contentResult = "init_content_populated";
570
- } catch (err) {
571
- contentResult = `content_failed: ${err instanceof Error ? err.message : String(err)}`;
572
- }
573
- }
586
+ // Refresh content to get latest document structure and version
587
+ // API requires: version difference 100 from latest
588
+ const currentContent = await docClient.getDocContent({
589
+ agent: account,
590
+ docId: result.docId,
591
+ });
574
592
 
575
- let accessResult: any = null;
576
- if ((Array.isArray(params.viewers) && params.viewers.length > 0) || explicitCollaborators.length > 0) {
577
- try {
578
- accessResult = await docClient.grantDocAccess({
579
- agent: account,
580
- docId: result.docId,
581
- viewers: params.viewers,
582
- collaborators: explicitCollaborators,
583
- });
584
- } catch (err) {
585
- return buildToolResult({
586
- ok: false,
587
- partial: true,
588
- action: "create",
589
- accountId: account.accountId,
590
- resourceType: result.docTypeLabel,
591
- canonicalDocId: result.docId,
592
- docId: result.docId,
593
- title: readString(params.docName),
594
- url: result.url || undefined,
595
- summary: `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId}),但权限授予失败`,
596
- usageHint: buildDocIdUsageHint(result.docId) || undefined,
597
- error: err instanceof Error ? err.message : String(err),
598
- raw: { create: result.raw },
599
- });
600
- }
601
- }
602
- return buildToolResult({
603
- ok: true,
604
- action: "create",
605
- accountId: account.accountId,
606
- resourceType: result.docTypeLabel,
607
- canonicalDocId: result.docId,
608
- docId: result.docId,
609
- title: readString(params.docName),
610
- url: result.url || undefined,
611
- summary: accessResult
612
- ? `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId});${summarizeDocAccess(accessResult)}` + (contentResult ? `;内容填充: ${contentResult}` : "")
613
- : `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId})` + (contentResult ? `;内容填充: ${contentResult}` : ""),
614
- usageHint: buildDocIdUsageHint(result.docId) || undefined,
615
- raw: accessResult ? { create: result.raw, access: accessResult.raw } : result.raw,
616
- });
617
- }
618
- case "rename": {
619
- const result = await docClient.renameDoc({
620
- agent: account,
621
- docId: params.docId,
622
- newName: params.newName,
623
- });
624
- return buildToolResult({
625
- ok: true,
626
- action: "rename",
627
- accountId: account.accountId,
628
- docId: result.docId,
629
- title: result.newName,
630
- summary: `文档已重命名为"${result.newName}"`,
631
- raw: result.raw,
632
- });
633
- }
634
- case "copy": {
635
- const result = await docClient.copyDoc({
636
- agent: account,
637
- docId: params.docId,
638
- newName: params.newName,
639
- spaceId: params.spaceId,
640
- fatherId: params.fatherId,
641
- });
642
- return buildToolResult({
643
- ok: true,
644
- action: "copy",
645
- accountId: account.accountId,
646
- docId: result.docId,
647
- summary: `文档已成功复制,新 docId: ${result.docId}`,
648
- raw: result.raw,
649
- });
650
- }
651
- case "get_info": {
652
- const result = await docClient.getDocBaseInfo({
653
- agent: account,
654
- docId: params.docId,
655
- });
656
- return buildToolResult({
657
- ok: true,
658
- action: "get_info",
659
- accountId: account.accountId,
660
- docId: params.docId,
661
- title: readString(result.info?.doc_name) || undefined,
662
- resourceType:
663
- Number(result.info?.doc_type) === 10 ? "smart_table" : Number(result.info?.doc_type) === 4 ? "spreadsheet" : "doc",
664
- summary: summarizeDocInfo(result.info),
665
- raw: result.raw,
666
- });
667
- }
668
- case "share": {
669
- const result = await docClient.shareDoc({
670
- agent: account,
671
- docId: params.docId,
672
- });
673
- return buildToolResult({
674
- ok: true,
675
- action: "share",
676
- accountId: account.accountId,
677
- canonicalDocId: params.docId,
678
- docId: params.docId,
679
- url: result.shareUrl || undefined,
680
- summary: result.shareUrl ? `文档分享链接已获取(docId: ${params.docId})` : `文档分享接口调用成功(docId: ${params.docId})`,
681
- usageHint: buildDocIdUsageHint(params.docId) || undefined,
682
- raw: result.raw,
683
- });
684
- }
685
- case "get_auth": {
686
- const result = await docClient.getDocAuth({
687
- agent: account,
688
- docId: params.docId,
689
- });
690
- const diagnosis = buildDocAuthDiagnosis(result, toolContext?.senderId);
691
- return buildToolResult({
692
- ok: true,
693
- action: "get_auth",
694
- accountId: account.accountId,
695
- canonicalDocId: params.docId,
696
- docId: params.docId,
697
- summary: summarizeDocAuth(result),
698
- diagnosis,
699
- raw: result.raw,
700
- });
701
- }
702
- case "diagnose_auth": {
703
- const result = await docClient.getDocAuth({
704
- agent: account,
705
- docId: params.docId,
706
- });
707
- const diagnosis = buildDocAuthDiagnosis(result, toolContext?.senderId);
708
- return buildToolResult({
709
- ok: true,
710
- action: "diagnose_auth",
711
- accountId: account.accountId,
712
- canonicalDocId: params.docId,
713
- docId: params.docId,
714
- summary: summarizeDocAuthDiagnosis(diagnosis),
715
- diagnosis,
716
- raw: result.raw,
717
- });
718
- }
719
- case "validate_share_link": {
720
- const result = await inspectWecomShareLink({
721
- shareUrl: params.shareUrl,
722
- });
723
- return buildToolResult({
724
- ok: true,
725
- action: "validate_share_link",
726
- accountId: account.accountId,
727
- url: result.diagnosis.finalUrl || params.shareUrl,
728
- summary: summarizeShareLinkDiagnosis(result.diagnosis),
729
- diagnosis: result.diagnosis,
730
- raw: result.raw,
731
- });
732
- }
733
- case "delete": {
734
- const result = await docClient.deleteDoc({
735
- agent: account,
736
- docId: params.docId,
737
- formId: params.formId,
738
- });
739
- return buildToolResult({
740
- ok: true,
741
- action: "delete",
742
- accountId: account.accountId,
743
- docId: result.docId || undefined,
744
- formId: result.formId || undefined,
745
- summary: result.formId ? "收集表已删除" : "文档已删除",
746
- raw: result.raw,
747
- });
748
- }
749
- case "set_join_rule": {
750
- const result = await docClient.setDocJoinRule({
751
- agent: account,
752
- docId: params.docId,
753
- request: params.request,
754
- });
755
- return buildToolResult({
756
- ok: true,
757
- action: "set_join_rule",
758
- accountId: account.accountId,
759
- docId: result.docId,
760
- summary: "文档查看规则已更新",
761
- raw: result.raw,
762
- });
763
- }
764
- case "set_member_auth": {
765
- const result = await docClient.setDocMemberAuth({
766
- agent: account,
767
- docId: params.docId,
768
- request: params.request,
769
- });
770
- return buildToolResult({
771
- ok: true,
772
- action: "set_member_auth",
773
- accountId: account.accountId,
774
- docId: result.docId,
775
- summary: "文档通知范围及成员权限已更新",
776
- raw: result.raw,
777
- });
778
- }
779
- case "grant_access": {
780
- const result = await docClient.grantDocAccess({
781
- agent: account,
782
- docId: params.docId,
783
- viewers: params.viewers,
784
- collaborators: params.collaborators,
785
- removeViewers: params.removeViewers,
786
- removeCollaborators: params.removeCollaborators,
787
- authLevel: params.auth,
788
- });
789
- return buildToolResult({
790
- ok: true,
791
- action: "grant_access",
792
- accountId: account.accountId,
793
- docId: result.docId,
794
- summary: summarizeDocAccess(result),
795
- raw: result.raw,
796
- });
797
- }
798
- case "add_collaborators": {
799
- const result = await docClient.addDocCollaborators({
800
- agent: account,
801
- docId: params.docId,
802
- collaborators: params.collaborators,
803
- auth: params.auth,
804
- });
805
- return buildToolResult({
806
- ok: true,
807
- action: "add_collaborators",
808
- accountId: account.accountId,
809
- docId: result.docId,
810
- summary: `协作者已添加:${result.addedCollaboratorCount ?? 0}`,
811
- raw: result.raw,
812
- });
813
- }
814
- case "get_content": {
815
- const result = await docClient.getDocContent({
816
- agent: account,
817
- docId: params.docId,
818
- });
819
- return buildToolResult({
820
- ok: true,
821
- action: "get_content",
822
- accountId: account.accountId,
823
- docId: params.docId,
824
- summary: "文档内容已获取",
825
- raw: result.raw,
826
- });
827
- }
828
- case "update_content": {
829
- const batchMode = params.batchMode === true;
830
-
831
- const result = await docClient.updateDocContent({
832
- agent: account,
833
- docId: params.docId,
834
- requests: params.requests,
835
- version: params.version,
836
- batchMode: batchMode,
837
- });
838
-
839
- return buildToolResult({
840
- ok: true,
841
- action: "update_content",
842
- accountId: account.accountId,
843
- docId: params.docId,
844
- summary: `文档内容已更新(${batchMode ? '批量' : '顺序'}模式)`,
845
- raw: result.raw,
846
- });
847
- }
848
- case "set_safety_setting": {
849
- const result = await docClient.setDocSafetySetting({
850
- agent: account,
851
- docId: params.docId,
852
- request: params.request,
853
- });
854
- return buildToolResult({
855
- ok: true,
856
- action: "set_safety_setting",
857
- accountId: account.accountId,
858
- docId: result.docId,
859
- summary: "文档安全设置已更新",
860
- raw: result.raw,
861
- });
862
- }
863
- case "get_doc_security_setting": {
864
- const result = await docClient.getDocAuth({
865
- agent: account,
866
- docId: params.docId,
867
- });
868
- return buildToolResult({
869
- ok: true,
870
- action: "get_doc_security_setting",
871
- accountId: account.accountId,
872
- docId: params.docId,
873
- summary: "文档安全设置已获取",
874
- details: result.secureSetting,
875
- raw: result.raw,
876
- });
877
- }
878
- case "mod_doc_security_setting": {
879
- // Alias to setDocSafetySetting logic
880
- const result = await docClient.setDocSafetySetting({
881
- agent: account,
882
- docId: params.docId,
883
- request: params.setting,
884
- });
885
- return buildToolResult({
886
- ok: true,
887
- action: "mod_doc_security_setting",
888
- accountId: account.accountId,
889
- docId: result.docId,
890
- summary: "文档安全设置已更新",
891
- raw: result.raw,
892
- });
893
- }
894
- case "mod_doc_member_notified_scope": {
895
- const result = await docClient.modDocMemberNotifiedScope({
896
- agent: account,
897
- docId: params.docId,
898
- notified_scope_type: params.notified_scope_type,
899
- notified_member_list: params.notified_member_list,
900
- });
901
- return buildToolResult({
902
- ok: true,
903
- action: "mod_doc_member_notified_scope",
904
- accountId: account.accountId,
905
- docId: params.docId,
906
- summary: "文档成员通知范围已更新",
907
- raw: result,
908
- });
909
- }
910
- case "create_collect": {
911
- // 创建收集表(表单)
912
- // 参考 API 规范文档:E8_AF_B7_E4_B8_A5_E6_A0_BC_E6_8C_89_E7_85_A7_E4_BB_A5_E4_B8_---099c30ec-70bd-4e5b-ae03-212de0226a25.docx
913
- try {
914
- const result = await docClient.createCollect({
915
- agent: account,
916
- formInfo: params.formInfo,
917
- spaceId: params.spaceId,
918
- fatherId: params.fatherId,
919
- });
920
- const title = readString(result.title);
921
- return buildToolResult({
922
- ok: true,
923
- action: "create_collect",
924
- accountId: account.accountId,
925
- formId: result.formId,
926
- title: title || undefined,
927
- summary: title ? `已创建收集表"${title}"(formId: ${result.formId})` : `已创建收集表(formId: ${result.formId})`,
928
- raw: result.raw,
929
- });
930
- } catch (err) {
931
- // 提供更详细的错误提示
932
- const errorMsg = err instanceof Error ? err.message : String(err);
933
- const hint = `
934
- 创建收集表失败。请检查以下必填项:
935
- - form_title: 收集表标题(必填)
936
- - form_question.items: 问题数组(必填,≤200 个)
937
- - 每个问题必须包含:question_id, title, pos, reply_type, must_reply
938
- - 单选/多选/下拉列表必须提供 option_item 数组
939
- - reply_type 对照表:1 文本,2 单选,3 多选,5 位置,9 图片,10 文件,11 日期,14 时间,15 下拉列表,16 体温,17 签名,18 部门,19 成员,22 时长
593
+ // Get the end index of the document
594
+ const docEndIndex = currentContent.document.end;
595
+ const currentVersion = currentContent.version;
940
596
 
941
- 错误详情:${errorMsg}`;
942
- return buildToolResult({
943
- ok: false,
944
- action: "create_collect",
945
- accountId: account.accountId,
946
- error: errorMsg,
947
- summary: "创建收集表失败",
948
- hint: hint.trim(),
949
- raw: {},
950
- });
951
- }
952
- }
953
- case "modify_collect": {
954
- const result = await docClient.modifyCollect({
955
- agent: account,
956
- oper: params.oper,
957
- formId: params.formId,
958
- formInfo: params.formInfo,
959
- });
960
- const title = readString(result.title);
961
- return buildToolResult({
962
- ok: true,
963
- action: "modify_collect",
964
- accountId: account.accountId,
965
- formId: result.formId,
966
- title: title || undefined,
967
- summary: title
968
- ? `收集表已更新(${result.oper}):"${title}"`
969
- : `收集表已更新(${result.oper})`,
970
- raw: result.raw,
971
- });
972
- }
973
- case "get_form_info": {
974
- const result = await docClient.getFormInfo({
975
- agent: account,
976
- formId: params.formId,
977
- });
978
- return buildToolResult({
979
- ok: true,
980
- action: "get_form_info",
981
- accountId: account.accountId,
982
- formId: params.formId,
983
- title: readString(result.formInfo?.form_title) || undefined,
984
- summary: summarizeFormInfo(result),
985
- raw: result.raw,
986
- });
987
- }
988
- case "get_form_answer": {
989
- const result = await docClient.getFormAnswer({
990
- agent: account,
991
- repeatedId: params.repeatedId,
992
- answerIds: params.answerIds,
993
- });
994
- return buildToolResult({
995
- ok: true,
996
- action: "get_form_answer",
997
- accountId: account.accountId,
998
- repeatedId: params.repeatedId,
999
- summary: summarizeFormAnswer(result),
1000
- raw: result.raw,
1001
- });
1002
- }
1003
- case "get_form_statistic": {
1004
- const result = await docClient.getFormStatistic({
1005
- agent: account,
1006
- requests: params.requests,
1007
- });
1008
- return buildToolResult({
1009
- ok: true,
1010
- action: "get_form_statistic",
1011
- accountId: account.accountId,
1012
- summary: summarizeFormStatistic(result),
1013
- raw: result.raw,
1014
- });
1015
- }
1016
- case "get_sheet_properties": {
1017
- const result = await docClient.getSheetProperties({
1018
- agent: account,
1019
- docId: params.docId,
1020
- });
1021
- return buildToolResult({
1022
- ok: true,
1023
- action: "get_sheet_properties",
1024
- accountId: account.accountId,
1025
- docId: params.docId,
1026
- summary: summarizeSheetProperties(result),
1027
- raw: result.raw,
1028
- });
1029
- }
1030
- case "edit_sheet_data": {
1031
- const result = await docClient.editSheetData({
1032
- agent: account,
1033
- docId: params.docId,
1034
- sheetId: params.sheetId,
1035
- startRow: params.startRow ?? 0,
1036
- startColumn: params.startColumn ?? 0,
1037
- gridData: params.gridData,
1038
- });
1039
- return buildToolResult({
1040
- ok: true,
1041
- action: "edit_sheet_data",
1042
- accountId: account.accountId,
1043
- docId: result.docId,
1044
- summary: "在线表格数据已编辑",
1045
- raw: result.raw,
1046
- });
1047
- }
1048
- case "get_sheet_data": {
1049
- const result = await docClient.getSheetData({
1050
- agent: account,
1051
- docId: params.docId,
1052
- sheetId: params.sheetId,
1053
- range: params.range,
1054
- });
1055
- return buildToolResult({
1056
- ok: true,
1057
- action: "get_sheet_data",
1058
- accountId: account.accountId,
1059
- docId: params.docId,
1060
- summary: "在线表格数据已读取",
1061
- data: result.data,
1062
- raw: result.raw,
1063
- });
1064
- }
1065
- case "modify_sheet_properties": {
1066
- const result = await docClient.modifySheetProperties({
1067
- agent: account,
1068
- docId: params.docId,
1069
- requests: params.requests,
1070
- });
1071
- return buildToolResult({
1072
- ok: true,
1073
- action: "modify_sheet_properties",
1074
- accountId: account.accountId,
1075
- docId: result.docId,
1076
- summary: "在线表格属性已修改",
1077
- raw: result.raw,
1078
- });
1079
- }
1080
- case "smartsheet_add_records": {
1081
- const result = await docClient.smartTableOperate({
1082
- agent: account,
1083
- docId: params.docId,
1084
- operation: "add_records",
1085
- bodyData: params,
1086
- });
1087
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已添加", raw: result.raw });
1088
- }
1089
- case "smartsheet_update_records": {
1090
- const result = await docClient.smartTableOperate({
1091
- agent: account,
1092
- docId: params.docId,
1093
- operation: "update_records",
1094
- bodyData: params,
1095
- });
1096
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已更新", raw: result.raw });
1097
- }
1098
- case "smartsheet_del_records": {
1099
- const result = await docClient.smartTableOperate({
1100
- agent: account,
1101
- docId: params.docId,
1102
- operation: "del_records",
1103
- bodyData: params,
1104
- });
1105
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已删除", raw: result.raw });
1106
- }
1107
- case "smartsheet_get_records": {
1108
- const result = await docClient.smartTableOperate({
1109
- agent: account,
1110
- docId: params.docId,
1111
- operation: "get_records",
1112
- bodyData: params,
1113
- });
1114
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已获取", raw: result.raw });
1115
- }
1116
- case "smartsheet_add_sheet": {
1117
- const result = await docClient.smartTableAddSheet({ agent: account, ...params });
1118
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格子表已添加", raw: result.raw });
1119
- }
1120
- case "smartsheet_del_sheet": {
1121
- const result = await docClient.smartTableDelSheet({ agent: account, ...params });
1122
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格子表已删除", raw: result.raw });
1123
- }
1124
- case "smartsheet_update_sheet": {
1125
- const result = await docClient.smartTableUpdateSheet({ agent: account, ...params });
1126
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格子表已更新", raw: result.raw });
1127
- }
1128
- case "smartsheet_add_view": {
1129
- const result = await docClient.smartTableAddView({ agent: account, ...params });
1130
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格视图已添加", raw: result.raw });
1131
- }
1132
- case "smartsheet_del_view": {
1133
- const result = await docClient.smartTableDelView({ agent: account, ...params });
1134
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格视图已删除", raw: result.raw });
1135
- }
1136
- case "smartsheet_get_views": {
1137
- const result = await docClient.smartTableOperate({ agent: account, docId: params.docId, operation: "get_views", bodyData: { sheet_id: params.sheetId } });
1138
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格视图列表已获取", raw: result.raw });
1139
- }
1140
- case "smartsheet_add_fields": {
1141
- const result = await docClient.smartTableAddFields({ agent: account, ...params });
1142
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格字段已添加", raw: result.raw });
1143
- }
1144
- case "smartsheet_del_fields": {
1145
- const result = await docClient.smartTableDelFields({ agent: account, ...params });
1146
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格字段已删除", raw: result.raw });
1147
- }
1148
- case "smartsheet_update_fields": {
1149
- const result = await docClient.smartTableUpdateFields({ agent: account, ...params });
1150
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格字段已更新", raw: result.raw });
1151
- }
1152
- case "smartsheet_update_view": {
1153
- const result = await docClient.smartTableUpdateView({ agent: account, ...params });
1154
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格视图已更新", raw: result.raw });
1155
- }
1156
- case "smartsheet_get_fields": {
1157
- const result = await docClient.smartTableOperate({ agent: account, docId: params.docId, operation: "get_fields", bodyData: { sheet_id: params.sheetId, view_id: params.view_id } });
1158
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格字段列表已获取", raw: result.raw });
1159
- }
1160
- case "smartsheet_add_group": {
1161
- const result = await docClient.smartTableAddGroup({ agent: account, ...params });
1162
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格编组已添加", raw: result.raw });
1163
- }
1164
- case "smartsheet_del_group": {
1165
- const result = await docClient.smartTableDelGroup({ agent: account, ...params });
1166
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格编组已删除", raw: result.raw });
1167
- }
1168
- case "smartsheet_update_group": {
1169
- const result = await docClient.smartTableUpdateGroup({ agent: account, ...params });
1170
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格编组已更新", raw: result.raw });
1171
- }
1172
- case "smartsheet_get_groups": {
1173
- const result = await docClient.smartTableGetGroups({ agent: account, ...params });
1174
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格编组列表已获取", raw: result.raw });
1175
- }
1176
- case "smartsheet_add_external_records": {
1177
- const result = await docClient.smartTableAddExternalRecords({ agent: account, ...params });
1178
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格外部记录已添加", raw: result.raw });
1179
- }
1180
- case "smartsheet_update_external_records": {
1181
- const result = await docClient.smartTableUpdateExternalRecords({ agent: account, ...params });
1182
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格外部记录已更新", raw: result.raw });
1183
- }
1184
- case "smartsheet_add_records": {
1185
- const result = await docClient.smartTableAddRecords({ agent: account, ...params });
1186
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已添加", raw: result.raw });
1187
- }
1188
- case "smartsheet_update_records": {
1189
- const result = await docClient.smartTableUpdateRecords({ agent: account, ...params });
1190
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已更新", raw: result.raw });
1191
- }
1192
- case "smartsheet_del_records": {
1193
- const result = await docClient.smartTableDelRecords({ agent: account, ...params });
1194
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录已删除", raw: result.raw });
1195
- }
1196
- case "smartsheet_get_records": {
1197
- const result = await docClient.smartTableGetRecords({ agent: account, ...params });
1198
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格记录列表已获取", raw: result.raw });
1199
- }
1200
- case "smartsheet_get_sheets": {
1201
- const result = await docClient.smartTableGetSheets({
1202
- agent: account,
1203
- docId: params.docId,
1204
- });
1205
- return buildToolResult({
1206
- ok: true,
1207
- action: "smartsheet_get_sheets",
1208
- accountId: account.accountId,
1209
- docId: params.docId,
1210
- summary: `智能表格子表列表已获取:${result.sheets.length} 个`,
1211
- raw: result.raw,
1212
- });
1213
- }
1214
- case "smartsheet_get_sheet_priv": {
1215
- const result = await docClient.smartTableGetSheetPriv({ agent: account, ...params });
1216
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格子表权限已获取", raw: result.raw });
1217
- }
1218
- case "smartsheet_update_sheet_priv": {
1219
- const result = await docClient.smartTableUpdateSheetPriv({ agent: account, ...params });
1220
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格子表权限已更新", raw: result.raw });
1221
- }
1222
- case "smartsheet_create_rule": {
1223
- const result = await docClient.smartTableCreateRule({ agent: account, ...params });
1224
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: `智能表格成员额外权限规则已创建 (rule_id: ${result.rule_id})`, raw: result.raw });
1225
- }
1226
- case "smartsheet_mod_rule_member": {
1227
- const result = await docClient.smartTableModRuleMember({ agent: account, ...params });
1228
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格成员额外权限成员已更新", raw: result.raw });
1229
- }
1230
- case "smartsheet_delete_rule": {
1231
- const result = await docClient.smartTableDeleteRule({ agent: account, ...params });
1232
- return buildToolResult({ ok: true, action, accountId: account.accountId, docId: params.docId, summary: "智能表格成员额外权限规则已删除", raw: result.raw });
1233
- }
1234
- case "doc_assign_advanced_account": {
1235
- const result = await docClient.assignDocAdvancedAccount({ agent: account, userid_list: params.userid_list });
1236
- return buildToolResult({ ok: true, action, accountId: account.accountId, summary: summarizeAdvancedAccount(result.raw, "assign"), raw: result.raw });
1237
- }
1238
- case "doc_cancel_advanced_account": {
1239
- const result = await docClient.cancelDocAdvancedAccount({ agent: account, userid_list: params.userid_list });
1240
- return buildToolResult({ ok: true, action, accountId: account.accountId, summary: summarizeAdvancedAccount(result.raw, "cancel"), raw: result.raw });
1241
- }
1242
- case "doc_get_advanced_account_list": {
1243
- const result = await docClient.getDocAdvancedAccountList({ agent: account, ...params });
1244
- return buildToolResult({ ok: true, action, accountId: account.accountId, summary: summarizeAdvancedAccount(result, "list"), raw: result.raw });
1245
- }
1246
- case "upload_doc_image": {
1247
- const filePath = params.file_path;
1248
- if (!fs.existsSync(filePath)) {
1249
- throw new Error(`File not found: ${filePath}`);
1250
- }
1251
- const fileContent = fs.readFileSync(filePath);
1252
- const base64Content = fileContent.toString("base64");
597
+ if (isImageItem(item)) {
598
+ // Insert image
599
+ const imgUrl = getImageUrl(item);
1253
600
 
1254
- const result = await docClient.uploadDocImage({
1255
- agent: account,
1256
- docId: params.docId,
1257
- base64_content: base64Content,
601
+ try {
602
+ // Upload image to WeCom
603
+ const base64 = await getImageAsBase64(imgUrl);
604
+ const uploadResult = await docClient.uploadDocImage({
605
+ agent: account,
606
+ docId: result.docId,
607
+ base64_content: base64,
1258
608
  });
1259
- return buildToolResult({
1260
- ok: true,
1261
- action,
1262
- accountId: account.accountId,
1263
- summary: "图片上传成功",
1264
- details: {
1265
- url: result.url,
1266
- width: result.width,
1267
- height: result.height,
1268
- size: result.size,
609
+ console.log(
610
+ `[wecom-doc] Item ${i} image uploaded: ${uploadResult.width}x${uploadResult.height}`,
611
+ );
612
+
613
+ // Step 2: Create new paragraph and insert image in one batch (2 operations ≤ 30)
614
+ // Per API spec: all indices are based on the same document snapshot
615
+ // insert_paragraph at docEndIndex creates a new paragraph
616
+ // insert_image at docEndIndex + 1 inserts into the newly created paragraph
617
+ await docClient.updateDocContent({
618
+ agent: account,
619
+ docId: result.docId,
620
+ version: currentVersion, // Pass version for concurrency control
621
+ requests: [
622
+ {
623
+ insert_paragraph: {
624
+ location: { index: docEndIndex },
625
+ },
626
+ },
627
+ {
628
+ insert_image: {
629
+ image_id: uploadResult.url,
630
+ location: { index: docEndIndex + 1 },
631
+ width: uploadResult.width,
632
+ height: uploadResult.height,
633
+ },
1269
634
  },
1270
- raw: result.raw,
635
+ ],
1271
636
  });
637
+ } catch (uploadErr) {
638
+ console.error(`Failed to upload image ${imgUrl}:`, uploadErr);
639
+ throw new Error(
640
+ `Image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`,
641
+ );
642
+ }
643
+ } else {
644
+ const text = getText(item);
645
+ if (!text) continue;
646
+
647
+ // Insert text: create paragraph and insert text in one batch (2 operations ≤ 30)
648
+ // Per API spec: all indices are based on the same document snapshot
649
+ // insert_paragraph at docEndIndex creates a new paragraph
650
+ // insert_text at docEndIndex + 1 inserts into the newly created paragraph
651
+ await docClient.updateDocContent({
652
+ agent: account,
653
+ docId: result.docId,
654
+ version: currentVersion, // Pass version for concurrency control
655
+ requests: [
656
+ {
657
+ insert_paragraph: {
658
+ location: { index: docEndIndex },
659
+ },
660
+ },
661
+ {
662
+ insert_text: {
663
+ text: text,
664
+ location: { index: docEndIndex + 1 },
665
+ },
666
+ },
667
+ ],
668
+ });
1272
669
  }
1273
- default:
1274
- throw new Error(`Unsupported action: ${String(action)}`);
670
+ }
671
+ contentResult = "init_content_populated";
672
+ } catch (err) {
673
+ contentResult = `content_failed: ${err instanceof Error ? err.message : String(err)}`;
1275
674
  }
1276
- } catch (err) {
1277
- return {
1278
- content: [
1279
- {
1280
- type: "text" as const,
1281
- text: JSON.stringify({ ok: false, action: params?.action, error: err instanceof Error ? err.message : String(err) }, null, 2),
1282
- },
1283
- ],
1284
- details: {},
1285
- isError: true,
1286
- };
675
+ }
676
+
677
+ let accessResult: any = null;
678
+ if (
679
+ (Array.isArray(params.viewers) && params.viewers.length > 0) ||
680
+ explicitCollaborators.length > 0
681
+ ) {
682
+ try {
683
+ accessResult = await docClient.grantDocAccess({
684
+ agent: account,
685
+ docId: result.docId,
686
+ viewers: params.viewers,
687
+ collaborators: explicitCollaborators,
688
+ });
689
+ } catch (err) {
690
+ return buildToolResult({
691
+ ok: false,
692
+ partial: true,
693
+ action: "create",
694
+ accountId: account.accountId,
695
+ resourceType: result.docTypeLabel,
696
+ canonicalDocId: result.docId,
697
+ docId: result.docId,
698
+ title: readString(params.docName),
699
+ url: result.url || undefined,
700
+ summary: `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId}),但权限授予失败`,
701
+ usageHint: buildDocIdUsageHint(result.docId) || undefined,
702
+ error: err instanceof Error ? err.message : String(err),
703
+ raw: { create: result.raw },
704
+ });
705
+ }
706
+ }
707
+ return buildToolResult({
708
+ ok: true,
709
+ action: "create",
710
+ accountId: account.accountId,
711
+ resourceType: result.docTypeLabel,
712
+ canonicalDocId: result.docId,
713
+ docId: result.docId,
714
+ title: readString(params.docName),
715
+ url: result.url || undefined,
716
+ summary: accessResult
717
+ ? `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId});${summarizeDocAccess(accessResult)}` +
718
+ (contentResult ? `;内容填充: ${contentResult}` : "")
719
+ : `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId})` +
720
+ (contentResult ? `;内容填充: ${contentResult}` : ""),
721
+ usageHint: buildDocIdUsageHint(result.docId) || undefined,
722
+ raw: accessResult ? { create: result.raw, access: accessResult.raw } : result.raw,
723
+ });
724
+ }
725
+ case "rename": {
726
+ const result = await docClient.renameDoc({
727
+ agent: account,
728
+ docId: params.docId,
729
+ newName: params.newName,
730
+ });
731
+ return buildToolResult({
732
+ ok: true,
733
+ action: "rename",
734
+ accountId: account.accountId,
735
+ docId: result.docId,
736
+ title: result.newName,
737
+ summary: `文档已重命名为"${result.newName}"`,
738
+ raw: result.raw,
739
+ });
740
+ }
741
+ case "copy": {
742
+ const result = await docClient.copyDoc({
743
+ agent: account,
744
+ docId: params.docId,
745
+ newName: params.newName,
746
+ spaceId: params.spaceId,
747
+ fatherId: params.fatherId,
748
+ });
749
+ return buildToolResult({
750
+ ok: true,
751
+ action: "copy",
752
+ accountId: account.accountId,
753
+ docId: result.docId,
754
+ summary: `文档已成功复制,新 docId: ${result.docId}`,
755
+ raw: result.raw,
756
+ });
757
+ }
758
+ case "get_info": {
759
+ const result = await docClient.getDocBaseInfo({
760
+ agent: account,
761
+ docId: params.docId,
762
+ });
763
+ return buildToolResult({
764
+ ok: true,
765
+ action: "get_info",
766
+ accountId: account.accountId,
767
+ docId: params.docId,
768
+ title: readString(result.info?.doc_name) || undefined,
769
+ resourceType:
770
+ Number(result.info?.doc_type) === 10
771
+ ? "smart_table"
772
+ : Number(result.info?.doc_type) === 4
773
+ ? "spreadsheet"
774
+ : "doc",
775
+ summary: summarizeDocInfo(result.info),
776
+ raw: result.raw,
777
+ });
778
+ }
779
+ case "share": {
780
+ const result = await docClient.shareDoc({
781
+ agent: account,
782
+ docId: params.docId,
783
+ });
784
+ return buildToolResult({
785
+ ok: true,
786
+ action: "share",
787
+ accountId: account.accountId,
788
+ canonicalDocId: params.docId,
789
+ docId: params.docId,
790
+ url: result.shareUrl || undefined,
791
+ summary: result.shareUrl
792
+ ? `文档分享链接已获取(docId: ${params.docId})`
793
+ : `文档分享接口调用成功(docId: ${params.docId})`,
794
+ usageHint: buildDocIdUsageHint(params.docId) || undefined,
795
+ raw: result.raw,
796
+ });
797
+ }
798
+ case "get_auth": {
799
+ const result = await docClient.getDocAuth({
800
+ agent: account,
801
+ docId: params.docId,
802
+ });
803
+ const diagnosis = buildDocAuthDiagnosis(result, toolContext?.senderId);
804
+ return buildToolResult({
805
+ ok: true,
806
+ action: "get_auth",
807
+ accountId: account.accountId,
808
+ canonicalDocId: params.docId,
809
+ docId: params.docId,
810
+ summary: summarizeDocAuth(result),
811
+ diagnosis,
812
+ raw: result.raw,
813
+ });
1287
814
  }
1288
- },
1289
- }));
815
+ case "diagnose_auth": {
816
+ const result = await docClient.getDocAuth({
817
+ agent: account,
818
+ docId: params.docId,
819
+ });
820
+ const diagnosis = buildDocAuthDiagnosis(result, toolContext?.senderId);
821
+ return buildToolResult({
822
+ ok: true,
823
+ action: "diagnose_auth",
824
+ accountId: account.accountId,
825
+ canonicalDocId: params.docId,
826
+ docId: params.docId,
827
+ summary: summarizeDocAuthDiagnosis(diagnosis),
828
+ diagnosis,
829
+ raw: result.raw,
830
+ });
831
+ }
832
+ case "validate_share_link": {
833
+ const result = await inspectWecomShareLink({
834
+ shareUrl: params.shareUrl,
835
+ });
836
+ return buildToolResult({
837
+ ok: true,
838
+ action: "validate_share_link",
839
+ accountId: account.accountId,
840
+ url: result.diagnosis.finalUrl || params.shareUrl,
841
+ summary: summarizeShareLinkDiagnosis(result.diagnosis),
842
+ diagnosis: result.diagnosis,
843
+ raw: result.raw,
844
+ });
845
+ }
846
+ case "delete": {
847
+ const result = await docClient.deleteDoc({
848
+ agent: account,
849
+ docId: params.docId,
850
+ formId: params.formId,
851
+ });
852
+ return buildToolResult({
853
+ ok: true,
854
+ action: "delete",
855
+ accountId: account.accountId,
856
+ docId: result.docId || undefined,
857
+ formId: result.formId || undefined,
858
+ summary: result.formId ? "收集表已删除" : "文档已删除",
859
+ raw: result.raw,
860
+ });
861
+ }
862
+ case "set_join_rule": {
863
+ const result = await docClient.setDocJoinRule({
864
+ agent: account,
865
+ docId: params.docId,
866
+ request: params.request,
867
+ });
868
+ return buildToolResult({
869
+ ok: true,
870
+ action: "set_join_rule",
871
+ accountId: account.accountId,
872
+ docId: result.docId,
873
+ summary: "文档查看规则已更新",
874
+ raw: result.raw,
875
+ });
876
+ }
877
+ case "set_member_auth": {
878
+ const result = await docClient.setDocMemberAuth({
879
+ agent: account,
880
+ docId: params.docId,
881
+ request: params.request,
882
+ });
883
+ return buildToolResult({
884
+ ok: true,
885
+ action: "set_member_auth",
886
+ accountId: account.accountId,
887
+ docId: result.docId,
888
+ summary: "文档通知范围及成员权限已更新",
889
+ raw: result.raw,
890
+ });
891
+ }
892
+ case "grant_access": {
893
+ const result = await docClient.grantDocAccess({
894
+ agent: account,
895
+ docId: params.docId,
896
+ viewers: params.viewers,
897
+ collaborators: params.collaborators,
898
+ removeViewers: params.removeViewers,
899
+ removeCollaborators: params.removeCollaborators,
900
+ authLevel: params.auth,
901
+ });
902
+ return buildToolResult({
903
+ ok: true,
904
+ action: "grant_access",
905
+ accountId: account.accountId,
906
+ docId: result.docId,
907
+ summary: summarizeDocAccess(result),
908
+ raw: result.raw,
909
+ });
910
+ }
911
+ case "add_collaborators": {
912
+ const result = await docClient.addDocCollaborators({
913
+ agent: account,
914
+ docId: params.docId,
915
+ collaborators: params.collaborators,
916
+ auth: params.auth,
917
+ });
918
+ return buildToolResult({
919
+ ok: true,
920
+ action: "add_collaborators",
921
+ accountId: account.accountId,
922
+ docId: result.docId,
923
+ summary: `协作者已添加:${result.addedCollaboratorCount ?? 0}`,
924
+ raw: result.raw,
925
+ });
926
+ }
927
+ case "get_content": {
928
+ const result = await docClient.getDocContent({
929
+ agent: account,
930
+ docId: params.docId,
931
+ });
932
+ return buildToolResult({
933
+ ok: true,
934
+ action: "get_content",
935
+ accountId: account.accountId,
936
+ docId: params.docId,
937
+ summary: "文档内容已获取",
938
+ raw: result.raw,
939
+ });
940
+ }
941
+ case "update_content": {
942
+ const result = await docClient.updateDocContent({
943
+ agent: account,
944
+ docId: params.docId,
945
+ requests: params.requests,
946
+ version: params.version,
947
+ });
948
+
949
+ const batchCount = result.batches ?? 1;
950
+ return buildToolResult({
951
+ ok: true,
952
+ action: "update_content",
953
+ accountId: account.accountId,
954
+ docId: params.docId,
955
+ summary:
956
+ batchCount > 1
957
+ ? `文档内容已更新(自动分批,共${batchCount}批)`
958
+ : "文档内容已更新",
959
+ batches: batchCount,
960
+ raw: result.raw,
961
+ });
962
+ }
963
+ case "set_safety_setting": {
964
+ const result = await docClient.setDocSafetySetting({
965
+ agent: account,
966
+ docId: params.docId,
967
+ request: params.request,
968
+ });
969
+ return buildToolResult({
970
+ ok: true,
971
+ action: "set_safety_setting",
972
+ accountId: account.accountId,
973
+ docId: result.docId,
974
+ summary: "文档安全设置已更新",
975
+ raw: result.raw,
976
+ });
977
+ }
978
+ case "get_doc_security_setting": {
979
+ const result = await docClient.getDocAuth({
980
+ agent: account,
981
+ docId: params.docId,
982
+ });
983
+ return buildToolResult({
984
+ ok: true,
985
+ action: "get_doc_security_setting",
986
+ accountId: account.accountId,
987
+ docId: params.docId,
988
+ summary: "文档安全设置已获取",
989
+ details: result.secureSetting,
990
+ raw: result.raw,
991
+ });
992
+ }
993
+ case "mod_doc_security_setting": {
994
+ // Alias to setDocSafetySetting logic
995
+ const result = await docClient.setDocSafetySetting({
996
+ agent: account,
997
+ docId: params.docId,
998
+ request: params.setting,
999
+ });
1000
+ return buildToolResult({
1001
+ ok: true,
1002
+ action: "mod_doc_security_setting",
1003
+ accountId: account.accountId,
1004
+ docId: result.docId,
1005
+ summary: "文档安全设置已更新",
1006
+ raw: result.raw,
1007
+ });
1008
+ }
1009
+ case "mod_doc_member_notified_scope": {
1010
+ const result = await docClient.modDocMemberNotifiedScope({
1011
+ agent: account,
1012
+ docId: params.docId,
1013
+ notified_scope_type: params.notified_scope_type,
1014
+ notified_member_list: params.notified_member_list,
1015
+ });
1016
+ return buildToolResult({
1017
+ ok: true,
1018
+ action: "mod_doc_member_notified_scope",
1019
+ accountId: account.accountId,
1020
+ docId: params.docId,
1021
+ summary: "文档成员通知范围已更新",
1022
+ raw: result,
1023
+ });
1024
+ }
1025
+ case "create_form": {
1026
+ // 创建收集表(表单)
1027
+ try {
1028
+ const result = await docClient.createCollect({
1029
+ agent: account,
1030
+ formInfo: params.formInfo,
1031
+ spaceId: params.spaceId,
1032
+ fatherId: params.fatherId,
1033
+ });
1034
+ const title = readString(result.title);
1035
+ return buildToolResult({
1036
+ ok: true,
1037
+ action: "create_form",
1038
+ accountId: account.accountId,
1039
+ formId: result.formId,
1040
+ title: title || undefined,
1041
+ summary: title
1042
+ ? `已创建收集表"${title}"(formId: ${result.formId})`
1043
+ : `已创建收集表(formId: ${result.formId})`,
1044
+ raw: result.raw,
1045
+ });
1046
+ } catch (err) {
1047
+ const errorMsg = err instanceof Error ? err.message : String(err);
1048
+ return buildToolResult({
1049
+ ok: false,
1050
+ action: "create_form",
1051
+ accountId: account.accountId,
1052
+ error: errorMsg,
1053
+ summary: "创建收集表失败",
1054
+ raw: {},
1055
+ });
1056
+ }
1057
+ }
1058
+ case "modify_form": {
1059
+ const result = await docClient.modifyCollect({
1060
+ agent: account,
1061
+ oper: params.oper,
1062
+ formId: params.formId,
1063
+ formInfo: params.formInfo,
1064
+ });
1065
+ const title = readString(result.title);
1066
+ return buildToolResult({
1067
+ ok: true,
1068
+ action: "modify_form",
1069
+ accountId: account.accountId,
1070
+ formId: result.formId,
1071
+ title: title || undefined,
1072
+ summary: title
1073
+ ? `收集表已更新(${result.oper}):"${title}"`
1074
+ : `收集表已更新(${result.oper})`,
1075
+ raw: result.raw,
1076
+ });
1077
+ }
1078
+ case "get_form_info": {
1079
+ const result = await docClient.getFormInfo({
1080
+ agent: account,
1081
+ formId: params.formId,
1082
+ });
1083
+ return buildToolResult({
1084
+ ok: true,
1085
+ action: "get_form_info",
1086
+ accountId: account.accountId,
1087
+ formId: params.formId,
1088
+ title: readString(result.formInfo?.form_title) || undefined,
1089
+ summary: summarizeFormInfo(result),
1090
+ raw: result.raw,
1091
+ });
1092
+ }
1093
+ case "get_form_answer": {
1094
+ const result = await docClient.getFormAnswer({
1095
+ agent: account,
1096
+ repeatedId: params.repeatedId,
1097
+ answerIds: params.answerIds,
1098
+ });
1099
+ return buildToolResult({
1100
+ ok: true,
1101
+ action: "get_form_answer",
1102
+ accountId: account.accountId,
1103
+ repeatedId: params.repeatedId,
1104
+ summary: summarizeFormAnswer(result),
1105
+ raw: result.raw,
1106
+ });
1107
+ }
1108
+ case "get_form_statistic": {
1109
+ const result = await docClient.getFormStatistic({
1110
+ agent: account,
1111
+ requests: params.requests,
1112
+ });
1113
+ return buildToolResult({
1114
+ ok: true,
1115
+ action: "get_form_statistic",
1116
+ accountId: account.accountId,
1117
+ summary: summarizeFormStatistic(result),
1118
+ raw: result.raw,
1119
+ });
1120
+ }
1121
+ case "get_sheet_properties": {
1122
+ const result = await docClient.getSheetProperties({
1123
+ agent: account,
1124
+ docId: params.docId,
1125
+ });
1126
+ return buildToolResult({
1127
+ ok: true,
1128
+ action: "get_sheet_properties",
1129
+ accountId: account.accountId,
1130
+ docId: params.docId,
1131
+ summary: summarizeSheetProperties(result),
1132
+ raw: result.raw,
1133
+ });
1134
+ }
1135
+ case "edit_sheet_data": {
1136
+ const result = await docClient.editSheetData({
1137
+ agent: account,
1138
+ docId: params.docId,
1139
+ sheetId: params.sheetId,
1140
+ startRow: params.startRow ?? 0,
1141
+ startColumn: params.startColumn ?? 0,
1142
+ gridData: params.gridData,
1143
+ requests: params.requests, // Support direct requests for multiple operations
1144
+ });
1145
+ const summary = result.operations
1146
+ ? `在线表格已更新(${result.operations}个操作)`
1147
+ : "在线表格数据已编辑";
1148
+ return buildToolResult({
1149
+ ok: true,
1150
+ action: "edit_sheet_data",
1151
+ accountId: account.accountId,
1152
+ docId: result.docId,
1153
+ summary: summary,
1154
+ operations: result.operations,
1155
+ updatedCells: result.updatedCells,
1156
+ raw: result.raw,
1157
+ });
1158
+ }
1159
+ case "get_sheet_data": {
1160
+ const result = await docClient.getSheetData({
1161
+ agent: account,
1162
+ docId: params.docId,
1163
+ sheetId: params.sheetId,
1164
+ range: params.range,
1165
+ });
1166
+ return buildToolResult({
1167
+ ok: true,
1168
+ action: "get_sheet_data",
1169
+ accountId: account.accountId,
1170
+ docId: params.docId,
1171
+ summary: "在线表格数据已读取",
1172
+ data: result.data,
1173
+ raw: result.raw,
1174
+ });
1175
+ }
1176
+ case "modify_sheet_properties": {
1177
+ const result = await docClient.modifySheetProperties({
1178
+ agent: account,
1179
+ docId: params.docId,
1180
+ requests: params.requests,
1181
+ });
1182
+ return buildToolResult({
1183
+ ok: true,
1184
+ action: "modify_sheet_properties",
1185
+ accountId: account.accountId,
1186
+ docId: result.docId,
1187
+ summary: "在线表格属性已修改",
1188
+ raw: result.raw,
1189
+ });
1190
+ }
1191
+ case "smartsheet_add_records": {
1192
+ const result = await docClient.smartTableOperate({
1193
+ agent: account,
1194
+ docId: params.docId,
1195
+ operation: "add_records",
1196
+ bodyData: params,
1197
+ });
1198
+ return buildToolResult({
1199
+ ok: true,
1200
+ action,
1201
+ accountId: account.accountId,
1202
+ docId: params.docId,
1203
+ summary: "智能表格记录已添加",
1204
+ raw: result.raw,
1205
+ });
1206
+ }
1207
+ case "smartsheet_update_records": {
1208
+ const result = await docClient.smartTableOperate({
1209
+ agent: account,
1210
+ docId: params.docId,
1211
+ operation: "update_records",
1212
+ bodyData: params,
1213
+ });
1214
+ return buildToolResult({
1215
+ ok: true,
1216
+ action,
1217
+ accountId: account.accountId,
1218
+ docId: params.docId,
1219
+ summary: "智能表格记录已更新",
1220
+ raw: result.raw,
1221
+ });
1222
+ }
1223
+ case "smartsheet_del_records": {
1224
+ const result = await docClient.smartTableOperate({
1225
+ agent: account,
1226
+ docId: params.docId,
1227
+ operation: "del_records",
1228
+ bodyData: params,
1229
+ });
1230
+ return buildToolResult({
1231
+ ok: true,
1232
+ action,
1233
+ accountId: account.accountId,
1234
+ docId: params.docId,
1235
+ summary: "智能表格记录已删除",
1236
+ raw: result.raw,
1237
+ });
1238
+ }
1239
+ case "smartsheet_get_records": {
1240
+ const result = await docClient.smartTableGetRecords({ agent: account, ...params });
1241
+ return buildToolResult({
1242
+ ok: true,
1243
+ action,
1244
+ accountId: account.accountId,
1245
+ docId: params.docId,
1246
+ summary: result.records?.length
1247
+ ? `智能表格记录已获取:${result.records.length} 条`
1248
+ : "智能表格记录列表已获取",
1249
+ total: result.total,
1250
+ has_more: result.has_more,
1251
+ ver: result.ver,
1252
+ raw: result.raw,
1253
+ });
1254
+ }
1255
+ case "smartsheet_get_views": {
1256
+ const result = await docClient.smartTableGetViews({ agent: account, ...params });
1257
+ return buildToolResult({
1258
+ ok: true,
1259
+ action,
1260
+ accountId: account.accountId,
1261
+ docId: params.docId,
1262
+ summary: result.views?.length
1263
+ ? `智能表格视图已获取:${result.views.length} 个`
1264
+ : "智能表格视图列表已获取",
1265
+ total: result.total,
1266
+ has_more: result.has_more,
1267
+ raw: result.raw,
1268
+ });
1269
+ }
1270
+ case "smartsheet_get_sheets": {
1271
+ const result = await docClient.smartTableGetSheets({
1272
+ agent: account,
1273
+ docId: params.docId,
1274
+ sheet_id: params.sheet_id,
1275
+ need_all_type_sheet: params.need_all_type_sheet,
1276
+ });
1277
+ return buildToolResult({
1278
+ ok: true,
1279
+ action: "smartsheet_get_sheets",
1280
+ accountId: account.accountId,
1281
+ docId: params.docId,
1282
+ summary: `智能表格子表列表已获取:${result.sheets.length} 个`,
1283
+ raw: result.raw,
1284
+ });
1285
+ }
1286
+ case "smartsheet_add_sheet": {
1287
+ const result = await docClient.smartTableAddSheet({ agent: account, ...params });
1288
+ return buildToolResult({
1289
+ ok: true,
1290
+ action,
1291
+ accountId: account.accountId,
1292
+ docId: params.docId,
1293
+ summary: "智能表格子表已添加",
1294
+ raw: result.raw,
1295
+ });
1296
+ }
1297
+ case "smartsheet_del_sheet": {
1298
+ const result = await docClient.smartTableDelSheet({ agent: account, ...params });
1299
+ return buildToolResult({
1300
+ ok: true,
1301
+ action,
1302
+ accountId: account.accountId,
1303
+ docId: params.docId,
1304
+ summary: "智能表格子表已删除",
1305
+ raw: result.raw,
1306
+ });
1307
+ }
1308
+ case "smartsheet_update_sheet": {
1309
+ const result = await docClient.smartTableUpdateSheet({ agent: account, ...params });
1310
+ return buildToolResult({
1311
+ ok: true,
1312
+ action,
1313
+ accountId: account.accountId,
1314
+ docId: params.docId,
1315
+ summary: "智能表格子表已更新",
1316
+ raw: result.raw,
1317
+ });
1318
+ }
1319
+ case "smartsheet_add_view": {
1320
+ const result = await docClient.smartTableAddView({ agent: account, ...params });
1321
+ return buildToolResult({
1322
+ ok: true,
1323
+ action,
1324
+ accountId: account.accountId,
1325
+ docId: params.docId,
1326
+ summary: `智能表格视图已添加:${result.view?.view_title || "未知"}`,
1327
+ raw: result.raw,
1328
+ });
1329
+ }
1330
+ case "smartsheet_del_view": {
1331
+ const result = await docClient.smartTableDelView({ agent: account, ...params });
1332
+ return buildToolResult({
1333
+ ok: true,
1334
+ action,
1335
+ accountId: account.accountId,
1336
+ docId: params.docId,
1337
+ summary: `智能表格视图已删除:${params.view_ids?.length || 0} 个`,
1338
+ raw: result.raw,
1339
+ });
1340
+ }
1341
+ case "smartsheet_update_view": {
1342
+ const result = await docClient.smartTableUpdateView({ agent: account, ...params });
1343
+ return buildToolResult({
1344
+ ok: true,
1345
+ action,
1346
+ accountId: account.accountId,
1347
+ docId: params.docId,
1348
+ summary: `智能表格视图已更新:${result.view?.view_title || "未知"}`,
1349
+ raw: result.raw,
1350
+ });
1351
+ }
1352
+ case "smartsheet_add_fields": {
1353
+ const result = await docClient.smartTableAddFields({ agent: account, ...params });
1354
+ return buildToolResult({
1355
+ ok: true,
1356
+ action,
1357
+ accountId: account.accountId,
1358
+ docId: params.docId,
1359
+ summary: `智能表格字段已添加:${result.fields?.length || 0} 个`,
1360
+ raw: result.raw,
1361
+ });
1362
+ }
1363
+ case "smartsheet_del_fields": {
1364
+ const result = await docClient.smartTableDelFields({ agent: account, ...params });
1365
+ return buildToolResult({
1366
+ ok: true,
1367
+ action,
1368
+ accountId: account.accountId,
1369
+ docId: params.docId,
1370
+ summary: `智能表格字段已删除:${params.field_ids?.length || 0} 个`,
1371
+ raw: result.raw,
1372
+ });
1373
+ }
1374
+ case "smartsheet_update_fields": {
1375
+ const result = await docClient.smartTableUpdateFields({ agent: account, ...params });
1376
+ return buildToolResult({
1377
+ ok: true,
1378
+ action,
1379
+ accountId: account.accountId,
1380
+ docId: params.docId,
1381
+ summary: `智能表格字段已更新:${result.fields?.length || 0} 个`,
1382
+ raw: result.raw,
1383
+ });
1384
+ }
1385
+ case "smartsheet_get_fields": {
1386
+ const result = await docClient.smartTableGetFields({ agent: account, ...params });
1387
+ return buildToolResult({
1388
+ ok: true,
1389
+ action,
1390
+ accountId: account.accountId,
1391
+ docId: params.docId,
1392
+ summary: result.fields?.length
1393
+ ? `智能表格字段已获取:${result.fields.length} 个`
1394
+ : "智能表格字段列表已获取",
1395
+ total: result.total,
1396
+ has_more: result.has_more,
1397
+ raw: result.raw,
1398
+ });
1399
+ }
1400
+ case "smartsheet_add_group": {
1401
+ const result = await docClient.smartTableAddGroup({ agent: account, ...params });
1402
+ return buildToolResult({
1403
+ ok: true,
1404
+ action,
1405
+ accountId: account.accountId,
1406
+ docId: params.docId,
1407
+ summary: "智能表格编组已添加",
1408
+ raw: result.raw,
1409
+ });
1410
+ }
1411
+ case "smartsheet_del_group": {
1412
+ const result = await docClient.smartTableDelGroup({ agent: account, ...params });
1413
+ return buildToolResult({
1414
+ ok: true,
1415
+ action,
1416
+ accountId: account.accountId,
1417
+ docId: params.docId,
1418
+ summary: "智能表格编组已删除",
1419
+ raw: result.raw,
1420
+ });
1421
+ }
1422
+ case "smartsheet_update_group": {
1423
+ const result = await docClient.smartTableUpdateGroup({ agent: account, ...params });
1424
+ return buildToolResult({
1425
+ ok: true,
1426
+ action,
1427
+ accountId: account.accountId,
1428
+ docId: params.docId,
1429
+ summary: "智能表格编组已更新",
1430
+ raw: result.raw,
1431
+ });
1432
+ }
1433
+ case "smartsheet_get_groups": {
1434
+ const result = await docClient.smartTableGetGroups({ agent: account, ...params });
1435
+ return buildToolResult({
1436
+ ok: true,
1437
+ action,
1438
+ accountId: account.accountId,
1439
+ docId: params.docId,
1440
+ summary: "智能表格编组列表已获取",
1441
+ raw: result.raw,
1442
+ });
1443
+ }
1444
+ case "smartsheet_add_external_records": {
1445
+ const result = await docClient.smartTableAddExternalRecords({
1446
+ agent: account,
1447
+ ...params,
1448
+ });
1449
+ return buildToolResult({
1450
+ ok: true,
1451
+ action,
1452
+ accountId: account.accountId,
1453
+ docId: params.docId,
1454
+ summary: "智能表格外部记录已添加",
1455
+ raw: result.raw,
1456
+ });
1457
+ }
1458
+ case "smartsheet_update_external_records": {
1459
+ const result = await docClient.smartTableUpdateExternalRecords({
1460
+ agent: account,
1461
+ ...params,
1462
+ });
1463
+ return buildToolResult({
1464
+ ok: true,
1465
+ action,
1466
+ accountId: account.accountId,
1467
+ docId: params.docId,
1468
+ summary: "智能表格外部记录已更新",
1469
+ raw: result.raw,
1470
+ });
1471
+ }
1472
+ case "smartsheet_get_sheet_priv": {
1473
+ const result = await docClient.smartTableGetSheetPriv({ agent: account, ...params });
1474
+ return buildToolResult({
1475
+ ok: true,
1476
+ action,
1477
+ accountId: account.accountId,
1478
+ docId: params.docId,
1479
+ summary: "智能表格子表权限已获取",
1480
+ raw: result.raw,
1481
+ });
1482
+ }
1483
+ case "smartsheet_update_sheet_priv": {
1484
+ const result = await docClient.smartTableUpdateSheetPriv({
1485
+ agent: account,
1486
+ ...params,
1487
+ });
1488
+ return buildToolResult({
1489
+ ok: true,
1490
+ action,
1491
+ accountId: account.accountId,
1492
+ docId: params.docId,
1493
+ summary: "智能表格子表权限已更新",
1494
+ raw: result.raw,
1495
+ });
1496
+ }
1497
+ case "smartsheet_create_rule": {
1498
+ const result = await docClient.smartTableCreateRule({ agent: account, ...params });
1499
+ return buildToolResult({
1500
+ ok: true,
1501
+ action,
1502
+ accountId: account.accountId,
1503
+ docId: params.docId,
1504
+ summary: `智能表格成员额外权限规则已创建 (rule_id: ${result.rule_id})`,
1505
+ raw: result.raw,
1506
+ });
1507
+ }
1508
+ case "smartsheet_mod_rule_member": {
1509
+ const result = await docClient.smartTableModRuleMember({ agent: account, ...params });
1510
+ return buildToolResult({
1511
+ ok: true,
1512
+ action,
1513
+ accountId: account.accountId,
1514
+ docId: params.docId,
1515
+ summary: "智能表格成员额外权限成员已更新",
1516
+ raw: result.raw,
1517
+ });
1518
+ }
1519
+ case "smartsheet_delete_rule": {
1520
+ const result = await docClient.smartTableDeleteRule({ agent: account, ...params });
1521
+ return buildToolResult({
1522
+ ok: true,
1523
+ action,
1524
+ accountId: account.accountId,
1525
+ docId: params.docId,
1526
+ summary: "智能表格成员额外权限规则已删除",
1527
+ raw: result.raw,
1528
+ });
1529
+ }
1530
+ case "doc_assign_advanced_account": {
1531
+ const result = await docClient.assignDocAdvancedAccount({
1532
+ agent: account,
1533
+ userid_list: params.userid_list,
1534
+ });
1535
+ return buildToolResult({
1536
+ ok: true,
1537
+ action,
1538
+ accountId: account.accountId,
1539
+ summary: summarizeAdvancedAccount(result.raw, "assign"),
1540
+ raw: result.raw,
1541
+ });
1542
+ }
1543
+ case "doc_cancel_advanced_account": {
1544
+ const result = await docClient.cancelDocAdvancedAccount({
1545
+ agent: account,
1546
+ userid_list: params.userid_list,
1547
+ });
1548
+ return buildToolResult({
1549
+ ok: true,
1550
+ action,
1551
+ accountId: account.accountId,
1552
+ summary: summarizeAdvancedAccount(result.raw, "cancel"),
1553
+ raw: result.raw,
1554
+ });
1555
+ }
1556
+ case "doc_get_advanced_account_list": {
1557
+ const result = await docClient.getDocAdvancedAccountList({
1558
+ agent: account,
1559
+ ...params,
1560
+ });
1561
+ return buildToolResult({
1562
+ ok: true,
1563
+ action,
1564
+ accountId: account.accountId,
1565
+ summary: summarizeAdvancedAccount(result, "list"),
1566
+ raw: result.raw,
1567
+ });
1568
+ }
1569
+ case "upload_doc_image": {
1570
+ const filePath = params.file_path;
1571
+ if (!fs.existsSync(filePath)) {
1572
+ throw new Error(`File not found: ${filePath}`);
1573
+ }
1574
+ const fileContent = fs.readFileSync(filePath);
1575
+ const base64Content = fileContent.toString("base64");
1576
+
1577
+ const result = await docClient.uploadDocImage({
1578
+ agent: account,
1579
+ docId: params.docId,
1580
+ base64_content: base64Content,
1581
+ });
1582
+ return buildToolResult({
1583
+ ok: true,
1584
+ action,
1585
+ accountId: account.accountId,
1586
+ summary: "图片上传成功",
1587
+ details: {
1588
+ url: result.url,
1589
+ width: result.width,
1590
+ height: result.height,
1591
+ size: result.size,
1592
+ },
1593
+ raw: result.raw,
1594
+ });
1595
+ }
1596
+ default:
1597
+ throw new Error(`Unsupported action: ${String(action)}`);
1598
+ }
1599
+ } catch (err) {
1600
+ return {
1601
+ content: [
1602
+ {
1603
+ type: "text" as const,
1604
+ text: JSON.stringify(
1605
+ {
1606
+ ok: false,
1607
+ action: params?.action,
1608
+ error: err instanceof Error ? err.message : String(err),
1609
+ },
1610
+ null,
1611
+ 2,
1612
+ ),
1613
+ },
1614
+ ],
1615
+ details: {},
1616
+ isError: true,
1617
+ };
1618
+ }
1619
+ },
1620
+ };
1621
+ });
1290
1622
  }