@yanhaidao/wecom 2.4.160 → 2.5.110

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 (313) hide show
  1. package/dist/index.js +68 -0
  2. package/dist/src/accounts.js +20 -0
  3. package/dist/src/agent/handler.js +895 -0
  4. package/dist/src/agent/index.js +5 -0
  5. package/dist/src/app/account-runtime.js +216 -0
  6. package/dist/src/app/bootstrap.js +19 -0
  7. package/dist/src/app/index.js +118 -0
  8. package/dist/src/capability/agent/delivery-service.js +63 -0
  9. package/dist/src/capability/agent/fallback-policy.js +6 -0
  10. package/dist/src/capability/agent/ingress-service.js +33 -0
  11. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  12. package/dist/src/capability/bot/dispatch-config.js +45 -0
  13. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  14. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  15. package/dist/src/capability/bot/sandbox-media.js +138 -0
  16. package/dist/src/capability/bot/service.js +49 -0
  17. package/dist/src/capability/bot/stream-delivery.js +321 -0
  18. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  19. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  20. package/dist/src/capability/bot/types.js +1 -0
  21. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  22. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  23. package/dist/src/capability/calendar/tool.js +365 -0
  24. package/dist/src/capability/calendar/types.js +12 -0
  25. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  26. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  27. package/dist/src/capability/doc/tool.js +1556 -0
  28. package/dist/src/capability/doc/types.js +113 -0
  29. package/dist/src/capability/mcp/index.js +3 -0
  30. package/dist/src/capability/mcp/schema.js +102 -0
  31. package/dist/src/capability/mcp/tool.js +146 -0
  32. package/dist/src/capability/mcp/transport.js +293 -0
  33. package/dist/src/channel.js +224 -0
  34. package/dist/src/config/accounts.js +236 -0
  35. package/dist/src/config/derived-paths.js +31 -0
  36. package/dist/src/config/index.js +7 -0
  37. package/dist/src/config/media.js +110 -0
  38. package/dist/src/config/network.js +32 -0
  39. package/dist/src/config/routing.js +20 -0
  40. package/dist/src/config/runtime-config.js +25 -0
  41. package/dist/src/config/schema.js +4 -0
  42. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  43. package/dist/src/context-store.js +219 -0
  44. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  45. package/dist/src/crypto/index.js +9 -0
  46. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  47. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  48. package/dist/src/crypto.js +145 -0
  49. package/dist/src/domain/models.js +1 -0
  50. package/dist/src/domain/policies.js +32 -0
  51. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  52. package/dist/src/gateway-monitor.js +139 -0
  53. package/dist/src/http.js +114 -0
  54. package/{src/media.ts → dist/src/media.js} +21 -40
  55. package/dist/src/monitor/limits.js +7 -0
  56. package/dist/src/monitor/state.js +28 -0
  57. package/dist/src/monitor.js +84 -0
  58. package/dist/src/observability/audit-log.js +30 -0
  59. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  60. package/dist/src/observability/raw-envelope-log.js +24 -0
  61. package/dist/src/observability/status-registry.js +9 -0
  62. package/dist/src/observability/transport-session-view.js +14 -0
  63. package/dist/src/onboarding.js +546 -0
  64. package/dist/src/outbound.js +557 -0
  65. package/dist/src/runtime/dispatcher.js +57 -0
  66. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  67. package/dist/src/runtime/outbound-intent.js +1 -0
  68. package/dist/src/runtime/reply-orchestrator.js +38 -0
  69. package/dist/src/runtime/routing-bridge.js +26 -0
  70. package/dist/src/runtime/session-manager.js +112 -0
  71. package/dist/src/runtime/source-registry.js +174 -0
  72. package/dist/src/runtime.js +1 -0
  73. package/dist/src/shared/command-auth.js +57 -0
  74. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  75. package/dist/src/shared/media-asset.js +65 -0
  76. package/dist/src/shared/media-service.js +59 -0
  77. package/dist/src/shared/media-types.js +1 -0
  78. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  79. package/dist/src/store/active-reply-store.js +41 -0
  80. package/dist/src/store/interfaces.js +1 -0
  81. package/dist/src/store/memory-store.js +33 -0
  82. package/dist/src/store/stream-batch-store.js +319 -0
  83. package/{src/target.ts → dist/src/target.js} +15 -48
  84. package/dist/src/transport/agent-api/client.js +168 -0
  85. package/dist/src/transport/agent-api/core.js +337 -0
  86. package/dist/src/transport/agent-api/delivery.js +28 -0
  87. package/dist/src/transport/agent-api/media-upload.js +4 -0
  88. package/dist/src/transport/agent-api/reply.js +24 -0
  89. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  90. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  91. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  92. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  93. package/dist/src/transport/agent-callback/inbound.js +4 -0
  94. package/dist/src/transport/agent-callback/reply.js +8 -0
  95. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  96. package/dist/src/transport/agent-callback/session.js +15 -0
  97. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  98. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  99. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  100. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  101. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  102. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  103. package/dist/src/transport/bot-webhook/reply.js +9 -0
  104. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  105. package/dist/src/transport/bot-webhook/session.js +15 -0
  106. package/dist/src/transport/bot-ws/inbound.js +147 -0
  107. package/dist/src/transport/bot-ws/media.js +236 -0
  108. package/dist/src/transport/bot-ws/reply.js +310 -0
  109. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  110. package/dist/src/transport/bot-ws/session.js +15 -0
  111. package/dist/src/transport/http/common.js +78 -0
  112. package/dist/src/transport/http/registry.js +71 -0
  113. package/dist/src/transport/http/request-handler.js +51 -0
  114. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  115. package/dist/src/types/account.js +1 -0
  116. package/dist/src/types/config.js +1 -0
  117. package/dist/src/types/constants.js +28 -0
  118. package/dist/src/types/events.js +1 -0
  119. package/dist/src/types/index.js +1 -0
  120. package/dist/src/types/legacy-stream.js +1 -0
  121. package/dist/src/types/message.js +5 -0
  122. package/dist/src/types/runtime-context.js +1 -0
  123. package/dist/src/types/runtime.js +1 -0
  124. package/dist/src/types.js +1 -0
  125. package/dist/src/upstream/index.js +111 -0
  126. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  127. package/openclaw.plugin.json +15 -0
  128. package/package.json +18 -1
  129. package/.github/workflows/release.yml +0 -143
  130. package/GOVERNANCE.md +0 -26
  131. package/SKILLS_CAL.md +0 -895
  132. package/SKILLS_DOC.md +0 -2288
  133. package/UPSTREAM_CONFIG.md +0 -170
  134. package/UPSTREAM_PLAN.md +0 -175
  135. package/assets/01.bot-add.png +0 -0
  136. package/assets/01.bot-setp2.png +0 -0
  137. package/assets/01.image.jpg +0 -0
  138. package/assets/02.agent.add.png +0 -0
  139. package/assets/02.agent.api-set.png +0 -0
  140. package/assets/02.image.jpg +0 -0
  141. package/assets/03.agent.page.png +0 -0
  142. package/assets/03.bot.page.png +0 -0
  143. package/assets/link-me.jpg +0 -0
  144. package/assets/register.png +0 -0
  145. package/changelog/v2.2.28.md +0 -70
  146. package/changelog/v2.3.10.md +0 -17
  147. package/changelog/v2.3.11.md +0 -19
  148. package/changelog/v2.3.12.md +0 -25
  149. package/changelog/v2.3.13.md +0 -19
  150. package/changelog/v2.3.14.md +0 -48
  151. package/changelog/v2.3.15.md +0 -15
  152. package/changelog/v2.3.16.md +0 -11
  153. package/changelog/v2.3.18.md +0 -22
  154. package/changelog/v2.3.19.md +0 -73
  155. package/changelog/v2.3.2.md +0 -28
  156. package/changelog/v2.3.26.md +0 -21
  157. package/changelog/v2.3.27.md +0 -33
  158. package/changelog/v2.3.4.md +0 -20
  159. package/changelog/v2.3.9.md +0 -22
  160. package/changelog/v2.4.12.md +0 -37
  161. package/changelog/v2.4.16.md +0 -19
  162. package/compat-single-account.md +0 -148
  163. package/index.test.ts +0 -38
  164. package/scripts/test-proxy.ts +0 -70
  165. package/src/accounts.ts +0 -34
  166. package/src/agent/api-client.upload.test.ts +0 -109
  167. package/src/agent/handler.event-filter.test.ts +0 -100
  168. package/src/agent/handler.ts +0 -1105
  169. package/src/agent/index.ts +0 -12
  170. package/src/app/account-runtime.ts +0 -276
  171. package/src/app/bootstrap.ts +0 -29
  172. package/src/app/index.ts +0 -192
  173. package/src/capability/agent/delivery-service.ts +0 -87
  174. package/src/capability/agent/fallback-policy.ts +0 -13
  175. package/src/capability/agent/ingress-service.ts +0 -38
  176. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  177. package/src/capability/bot/dispatch-config.ts +0 -47
  178. package/src/capability/bot/fallback-delivery.ts +0 -178
  179. package/src/capability/bot/local-path-delivery.ts +0 -215
  180. package/src/capability/bot/sandbox-media.test.ts +0 -221
  181. package/src/capability/bot/sandbox-media.ts +0 -176
  182. package/src/capability/bot/service.ts +0 -56
  183. package/src/capability/bot/stream-delivery.ts +0 -379
  184. package/src/capability/bot/stream-finalizer.ts +0 -120
  185. package/src/capability/bot/stream-orchestrator.ts +0 -371
  186. package/src/capability/bot/types.ts +0 -8
  187. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  188. package/src/capability/calendar/tool.ts +0 -417
  189. package/src/capability/calendar/types.ts +0 -309
  190. package/src/capability/doc/tool.ts +0 -1629
  191. package/src/capability/doc/types.ts +0 -792
  192. package/src/capability/mcp/index.ts +0 -10
  193. package/src/capability/mcp/schema.ts +0 -107
  194. package/src/capability/mcp/tool.ts +0 -174
  195. package/src/capability/mcp/transport.ts +0 -394
  196. package/src/channel.config.test.ts +0 -147
  197. package/src/channel.lifecycle.test.ts +0 -255
  198. package/src/channel.meta.test.ts +0 -26
  199. package/src/channel.ts +0 -256
  200. package/src/config/accounts.resolve.test.ts +0 -75
  201. package/src/config/accounts.ts +0 -296
  202. package/src/config/derived-paths.test.ts +0 -111
  203. package/src/config/derived-paths.ts +0 -41
  204. package/src/config/index.ts +0 -26
  205. package/src/config/media.test.ts +0 -113
  206. package/src/config/media.ts +0 -139
  207. package/src/config/network.ts +0 -53
  208. package/src/config/routing.test.ts +0 -88
  209. package/src/config/routing.ts +0 -26
  210. package/src/config/runtime-config.ts +0 -46
  211. package/src/config/schema.ts +0 -90
  212. package/src/context-store.ts +0 -297
  213. package/src/crypto/index.ts +0 -24
  214. package/src/crypto.test.ts +0 -32
  215. package/src/crypto.ts +0 -176
  216. package/src/domain/models.ts +0 -7
  217. package/src/domain/policies.ts +0 -36
  218. package/src/dynamic-agent.account-scope.test.ts +0 -17
  219. package/src/gateway-monitor.ts +0 -181
  220. package/src/http.ts +0 -145
  221. package/src/media.test.ts +0 -82
  222. package/src/monitor/limits.ts +0 -7
  223. package/src/monitor/state.queue.test.ts +0 -185
  224. package/src/monitor/state.ts +0 -34
  225. package/src/monitor.active.test.ts +0 -245
  226. package/src/monitor.inbound-filter.test.ts +0 -63
  227. package/src/monitor.integration.test.ts +0 -208
  228. package/src/monitor.ts +0 -121
  229. package/src/monitor.webhook.test.ts +0 -774
  230. package/src/observability/audit-log.ts +0 -48
  231. package/src/observability/legacy-operational-event-store.ts +0 -36
  232. package/src/observability/raw-envelope-log.ts +0 -28
  233. package/src/observability/status-registry.ts +0 -13
  234. package/src/observability/transport-session-view.ts +0 -14
  235. package/src/onboarding.test.ts +0 -336
  236. package/src/onboarding.ts +0 -704
  237. package/src/outbound.test.ts +0 -1271
  238. package/src/outbound.ts +0 -746
  239. package/src/runtime/dispatcher.ts +0 -71
  240. package/src/runtime/outbound-intent.ts +0 -4
  241. package/src/runtime/reply-orchestrator.test.ts +0 -71
  242. package/src/runtime/reply-orchestrator.ts +0 -67
  243. package/src/runtime/routing-bridge.test.ts +0 -115
  244. package/src/runtime/routing-bridge.ts +0 -44
  245. package/src/runtime/session-manager.test.ts +0 -174
  246. package/src/runtime/session-manager.ts +0 -139
  247. package/src/runtime/source-registry.ts +0 -249
  248. package/src/runtime.ts +0 -14
  249. package/src/shared/command-auth.ts +0 -87
  250. package/src/shared/media-asset.ts +0 -78
  251. package/src/shared/media-service.test.ts +0 -111
  252. package/src/shared/media-service.ts +0 -84
  253. package/src/shared/media-types.ts +0 -5
  254. package/src/shared/xml-parser.test.ts +0 -50
  255. package/src/store/active-reply-store.ts +0 -42
  256. package/src/store/interfaces.ts +0 -11
  257. package/src/store/memory-store.ts +0 -43
  258. package/src/store/stream-batch-store.ts +0 -350
  259. package/src/transport/agent-api/client.ts +0 -277
  260. package/src/transport/agent-api/core.ts +0 -463
  261. package/src/transport/agent-api/delivery.ts +0 -41
  262. package/src/transport/agent-api/media-upload.ts +0 -11
  263. package/src/transport/agent-api/reply.ts +0 -39
  264. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  265. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  266. package/src/transport/agent-api/upstream-reply.ts +0 -43
  267. package/src/transport/agent-callback/http-handler.ts +0 -47
  268. package/src/transport/agent-callback/inbound.ts +0 -5
  269. package/src/transport/agent-callback/reply.ts +0 -13
  270. package/src/transport/agent-callback/request-handler.ts +0 -244
  271. package/src/transport/agent-callback/session.ts +0 -23
  272. package/src/transport/bot-webhook/active-reply.ts +0 -39
  273. package/src/transport/bot-webhook/http-handler.ts +0 -48
  274. package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
  275. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
  276. package/src/transport/bot-webhook/inbound.ts +0 -5
  277. package/src/transport/bot-webhook/message-shape.ts +0 -92
  278. package/src/transport/bot-webhook/protocol.ts +0 -148
  279. package/src/transport/bot-webhook/reply.ts +0 -15
  280. package/src/transport/bot-webhook/request-handler.ts +0 -394
  281. package/src/transport/bot-webhook/session.ts +0 -23
  282. package/src/transport/bot-ws/inbound.test.ts +0 -290
  283. package/src/transport/bot-ws/inbound.ts +0 -163
  284. package/src/transport/bot-ws/media.test.ts +0 -44
  285. package/src/transport/bot-ws/media.ts +0 -321
  286. package/src/transport/bot-ws/reply.test.ts +0 -450
  287. package/src/transport/bot-ws/reply.ts +0 -365
  288. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  289. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  290. package/src/transport/bot-ws/session.ts +0 -28
  291. package/src/transport/http/common.ts +0 -109
  292. package/src/transport/http/registry.ts +0 -92
  293. package/src/transport/http/request-handler.ts +0 -84
  294. package/src/types/account.ts +0 -70
  295. package/src/types/config.ts +0 -114
  296. package/src/types/constants.ts +0 -31
  297. package/src/types/events.ts +0 -21
  298. package/src/types/global.d.ts +0 -9
  299. package/src/types/index.ts +0 -17
  300. package/src/types/legacy-stream.ts +0 -50
  301. package/src/types/message.ts +0 -189
  302. package/src/types/runtime-context.ts +0 -28
  303. package/src/types/runtime.ts +0 -165
  304. package/src/types.ts +0 -41
  305. package/src/upstream/index.ts +0 -150
  306. package/src/upstream.test.ts +0 -84
  307. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  308. package/tsconfig.json +0 -22
  309. package/vitest.config.ts +0 -26
  310. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  311. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  312. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  313. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -1,126 +1,103 @@
1
- import type { ResolvedAgentAccount } from "../../types/index.js";
2
1
  import { getAccessToken } from "../../transport/agent-api/core.js";
3
2
  import { wecomFetch } from "../../http.js";
4
3
  import { resolveWecomEgressProxyUrlFromNetwork } from "../../config/index.js";
5
4
  import { LIMITS } from "../../types/constants.js";
6
- import {
7
- BatchUpdateDocResponse,
8
- GetDocContentResponse,
9
- Node,
10
- UpdateRequest
11
- } from "./types.js";
12
-
13
- function readString(value: unknown): string {
5
+ function readString(value) {
14
6
  const trimmed = String(value ?? "").trim();
15
7
  return trimmed || "";
16
8
  }
17
-
18
- function normalizeDocType(docType: unknown): 3 | 4 | 10 {
19
- if (docType === 3 || docType === "3") return 3;
20
- if (docType === 4 || docType === "4") return 4;
21
- if (docType === 10 || docType === "10" || docType === 5 || docType === "5") return 10;
9
+ function normalizeDocType(docType) {
10
+ if (docType === 3 || docType === "3")
11
+ return 3;
12
+ if (docType === 4 || docType === "4")
13
+ return 4;
14
+ if (docType === 10 || docType === "10" || docType === 5 || docType === "5")
15
+ return 10;
22
16
  const normalized = readString(docType).toLowerCase();
23
- if (!normalized || normalized === "doc") return 3;
24
- if (normalized === "spreadsheet" || normalized === "sheet" || normalized === "table") return 4;
25
- if (normalized === "smart_table" || normalized === "smarttable") return 10;
17
+ if (!normalized || normalized === "doc")
18
+ return 3;
19
+ if (normalized === "spreadsheet" || normalized === "sheet" || normalized === "table")
20
+ return 4;
21
+ if (normalized === "smart_table" || normalized === "smarttable")
22
+ return 10;
26
23
  throw new Error(`Unsupported WeCom docType: ${String(docType)}`);
27
24
  }
28
-
29
- function mapDocTypeLabel(docType: 3 | 4 | 10): string {
30
- if (docType === 10) return "smart_table";
31
- if (docType === 4) return "spreadsheet";
25
+ function mapDocTypeLabel(docType) {
26
+ if (docType === 10)
27
+ return "smart_table";
28
+ if (docType === 4)
29
+ return "spreadsheet";
32
30
  return "doc";
33
31
  }
34
-
35
- function isRecord(value: unknown): value is Record<string, unknown> {
32
+ function isRecord(value) {
36
33
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
37
34
  }
38
-
39
- function readObject(value: unknown): Record<string, unknown> {
35
+ function readObject(value) {
40
36
  return isRecord(value) ? value : {};
41
37
  }
42
-
43
- function readArray(value: unknown): unknown[] {
38
+ function readArray(value) {
44
39
  return Array.isArray(value) ? value : [];
45
40
  }
46
-
47
- export interface DocMemberEntry {
48
- userid?: string;
49
- partyid?: string;
50
- tagid?: string;
51
- /**
52
- * 权限位:1-查看,2-编辑,7-管理
53
- * 只有“智能表格”才支持读写权限(auth=2)?
54
- * 实际上企微文档现在也支持设置协作者权限了。
55
- */
56
- auth?: number;
57
- }
58
-
59
- function normalizeDocMemberEntry(value: unknown): DocMemberEntry | null {
41
+ function normalizeDocMemberEntry(value) {
60
42
  if (typeof value === "string" || typeof value === "number") {
61
43
  const userid = readString(value);
62
44
  return userid ? { userid } : null;
63
45
  }
64
- if (!isRecord(value)) return null;
65
- const entry: DocMemberEntry = { ...value } as DocMemberEntry;
46
+ if (!isRecord(value))
47
+ return null;
48
+ const entry = { ...value };
66
49
  if (!readString(entry.userid) && readString(value.userId)) {
67
50
  entry.userid = readString(value.userId);
68
51
  }
69
52
  if (!readString(entry.userid) && !readString(entry.partyid) && !readString(entry.tagid)) {
70
53
  return null;
71
54
  }
72
- if (readString(entry.userid)) entry.userid = readString(entry.userid);
73
- if (readString(entry.partyid)) entry.partyid = readString(entry.partyid);
74
- if (readString(entry.tagid)) entry.tagid = readString(entry.tagid);
75
- if (entry.auth !== undefined) entry.auth = Number(entry.auth);
55
+ if (readString(entry.userid))
56
+ entry.userid = readString(entry.userid);
57
+ if (readString(entry.partyid))
58
+ entry.partyid = readString(entry.partyid);
59
+ if (readString(entry.tagid))
60
+ entry.tagid = readString(entry.tagid);
61
+ if (entry.auth !== undefined)
62
+ entry.auth = Number(entry.auth);
76
63
  return entry;
77
64
  }
78
-
79
- function normalizeDocMemberEntryList(values: unknown): DocMemberEntry[] {
80
- return readArray(values).map(normalizeDocMemberEntry).filter((v): v is DocMemberEntry => v !== null);
65
+ function normalizeDocMemberEntryList(values) {
66
+ return readArray(values).map(normalizeDocMemberEntry).filter((v) => v !== null);
81
67
  }
82
-
83
- function buildDocMemberAuthRequest(params: {
84
- docId: string;
85
- viewers?: unknown;
86
- collaborators?: unknown;
87
- removeViewers?: unknown;
88
- removeCollaborators?: unknown;
89
- authLevel?: number; // Default auth level for new members if not specified in entry
90
- }): Record<string, unknown> {
68
+ function buildDocMemberAuthRequest(params) {
91
69
  const { docId, viewers, collaborators, removeViewers, removeCollaborators, authLevel } = params;
92
- const payload: Record<string, unknown> = {
70
+ const payload = {
93
71
  docid: readString(docId),
94
72
  };
95
- if (!payload.docid) throw new Error("docId required");
96
-
73
+ if (!payload.docid)
74
+ throw new Error("docId required");
97
75
  const normalizedViewers = normalizeDocMemberEntryList(viewers).map(v => ({ ...v, auth: v.auth ?? authLevel ?? 1 }));
98
76
  const normalizedCollaborators = normalizeDocMemberEntryList(collaborators).map(v => ({ ...v, auth: v.auth ?? authLevel ?? 2 }));
99
77
  const normalizedRemovedViewers = normalizeDocMemberEntryList(removeViewers);
100
78
  const normalizedRemovedCollaborators = normalizeDocMemberEntryList(removeCollaborators);
101
-
102
- if (normalizedViewers.length > 0) payload.update_file_member_list = normalizedViewers;
103
- if (normalizedCollaborators.length > 0) payload.update_co_auth_list = normalizedCollaborators;
104
- if (normalizedRemovedViewers.length > 0) payload.del_file_member_list = normalizedRemovedViewers;
105
- if (normalizedRemovedCollaborators.length > 0) payload.del_co_auth_list = normalizedRemovedCollaborators;
106
-
107
- if (
108
- !payload.update_doc_member_list &&
79
+ if (normalizedViewers.length > 0)
80
+ payload.update_file_member_list = normalizedViewers;
81
+ if (normalizedCollaborators.length > 0)
82
+ payload.update_co_auth_list = normalizedCollaborators;
83
+ if (normalizedRemovedViewers.length > 0)
84
+ payload.del_file_member_list = normalizedRemovedViewers;
85
+ if (normalizedRemovedCollaborators.length > 0)
86
+ payload.del_co_auth_list = normalizedRemovedCollaborators;
87
+ if (!payload.update_doc_member_list &&
109
88
  !payload.update_co_auth_list &&
110
89
  !payload.del_doc_member_list &&
111
- !payload.del_co_auth_list
112
- ) {
90
+ !payload.del_co_auth_list) {
113
91
  throw new Error("at least one viewer/collaborator change is required");
114
92
  }
115
-
116
93
  return payload;
117
94
  }
118
-
119
- async function parseJsonResponse(res: Response, actionLabel: string): Promise<any> {
120
- let payload: any = null;
95
+ async function parseJsonResponse(res, actionLabel) {
96
+ let payload = null;
121
97
  try {
122
98
  payload = await res.json();
123
- } catch {
99
+ }
100
+ catch {
124
101
  if (!res.ok) {
125
102
  throw new Error(`WeCom ${actionLabel} failed: HTTP ${res.status}`);
126
103
  }
@@ -135,34 +112,22 @@ async function parseJsonResponse(res: Response, actionLabel: string): Promise<an
135
112
  if (Array.isArray(payload)) {
136
113
  const failedItem = payload.find((item) => Number(item?.errcode ?? 0) !== 0);
137
114
  if (failedItem) {
138
- throw new Error(
139
- `WeCom ${actionLabel} failed: ${String(failedItem?.errmsg || "unknown error")} (errcode ${String(failedItem?.errcode)})`,
140
- );
115
+ throw new Error(`WeCom ${actionLabel} failed: ${String(failedItem?.errmsg || "unknown error")} (errcode ${String(failedItem?.errcode)})`);
141
116
  }
142
117
  return payload;
143
118
  }
144
119
  if (Number(payload.errcode ?? 0) !== 0) {
145
- throw new Error(
146
- `WeCom ${actionLabel} failed: ${String(payload.errmsg || "unknown error")} (errcode ${String(payload.errcode)})`,
147
- );
120
+ throw new Error(`WeCom ${actionLabel} failed: ${String(payload.errmsg || "unknown error")} (errcode ${String(payload.errcode)})`);
148
121
  }
149
122
  return payload;
150
123
  }
151
-
152
124
  export class WecomDocClient {
153
- private async postWecomDocApi(params: {
154
- path: string;
155
- actionLabel: string;
156
- agent: ResolvedAgentAccount;
157
- body: Record<string, unknown> | unknown[];
158
- }): Promise<any> {
125
+ async postWecomDocApi(params) {
159
126
  const { path, actionLabel, agent, body } = params;
160
-
161
127
  const token = await getAccessToken(agent);
162
128
  const url = `https://qyapi.weixin.qq.com${path}?access_token=${encodeURIComponent(token)}`;
163
129
  const proxyUrl = resolveWecomEgressProxyUrlFromNetwork(agent.network);
164
-
165
- let lastErr: any;
130
+ let lastErr;
166
131
  for (let attempt = 1; attempt <= 3; attempt++) {
167
132
  try {
168
133
  const res = await wecomFetch(url, {
@@ -172,9 +137,9 @@ export class WecomDocClient {
172
137
  },
173
138
  body: JSON.stringify(body ?? {}),
174
139
  }, { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS });
175
-
176
140
  return await parseJsonResponse(res, actionLabel);
177
- } catch (err) {
141
+ }
142
+ catch (err) {
178
143
  lastErr = err;
179
144
  if (attempt < 3) {
180
145
  await new Promise(r => setTimeout(r, 1000));
@@ -183,20 +148,21 @@ export class WecomDocClient {
183
148
  }
184
149
  throw lastErr;
185
150
  }
186
-
187
- async createDoc(params: { agent: ResolvedAgentAccount; docName: string; docType?: unknown; spaceId?: string; fatherId?: string; adminUsers?: string[] }) {
151
+ async createDoc(params) {
188
152
  const { agent, docName, docType, spaceId, fatherId, adminUsers } = params;
189
153
  const normalizedDocType = normalizeDocType(docType);
190
- const payload: Record<string, unknown> = {
154
+ const payload = {
191
155
  doc_type: normalizedDocType,
192
156
  doc_name: readString(docName),
193
157
  };
194
- if (!payload.doc_name) throw new Error("docName required");
158
+ if (!payload.doc_name)
159
+ throw new Error("docName required");
195
160
  const normalizedSpaceId = readString(spaceId);
196
161
  const normalizedFatherId = readString(fatherId);
197
- if (normalizedSpaceId) payload.spaceid = normalizedSpaceId;
198
- if (normalizedFatherId) payload.fatherid = normalizedFatherId;
199
-
162
+ if (normalizedSpaceId)
163
+ payload.spaceid = normalizedSpaceId;
164
+ if (normalizedFatherId)
165
+ payload.fatherid = normalizedFatherId;
200
166
  // admin_users is required for smart_table to ensure proper permissions
201
167
  const normalizedAdminUsers = Array.isArray(adminUsers)
202
168
  ? adminUsers.map((item) => readString(item)).filter(Boolean)
@@ -204,14 +170,12 @@ export class WecomDocClient {
204
170
  if (normalizedAdminUsers.length > 0) {
205
171
  payload.admin_users = normalizedAdminUsers;
206
172
  }
207
-
208
173
  const json = await this.postWecomDocApi({
209
174
  path: "/cgi-bin/wedoc/create_doc",
210
175
  actionLabel: "create_doc",
211
176
  agent,
212
177
  body: payload,
213
178
  });
214
-
215
179
  const result = {
216
180
  raw: json,
217
181
  docId: readString(json.docid),
@@ -219,15 +183,12 @@ export class WecomDocClient {
219
183
  docType: normalizedDocType,
220
184
  docTypeLabel: mapDocTypeLabel(normalizedDocType),
221
185
  };
222
-
223
186
  // Auto-initialize smart_table: clean up default fields and records
224
187
  if (normalizedDocType === 10) {
225
188
  await this.initializeSmartTable({ agent, docId: result.docId });
226
189
  }
227
-
228
190
  return result;
229
191
  }
230
-
231
192
  /**
232
193
  * Initialize smart_table after creation:
233
194
  * 1. Get default sheet (smartsheet type)
@@ -236,21 +197,18 @@ export class WecomDocClient {
236
197
  * 4. Get default records (usually 5 empty records)
237
198
  * 5. Delete all default records
238
199
  */
239
- private async initializeSmartTable(params: { agent: ResolvedAgentAccount; docId: string }) {
200
+ async initializeSmartTable(params) {
240
201
  const { agent, docId } = params;
241
-
242
202
  try {
243
203
  // Step 1: Get sheet list to find the default smartsheet
244
204
  const sheetsResult = await this.smartTableGetSheets({ agent, docId });
245
- const defaultSheet = sheetsResult.sheets.find((s: any) => s.type === "smartsheet");
246
- if (!defaultSheet) return; // No smartsheet found, skip initialization
247
-
248
- const sheetId = (defaultSheet as any).sheet_id;
249
-
205
+ const defaultSheet = sheetsResult.sheets.find((s) => s.type === "smartsheet");
206
+ if (!defaultSheet)
207
+ return; // No smartsheet found, skip initialization
208
+ const sheetId = defaultSheet.sheet_id;
250
209
  // Step 2: Get default fields
251
210
  const fieldsResult = await this.smartTableGetFields({ agent, docId, sheetId });
252
211
  const fields = fieldsResult.fields || [];
253
-
254
212
  if (fields.length > 1) {
255
213
  // Keep the last field as primary key, delete the rest
256
214
  // Primary key capable types: TEXT, NUMBER, DATE_TIME, URL, PROGRESS, EMAIL, PHONE_NUMBER, FORMULA, LOCATION, CURRENCY, AUTONUMBER, TITLE, WWGROUP
@@ -260,58 +218,55 @@ export class WecomDocClient {
260
218
  'FIELD_TYPE_PHONE_NUMBER', 'FIELD_TYPE_LOCATION', 'FIELD_TYPE_CURRENCY',
261
219
  'FIELD_TYPE_AUTONUMBER', 'FIELD_TYPE_WWGROUP'
262
220
  ];
263
-
264
221
  // Find a field that can be primary key (prefer the last one)
265
- let fieldToDelete: string[] = [];
266
- let fieldToKeep: string | null = null;
267
-
222
+ let fieldToDelete = [];
223
+ let fieldToKeep = null;
268
224
  for (let i = fields.length - 1; i >= 0; i--) {
269
- const field = fields[i] as any;
225
+ const field = fields[i];
270
226
  if (!fieldToKeep && primaryKeyCapableTypes.includes(field.field_type)) {
271
227
  fieldToKeep = field.field_id;
272
- } else if (field.field_id) {
228
+ }
229
+ else if (field.field_id) {
273
230
  fieldToDelete.push(field.field_id);
274
231
  }
275
232
  }
276
-
277
233
  // If no primary key capable field found, keep the last one anyway
278
234
  if (!fieldToKeep && fields.length > 0) {
279
- const lastField = fields[fields.length - 1] as any;
235
+ const lastField = fields[fields.length - 1];
280
236
  fieldToKeep = lastField.field_id;
281
- fieldToDelete = fields.slice(0, -1).map((f: any) => f.field_id);
237
+ fieldToDelete = fields.slice(0, -1).map((f) => f.field_id);
282
238
  }
283
-
284
239
  // Delete the fields
285
240
  if (fieldToDelete.length > 0) {
286
241
  await this.smartTableDelFields({ agent, docId, sheetId, field_ids: fieldToDelete });
287
242
  }
288
243
  }
289
-
290
244
  // Step 3: Get default records
291
245
  const recordsResult = await this.smartTableGetRecords({ agent, docId, sheetId, limit: 100 });
292
246
  const records = recordsResult.records || [];
293
-
294
247
  if (records.length > 0) {
295
248
  // Delete all default empty records
296
- const recordIds = records.map((r: any) => r.record_id).filter(Boolean);
249
+ const recordIds = records.map((r) => r.record_id).filter(Boolean);
297
250
  if (recordIds.length > 0) {
298
251
  await this.smartTableDelRecords({ agent, docId, sheetId, record_ids: recordIds });
299
252
  }
300
253
  }
301
- } catch (err) {
254
+ }
255
+ catch (err) {
302
256
  // Non-fatal: smart_table created, just default cleanup failed
303
257
  console.error(`[WecomDocClient] initializeSmartTable failed:`, err);
304
258
  }
305
259
  }
306
-
307
- async renameDoc(params: { agent: ResolvedAgentAccount; docId: string; newName: string }) {
260
+ async renameDoc(params) {
308
261
  const { agent, docId, newName } = params;
309
262
  const payload = {
310
263
  docid: readString(docId),
311
264
  new_name: readString(newName),
312
265
  };
313
- if (!payload.docid) throw new Error("docId required");
314
- if (!payload.new_name) throw new Error("newName required");
266
+ if (!payload.docid)
267
+ throw new Error("docId required");
268
+ if (!payload.new_name)
269
+ throw new Error("newName required");
315
270
  const json = await this.postWecomDocApi({
316
271
  path: "/cgi-bin/wedoc/rename_doc",
317
272
  actionLabel: "rename_doc",
@@ -324,17 +279,19 @@ export class WecomDocClient {
324
279
  newName: payload.new_name,
325
280
  };
326
281
  }
327
-
328
- async copyDoc(params: { agent: ResolvedAgentAccount; docId: string; newName?: string; spaceId?: string; fatherId?: string }) {
282
+ async copyDoc(params) {
329
283
  const { agent, docId, newName, spaceId, fatherId } = params;
330
- const payload: Record<string, unknown> = {
284
+ const payload = {
331
285
  docid: readString(docId),
332
286
  };
333
- if (!payload.docid) throw new Error("docId required");
334
- if (newName) payload.new_name = readString(newName);
335
- if (spaceId) payload.spaceid = readString(spaceId);
336
- if (fatherId) payload.fatherid = readString(fatherId);
337
-
287
+ if (!payload.docid)
288
+ throw new Error("docId required");
289
+ if (newName)
290
+ payload.new_name = readString(newName);
291
+ if (spaceId)
292
+ payload.spaceid = readString(spaceId);
293
+ if (fatherId)
294
+ payload.fatherid = readString(fatherId);
338
295
  const json = await this.postWecomDocApi({
339
296
  path: "/cgi-bin/wedoc/smartsheet/copy",
340
297
  actionLabel: "copy_smartsheet",
@@ -347,11 +304,11 @@ export class WecomDocClient {
347
304
  url: readString(json.url),
348
305
  };
349
306
  }
350
-
351
- async getDocBaseInfo(params: { agent: ResolvedAgentAccount; docId: string }) {
307
+ async getDocBaseInfo(params) {
352
308
  const { agent, docId } = params;
353
309
  const normalizedDocId = readString(docId);
354
- if (!normalizedDocId) throw new Error("docId required");
310
+ if (!normalizedDocId)
311
+ throw new Error("docId required");
355
312
  const json = await this.postWecomDocApi({
356
313
  path: "/cgi-bin/wedoc/get_doc_base_info",
357
314
  actionLabel: "get_doc_base_info",
@@ -363,11 +320,11 @@ export class WecomDocClient {
363
320
  info: json.doc_base_info && typeof json.doc_base_info === "object" ? json.doc_base_info : {},
364
321
  };
365
322
  }
366
-
367
- async shareDoc(params: { agent: ResolvedAgentAccount; docId: string }) {
323
+ async shareDoc(params) {
368
324
  const { agent, docId } = params;
369
325
  const normalizedDocId = readString(docId);
370
- if (!normalizedDocId) throw new Error("docId required");
326
+ if (!normalizedDocId)
327
+ throw new Error("docId required");
371
328
  const json = await this.postWecomDocApi({
372
329
  path: "/cgi-bin/wedoc/doc_share",
373
330
  actionLabel: "doc_share",
@@ -379,11 +336,11 @@ export class WecomDocClient {
379
336
  shareUrl: readString(json.share_url),
380
337
  };
381
338
  }
382
-
383
- async getDocAuth(params: { agent: ResolvedAgentAccount; docId: string }) {
339
+ async getDocAuth(params) {
384
340
  const { agent, docId } = params;
385
341
  const normalizedDocId = readString(docId);
386
- if (!normalizedDocId) throw new Error("docId required");
342
+ if (!normalizedDocId)
343
+ throw new Error("docId required");
387
344
  const json = await this.postWecomDocApi({
388
345
  path: "/cgi-bin/wedoc/doc_get_auth",
389
346
  actionLabel: "doc_get_auth",
@@ -398,14 +355,15 @@ export class WecomDocClient {
398
355
  coAuthList: Array.isArray(json.co_auth_list) ? json.co_auth_list : [],
399
356
  };
400
357
  }
401
-
402
- async deleteDoc(params: { agent: ResolvedAgentAccount; docId?: string; formId?: string }) {
358
+ async deleteDoc(params) {
403
359
  const { agent, docId, formId } = params;
404
- const payload: Record<string, string> = {};
360
+ const payload = {};
405
361
  const normalizedDocId = readString(docId);
406
362
  const normalizedFormId = readString(formId);
407
- if (normalizedDocId) payload.docid = normalizedDocId;
408
- if (normalizedFormId) payload.formid = normalizedFormId;
363
+ if (normalizedDocId)
364
+ payload.docid = normalizedDocId;
365
+ if (normalizedFormId)
366
+ payload.formid = normalizedFormId;
409
367
  if (!payload.docid && !payload.formid) {
410
368
  throw new Error("docId or formId required");
411
369
  }
@@ -421,14 +379,14 @@ export class WecomDocClient {
421
379
  formId: payload.formid || "",
422
380
  };
423
381
  }
424
-
425
- async setDocJoinRule(params: { agent: ResolvedAgentAccount; docId: string; request: any }) {
382
+ async setDocJoinRule(params) {
426
383
  const { agent, docId, request } = params;
427
384
  const payload = {
428
385
  ...readObject(request),
429
386
  };
430
387
  payload.docid = readString(docId || payload.docid);
431
- if (!payload.docid) throw new Error("docId required");
388
+ if (!payload.docid)
389
+ throw new Error("docId required");
432
390
  const json = await this.postWecomDocApi({
433
391
  path: "/cgi-bin/wedoc/mod_doc_join_rule",
434
392
  actionLabel: "mod_doc_join_rule",
@@ -437,17 +395,17 @@ export class WecomDocClient {
437
395
  });
438
396
  return {
439
397
  raw: json,
440
- docId: payload.docid as string,
398
+ docId: payload.docid,
441
399
  };
442
400
  }
443
-
444
- async setDocMemberAuth(params: { agent: ResolvedAgentAccount; docId: string; request: any }) {
401
+ async setDocMemberAuth(params) {
445
402
  const { agent, docId, request } = params;
446
403
  const payload = {
447
404
  ...readObject(request),
448
405
  };
449
406
  payload.docid = readString(docId || payload.docid);
450
- if (!payload.docid) throw new Error("docId required");
407
+ if (!payload.docid)
408
+ throw new Error("docId required");
451
409
  const json = await this.postWecomDocApi({
452
410
  path: "/cgi-bin/wedoc/mod_doc_member",
453
411
  actionLabel: "mod_doc_member",
@@ -456,21 +414,11 @@ export class WecomDocClient {
456
414
  });
457
415
  return {
458
416
  raw: json,
459
- docId: payload.docid as string,
417
+ docId: payload.docid,
460
418
  };
461
419
  }
462
-
463
- async grantDocAccess(params: {
464
- agent: ResolvedAgentAccount;
465
- docId: string;
466
- viewers?: unknown;
467
- collaborators?: unknown;
468
- removeViewers?: unknown;
469
- removeCollaborators?: unknown;
470
- authLevel?: number;
471
- }) {
420
+ async grantDocAccess(params) {
472
421
  const { agent, docId, viewers, collaborators, removeViewers, removeCollaborators, authLevel } = params;
473
-
474
422
  // Auto-detect: if adding collaborators, check if they are already viewers and need to be removed
475
423
  // This prevents the "user is viewer but not collaborator" issue
476
424
  let finalRemoveViewers = removeViewers;
@@ -479,32 +427,29 @@ export class WecomDocClient {
479
427
  try {
480
428
  const currentAuth = await this.getDocAuth({ agent, docId });
481
429
  // Build a map of viewer entries with their full structure (preserving type and other fields)
482
- const viewerMap = new Map<string, any>();
430
+ const viewerMap = new Map();
483
431
  (currentAuth.docMembers || [])
484
- .filter((m: any) => m.userid)
485
- .forEach((m: any) => viewerMap.set(m.userid, m));
486
-
432
+ .filter((m) => m.userid)
433
+ .forEach((m) => viewerMap.set(m.userid, m));
487
434
  // Normalize new collaborators to get their userids
488
435
  const newCollaboratorEntries = normalizeDocMemberEntryList(collaborators);
489
-
490
436
  // Auto-add viewers who are being promoted to collaborators, preserving their original structure
491
437
  const autoRemoveViewers = newCollaboratorEntries
492
438
  .filter(entry => entry.userid && viewerMap.has(entry.userid))
493
439
  .map(entry => {
494
- // Preserve the original viewer's full structure (type, userid, etc.)
495
- const originalViewer = viewerMap.get(entry.userid!);
496
- return { ...originalViewer };
497
- });
498
-
440
+ // Preserve the original viewer's full structure (type, userid, etc.)
441
+ const originalViewer = viewerMap.get(entry.userid);
442
+ return { ...originalViewer };
443
+ });
499
444
  if (autoRemoveViewers.length > 0) {
500
445
  finalRemoveViewers = autoRemoveViewers;
501
446
  }
502
- } catch (err) {
447
+ }
448
+ catch (err) {
503
449
  // If we can't check auth, proceed without auto-removal
504
450
  // The caller can explicitly pass removeViewers if needed
505
451
  }
506
452
  }
507
-
508
453
  const payload = buildDocMemberAuthRequest({
509
454
  docId,
510
455
  viewers,
@@ -515,19 +460,18 @@ export class WecomDocClient {
515
460
  });
516
461
  const result = await this.setDocMemberAuth({
517
462
  agent,
518
- docId: payload.docid as string,
463
+ docId: payload.docid,
519
464
  request: payload,
520
465
  });
521
466
  return {
522
467
  ...result,
523
- addedViewerCount: (payload.update_file_member_list as any[])?.length ?? 0,
524
- addedCollaboratorCount: (payload.update_co_auth_list as any[])?.length ?? 0,
525
- removedViewerCount: (payload.del_file_member_list as any[])?.length ?? 0,
526
- removedCollaboratorCount: (payload.del_co_auth_list as any[])?.length ?? 0,
468
+ addedViewerCount: payload.update_file_member_list?.length ?? 0,
469
+ addedCollaboratorCount: payload.update_co_auth_list?.length ?? 0,
470
+ removedViewerCount: payload.del_file_member_list?.length ?? 0,
471
+ removedCollaboratorCount: payload.del_co_auth_list?.length ?? 0,
527
472
  };
528
473
  }
529
-
530
- async addDocCollaborators(params: { agent: ResolvedAgentAccount; docId: string; collaborators: unknown; auth?: number }) {
474
+ async addDocCollaborators(params) {
531
475
  const { agent, docId, collaborators, auth } = params;
532
476
  return this.grantDocAccess({
533
477
  agent,
@@ -536,14 +480,14 @@ export class WecomDocClient {
536
480
  authLevel: auth ?? 2, // Default to edit/read-write for collaborators
537
481
  });
538
482
  }
539
-
540
- async setDocSafetySetting(params: { agent: ResolvedAgentAccount; docId: string; request: any }) {
483
+ async setDocSafetySetting(params) {
541
484
  const { agent, docId, request } = params;
542
485
  const payload = {
543
486
  ...readObject(request),
544
487
  };
545
488
  payload.docid = readString(docId || payload.docid);
546
- if (!payload.docid) throw new Error("docId required");
489
+ if (!payload.docid)
490
+ throw new Error("docId required");
547
491
  const json = await this.postWecomDocApi({
548
492
  path: "/cgi-bin/wedoc/mod_doc_safty_setting",
549
493
  actionLabel: "mod_doc_safty_setting",
@@ -552,45 +496,40 @@ export class WecomDocClient {
552
496
  });
553
497
  return {
554
498
  raw: json,
555
- docId: payload.docid as string,
499
+ docId: payload.docid,
556
500
  };
557
501
  }
558
-
559
- async createCollect(params: { agent: ResolvedAgentAccount; formInfo: any; spaceId?: string; fatherId?: string }) {
502
+ async createCollect(params) {
560
503
  const { agent, formInfo, spaceId, fatherId } = params;
561
-
562
504
  // Validate form_info structure per API spec
563
505
  if (!formInfo || typeof formInfo !== 'object') {
564
506
  throw new Error("formInfo 必须是非空对象");
565
507
  }
566
-
567
508
  // Validate required fields
568
509
  if (!formInfo.form_title || readString(formInfo.form_title).length === 0) {
569
510
  throw new Error("form_title 必填");
570
511
  }
571
-
572
512
  if (!formInfo.form_question || !formInfo.form_question.items || !Array.isArray(formInfo.form_question.items)) {
573
513
  throw new Error("form_question.items 必填且必须为数组");
574
514
  }
575
-
576
515
  // Validate questions count ≤ 200
577
516
  const questions = formInfo.form_question.items;
578
517
  if (questions.length > 200) {
579
518
  throw new Error("问题数量不能超过 200 个");
580
519
  }
581
-
582
520
  // Auto-fill status fields for questions and options
583
- questions.forEach((q: any) => {
584
- if (q.status === undefined) q.status = 1;
521
+ questions.forEach((q) => {
522
+ if (q.status === undefined)
523
+ q.status = 1;
585
524
  if (Array.isArray(q.option_item)) {
586
- q.option_item.forEach((opt: any) => {
587
- if (opt.status === undefined) opt.status = 1;
525
+ q.option_item.forEach((opt) => {
526
+ if (opt.status === undefined)
527
+ opt.status = 1;
588
528
  });
589
529
  }
590
530
  });
591
-
592
531
  // Validate each question
593
- questions.forEach((q: any, index: number) => {
532
+ questions.forEach((q, index) => {
594
533
  if (!q.question_id || !Number.isInteger(q.question_id) || q.question_id < 1) {
595
534
  throw new Error(`第${index + 1}个问题:question_id 必填且必须从 1 开始`);
596
535
  }
@@ -609,7 +548,6 @@ export class WecomDocClient {
609
548
  if (q.status !== undefined && ![1, 2].includes(q.status)) {
610
549
  throw new Error(`第${index + 1}个问题:status 必须为 1(正常) 或 2(删除)`);
611
550
  }
612
-
613
551
  // Validate option_item for single/multiple/dropdown questions
614
552
  const requiresOptions = [2, 3, 15].includes(q.reply_type); // 单选/多选/下拉列表
615
553
  if (requiresOptions) {
@@ -617,7 +555,7 @@ export class WecomDocClient {
617
555
  throw new Error(`第${index + 1}个问题:单选/多选/下拉列表必须提供 option_item 数组`);
618
556
  }
619
557
  // Validate option keys are sequential from 1
620
- q.option_item.forEach((opt: any, optIndex: number) => {
558
+ q.option_item.forEach((opt, optIndex) => {
621
559
  if (!opt.key || !Number.isInteger(opt.key) || opt.key < 1) {
622
560
  throw new Error(`第${index + 1}个问题的第${optIndex + 1}个选项:key 必填且从 1 开始`);
623
561
  }
@@ -629,7 +567,6 @@ export class WecomDocClient {
629
567
  }
630
568
  });
631
569
  }
632
-
633
570
  // Validate image/file upload limits
634
571
  if ([9, 10].includes(q.reply_type)) { // 图片/文件
635
572
  const setting = q.question_extend_setting;
@@ -646,22 +583,19 @@ export class WecomDocClient {
646
583
  }
647
584
  }
648
585
  });
649
-
650
586
  // Validate timed_repeat_info and timed_finish are mutually exclusive
651
587
  const formSetting = formInfo.form_setting || {};
652
588
  if (formSetting.timed_repeat_info?.enable && formSetting.timed_finish) {
653
589
  console.warn("警告:timed_finish 与 timed_repeat_info 互斥,若都填优先定时重复");
654
590
  }
655
-
656
591
  // Validate timed_repeat_info.enable=true requires fill_in_range
657
592
  if (formSetting.timed_repeat_info?.enable) {
658
593
  if (!formSetting.fill_in_range || (!formSetting.fill_in_range.userids?.length && !formSetting.fill_in_range.departmentids?.length)) {
659
594
  throw new Error("timed_repeat_info 开启时,fill_in_range 必填(需指定 userids 或 departmentids)");
660
595
  }
661
596
  }
662
-
663
597
  // Build payload
664
- const payload: Record<string, unknown> = {
598
+ const payload = {
665
599
  form_info: {
666
600
  form_title: readString(formInfo.form_title),
667
601
  form_desc: formInfo.form_desc ? readString(formInfo.form_desc) : undefined,
@@ -670,12 +604,12 @@ export class WecomDocClient {
670
604
  form_setting: formSetting,
671
605
  },
672
606
  };
673
-
674
607
  const normalizedSpaceId = readString(spaceId);
675
608
  const normalizedFatherId = readString(fatherId);
676
- if (normalizedSpaceId) payload.spaceid = normalizedSpaceId;
677
- if (normalizedFatherId) payload.fatherid = normalizedFatherId;
678
-
609
+ if (normalizedSpaceId)
610
+ payload.spaceid = normalizedSpaceId;
611
+ if (normalizedFatherId)
612
+ payload.fatherid = normalizedFatherId;
679
613
  const json = await this.postWecomDocApi({
680
614
  path: "/cgi-bin/wedoc/create_form",
681
615
  actionLabel: "create_form",
@@ -685,52 +619,47 @@ export class WecomDocClient {
685
619
  return {
686
620
  raw: json,
687
621
  formId: readString(json.formid),
688
- title: readString((payload.form_info as any).form_title),
622
+ title: readString(payload.form_info.form_title),
689
623
  };
690
624
  }
691
-
692
- async modifyCollect(params: { agent: ResolvedAgentAccount; oper: string; formId: string; formInfo: any }) {
625
+ async modifyCollect(params) {
693
626
  const { agent, oper, formId, formInfo } = params;
694
-
695
627
  // Validate oper parameter
696
628
  const operNum = Number(oper);
697
629
  if (!operNum || ![1, 2].includes(operNum)) {
698
630
  throw new Error("oper 必填且必须为 1 或 2:1=全量修改问题,2=全量修改设置");
699
631
  }
700
-
701
632
  const normalizedFormId = readString(formId);
702
- if (!normalizedFormId) throw new Error("formId required");
703
-
633
+ if (!normalizedFormId)
634
+ throw new Error("formId required");
704
635
  // Build payload based on oper type
705
- const payload: Record<string, unknown> = {
636
+ const payload = {
706
637
  oper: operNum,
707
638
  formid: normalizedFormId,
708
639
  };
709
-
710
640
  if (operNum === 1) {
711
641
  // 全量修改问题:必须提供完整的 form_question 数组
712
642
  if (!formInfo || !formInfo.form_question || !Array.isArray(formInfo.form_question.items)) {
713
643
  throw new Error("oper=1 时,必须提供 form_question.items 数组(包含所有问题,缺失的问题将被删除)");
714
644
  }
715
-
716
645
  // Validate questions count ≤ 200
717
646
  const questions = formInfo.form_question.items;
718
647
  if (questions.length > 200) {
719
648
  throw new Error("问题数量不能超过 200 个");
720
649
  }
721
-
722
650
  // Auto-fill status fields for questions and options
723
- questions.forEach((q: any) => {
724
- if (q.status === undefined) q.status = 1;
651
+ questions.forEach((q) => {
652
+ if (q.status === undefined)
653
+ q.status = 1;
725
654
  if (Array.isArray(q.option_item)) {
726
- q.option_item.forEach((opt: any) => {
727
- if (opt.status === undefined) opt.status = 1;
655
+ q.option_item.forEach((opt) => {
656
+ if (opt.status === undefined)
657
+ opt.status = 1;
728
658
  });
729
659
  }
730
660
  });
731
-
732
661
  // Validate each question (same as createCollect)
733
- questions.forEach((q: any, index: number) => {
662
+ questions.forEach((q, index) => {
734
663
  if (!q.question_id || !Number.isInteger(q.question_id) || q.question_id < 1) {
735
664
  throw new Error(`第${index + 1}个问题:question_id 必填且必须从 1 开始`);
736
665
  }
@@ -746,14 +675,13 @@ export class WecomDocClient {
746
675
  if (q.must_reply === undefined || typeof q.must_reply !== 'boolean') {
747
676
  throw new Error(`第${index + 1}个问题:must_reply 必填且必须为布尔值`);
748
677
  }
749
-
750
678
  // Validate option_item for single/multiple/dropdown questions
751
679
  const requiresOptions = [2, 3, 15].includes(q.reply_type);
752
680
  if (requiresOptions) {
753
681
  if (!Array.isArray(q.option_item) || q.option_item.length === 0) {
754
682
  throw new Error(`第${index + 1}个问题:单选/多选/下拉列表必须提供 option_item 数组`);
755
683
  }
756
- q.option_item.forEach((opt: any, optIndex: number) => {
684
+ q.option_item.forEach((opt, optIndex) => {
757
685
  if (!opt.key || !Number.isInteger(opt.key) || opt.key < 1) {
758
686
  throw new Error(`第${index + 1}个问题的第${optIndex + 1}个选项:key 必填且从 1 开始`);
759
687
  }
@@ -763,24 +691,20 @@ export class WecomDocClient {
763
691
  });
764
692
  }
765
693
  });
766
-
767
694
  payload.form_info = { form_question: formInfo.form_question };
768
-
769
- } else if (operNum === 2) {
695
+ }
696
+ else if (operNum === 2) {
770
697
  // 全量修改设置:必须提供完整的 form_setting 对象
771
698
  if (!formInfo || !formInfo.form_setting || typeof formInfo.form_setting !== 'object') {
772
699
  throw new Error("oper=2 时,必须提供 form_setting 对象(缺失的设置项将被重置为默认值)");
773
700
  }
774
-
775
701
  // Validate timed_repeat_info and timed_finish are mutually exclusive
776
702
  const formSetting = formInfo.form_setting;
777
703
  if (formSetting.timed_repeat_info?.enable && formSetting.timed_finish) {
778
704
  console.warn("警告:timed_finish 与 timed_repeat_info 互斥,若都填优先定时重复");
779
705
  }
780
-
781
706
  payload.form_info = { form_setting: formSetting };
782
707
  }
783
-
784
708
  const json = await this.postWecomDocApi({
785
709
  path: "/cgi-bin/wedoc/modify_form",
786
710
  actionLabel: "modify_form",
@@ -789,16 +713,16 @@ export class WecomDocClient {
789
713
  });
790
714
  return {
791
715
  raw: json,
792
- formId: payload.formid as string,
793
- oper: payload.oper as string,
716
+ formId: payload.formid,
717
+ oper: payload.oper,
794
718
  title: formInfo?.form_title ? readString(formInfo.form_title) : undefined,
795
719
  };
796
720
  }
797
-
798
- async getFormInfo(params: { agent: ResolvedAgentAccount; formId: string }) {
721
+ async getFormInfo(params) {
799
722
  const { agent, formId } = params;
800
723
  const normalizedFormId = readString(formId);
801
- if (!normalizedFormId) throw new Error("formId required");
724
+ if (!normalizedFormId)
725
+ throw new Error("formId required");
802
726
  const json = await this.postWecomDocApi({
803
727
  path: "/cgi-bin/wedoc/get_form_info",
804
728
  actionLabel: "get_form_info",
@@ -810,23 +734,21 @@ export class WecomDocClient {
810
734
  formInfo: readObject(json.form_info),
811
735
  };
812
736
  }
813
-
814
- async getFormAnswer(params: { agent: ResolvedAgentAccount; repeatedId: string; answerIds?: unknown[] }) {
737
+ async getFormAnswer(params) {
815
738
  const { agent, repeatedId, answerIds } = params;
816
739
  const normalizedRepeatedId = readString(repeatedId);
817
- if (!normalizedRepeatedId) throw new Error("repeatedId required");
740
+ if (!normalizedRepeatedId)
741
+ throw new Error("repeatedId required");
818
742
  const normalizedAnswerIds = Array.isArray(answerIds)
819
743
  ? answerIds
820
744
  .map((item) => Number(item))
821
745
  .filter((item) => Number.isFinite(item))
822
746
  : [];
823
-
824
747
  // Official API limit: ≤100 answer IDs
825
748
  if (normalizedAnswerIds.length > 100) {
826
749
  throw new Error(`answer_ids 不能超过 100 个,当前:${normalizedAnswerIds.length}`);
827
750
  }
828
-
829
- const payload: Record<string, unknown> = {
751
+ const payload = {
830
752
  repeated_id: normalizedRepeatedId,
831
753
  };
832
754
  if (normalizedAnswerIds.length > 0) {
@@ -842,11 +764,10 @@ export class WecomDocClient {
842
764
  return {
843
765
  raw: json,
844
766
  answer,
845
- answerList: readArray((answer as any).answer_list),
767
+ answerList: readArray(answer.answer_list),
846
768
  };
847
769
  }
848
-
849
- async getFormStatistic(params: { agent: ResolvedAgentAccount; requests: unknown[] }) {
770
+ async getFormStatistic(params) {
850
771
  const { agent, requests } = params;
851
772
  const payload = Array.isArray(requests)
852
773
  ? requests.map((item) => readObject(item)).filter((item) => Object.keys(item).length > 0)
@@ -854,11 +775,9 @@ export class WecomDocClient {
854
775
  if (payload.length === 0) {
855
776
  throw new Error("requests required");
856
777
  }
857
-
858
778
  // Validate each request per official API
859
- payload.forEach((req: any, index: number) => {
779
+ payload.forEach((req, index) => {
860
780
  const reqType = Number(req.req_type);
861
-
862
781
  // req_type=2: Get submitted list - requires start_time and end_time (same day timestamps)
863
782
  if (reqType === 2) {
864
783
  if (!req.start_time || !req.end_time) {
@@ -873,13 +792,11 @@ export class WecomDocClient {
873
792
  throw new Error(`第${index + 1}个请求:end_time 必须大于等于 start_time`);
874
793
  }
875
794
  }
876
-
877
795
  // Validate repeated_id is present
878
796
  if (!req.repeated_id) {
879
797
  throw new Error(`第${index + 1}个请求:repeated_id 必填`);
880
798
  }
881
799
  });
882
-
883
800
  const json = await this.postWecomDocApi({
884
801
  path: "/cgi-bin/wedoc/get_form_statistic",
885
802
  actionLabel: "get_form_statistic",
@@ -890,21 +807,18 @@ export class WecomDocClient {
890
807
  return {
891
808
  raw: json,
892
809
  items: statisticList,
893
- successCount: statisticList.filter((item: any) => Number(item?.errcode ?? 0) === 0).length,
810
+ successCount: statisticList.filter((item) => Number(item?.errcode ?? 0) === 0).length,
894
811
  };
895
812
  }
896
-
897
813
  // --- Content Operations (New) ---
898
-
899
- async getDocContent(params: { agent: ResolvedAgentAccount; docId: string }) {
814
+ async getDocContent(params) {
900
815
  const { agent, docId } = params;
901
816
  const json = await this.postWecomDocApi({
902
817
  path: "/cgi-bin/wedoc/document/get",
903
818
  actionLabel: "get_doc_content",
904
819
  agent,
905
820
  body: { docid: readString(docId) },
906
- }) as GetDocContentResponse;
907
-
821
+ });
908
822
  // Ensure structure strictly matches official API: { version: number, document: Node }
909
823
  return {
910
824
  raw: json,
@@ -912,16 +826,13 @@ export class WecomDocClient {
912
826
  document: json.document
913
827
  };
914
828
  }
915
-
916
- async updateDocContent(params: { agent: ResolvedAgentAccount; docId: string; requests: UpdateRequest[]; version?: number; batchMode?: boolean }) {
829
+ async updateDocContent(params) {
917
830
  const { agent, docId, requests, version } = params;
918
-
919
831
  // Validate requests structure basic check
920
832
  const requestList = readArray(requests);
921
833
  if (requestList.length === 0) {
922
- throw new Error("requests list cannot be empty");
834
+ throw new Error("requests list cannot be empty");
923
835
  }
924
-
925
836
  // Validate version difference (≤100 per official API)
926
837
  if (version !== undefined && version !== null) {
927
838
  const currentContent = await this.getDocContent({ agent, docId });
@@ -930,9 +841,8 @@ export class WecomDocClient {
930
841
  throw new Error(`version 与最新版本差值不能超过 100(当前版本:${currentContent.version},传入版本:${version},差值:${versionDiff})`);
931
842
  }
932
843
  }
933
-
934
844
  // Validate each request's ranges count (≤10 per official API)
935
- requestList.forEach((req: any, index: number) => {
845
+ requestList.forEach((req, index) => {
936
846
  if (req.replace_text?.ranges && req.replace_text.ranges.length > 10) {
937
847
  throw new Error(`第${index + 1}个操作:replace_text.ranges 不能超过 10 个`);
938
848
  }
@@ -942,70 +852,65 @@ export class WecomDocClient {
942
852
  // Validate insert_table limits
943
853
  if (req.insert_table) {
944
854
  const { rows, cols } = req.insert_table;
945
- if (rows > 100) throw new Error(`第${index + 1}个操作:insert_table 行数不能超过 100`);
946
- if (cols > 60) throw new Error(`第${index + 1}个操作:insert_table 列数不能超过 60`);
947
- if (rows * cols > 1000) throw new Error(`第${index + 1}个操作:insert_table 单元格总数不能超过 1000`);
855
+ if (rows > 100)
856
+ throw new Error(`第${index + 1}个操作:insert_table 行数不能超过 100`);
857
+ if (cols > 60)
858
+ throw new Error(`第${index + 1}个操作:insert_table 列数不能超过 60`);
859
+ if (rows * cols > 1000)
860
+ throw new Error(`第${index + 1}个操作:insert_table 单元格总数不能超过 1000`);
948
861
  }
949
862
  });
950
-
951
863
  // Official API limit: ≤30 operations per batch
952
864
  const MAX_OPERATIONS = 30;
953
865
  if (requestList.length <= MAX_OPERATIONS) {
954
866
  // Single batch
955
- const body: Record<string, unknown> = {
867
+ const body = {
956
868
  docid: readString(docId),
957
869
  requests: requestList,
958
870
  };
959
871
  if (version !== undefined && version !== null) {
960
872
  body.version = Number(version);
961
873
  }
962
-
963
874
  const json = await this.postWecomDocApi({
964
875
  path: "/cgi-bin/wedoc/document/batch_update",
965
876
  actionLabel: "update_doc_content",
966
877
  agent,
967
878
  body,
968
- }) as BatchUpdateDocResponse;
879
+ });
969
880
  return { raw: json, batches: 1 };
970
881
  }
971
-
972
882
  // Auto-batch: split into multiple requests
973
883
  // Note: Each batch updates the version, so we need to get latest version for each batch
974
- const batches: BatchUpdateDocResponse[] = [];
884
+ const batches = [];
975
885
  for (let i = 0; i < requestList.length; i += MAX_OPERATIONS) {
976
886
  const batchRequests = requestList.slice(i, i + MAX_OPERATIONS);
977
-
978
887
  // Get latest version before each batch (except first if version provided)
979
888
  let currentVersion = version;
980
889
  if (i > 0 || currentVersion === undefined || currentVersion === null) {
981
890
  const content = await this.getDocContent({ agent, docId });
982
891
  currentVersion = content.version;
983
892
  }
984
-
985
- const body: Record<string, unknown> = {
893
+ const body = {
986
894
  docid: readString(docId),
987
895
  requests: batchRequests,
988
896
  version: currentVersion,
989
897
  };
990
-
991
898
  const json = await this.postWecomDocApi({
992
899
  path: "/cgi-bin/wedoc/document/batch_update",
993
900
  actionLabel: `update_doc_content_batch_${Math.floor(i / MAX_OPERATIONS) + 1}`,
994
901
  agent,
995
902
  body,
996
- }) as BatchUpdateDocResponse;
903
+ });
997
904
  batches.push(json);
998
905
  }
999
-
1000
906
  return { raw: batches[batches.length - 1], batches: batches.length, allBatches: batches };
1001
907
  }
1002
-
1003
908
  // --- Spreadsheet Operations ---
1004
-
1005
- async getSheetProperties(params: { agent: ResolvedAgentAccount; docId: string }) {
909
+ async getSheetProperties(params) {
1006
910
  const { agent, docId } = params;
1007
911
  const normalizedDocId = readString(docId);
1008
- if (!normalizedDocId) throw new Error("docId required");
912
+ if (!normalizedDocId)
913
+ throw new Error("docId required");
1009
914
  const json = await this.postWecomDocApi({
1010
915
  path: "/cgi-bin/wedoc/spreadsheet/get_sheet_properties",
1011
916
  actionLabel: "get_sheet_properties",
@@ -1014,15 +919,13 @@ export class WecomDocClient {
1014
919
  });
1015
920
  return {
1016
921
  raw: json,
1017
- properties:
1018
- (Array.isArray(json.properties) && json.properties) ||
922
+ properties: (Array.isArray(json.properties) && json.properties) ||
1019
923
  (Array.isArray(json.sheet_properties) && json.sheet_properties) ||
1020
924
  (Array.isArray(json.sheet_list) && json.sheet_list) ||
1021
925
  [],
1022
926
  };
1023
927
  }
1024
-
1025
- async modDocMemberNotifiedScope(params: { agent: ResolvedAgentAccount; docId: string; notified_scope_type: number; notified_member_list?: any[] }) {
928
+ async modDocMemberNotifiedScope(params) {
1026
929
  const { agent, docId, notified_scope_type, notified_member_list } = params;
1027
930
  const json = await this.postWecomDocApi({
1028
931
  path: "/cgi-bin/wedoc/mod_doc_member_notified_scope",
@@ -1032,106 +935,88 @@ export class WecomDocClient {
1032
935
  });
1033
936
  return json;
1034
937
  }
1035
-
1036
- async editSheetData(params: {
1037
- agent: ResolvedAgentAccount;
1038
- docId: string;
1039
- sheetId: string;
1040
- startRow?: number;
1041
- startColumn?: number;
1042
- gridData?: any;
1043
- requests?: any[]; // For direct batch_update with multiple operations
1044
- }) {
938
+ async editSheetData(params) {
1045
939
  const { agent, docId, sheetId, startRow = 0, startColumn = 0, gridData, requests } = params;
1046
-
1047
940
  // Validate required docId
1048
941
  const normalizedDocId = readString(docId);
1049
942
  if (!normalizedDocId) {
1050
943
  throw new Error('docId is required');
1051
944
  }
1052
-
1053
945
  // Validate required sheetId
1054
946
  const normalizedSheetId = readString(sheetId);
1055
947
  if (!normalizedSheetId) {
1056
948
  throw new Error('sheetId is required');
1057
949
  }
1058
-
1059
950
  // Handle direct requests (for multiple operations)
1060
951
  if (requests && requests.length > 0) {
1061
952
  // Official API limit: ≤5 operations per batch
1062
953
  const MAX_OPERATIONS = 5;
1063
-
1064
954
  // Validate each request
1065
- requests.forEach((req: any, index: number) => {
955
+ requests.forEach((req, index) => {
1066
956
  if (req.update_range_request?.grid_data?.rows) {
1067
957
  const rows = req.update_range_request.grid_data.rows;
1068
958
  const rowCount = rows.length;
1069
- const rowWidths = rows.map((row: any) => row.values?.length || 0);
959
+ const rowWidths = rows.map((row) => row.values?.length || 0);
1070
960
  const columnCount = rowWidths.length > 0 ? Math.max(...rowWidths) : 0;
1071
- const totalCells = rowWidths.reduce((sum: number, width: number) => sum + width, 0);
1072
-
1073
- if (rowCount > 1000) throw new Error(`第${index + 1}个操作:行数不能超过 1000`);
1074
- if (columnCount > 200) throw new Error(`第${index + 1}个操作:列数不能超过 200`);
1075
- if (totalCells > 10000) throw new Error(`第${index + 1}个操作:单元格总数不能超过 10000`);
961
+ const totalCells = rowWidths.reduce((sum, width) => sum + width, 0);
962
+ if (rowCount > 1000)
963
+ throw new Error(`第${index + 1}个操作:行数不能超过 1000`);
964
+ if (columnCount > 200)
965
+ throw new Error(`第${index + 1}个操作:列数不能超过 200`);
966
+ if (totalCells > 10000)
967
+ throw new Error(`第${index + 1}个操作:单元格总数不能超过 10000`);
1076
968
  }
1077
969
  });
1078
-
1079
970
  if (requests.length > MAX_OPERATIONS) {
1080
971
  throw new Error(`单次批量更新最多${MAX_OPERATIONS}个操作,当前:${requests.length}`);
1081
972
  }
1082
-
1083
973
  const body = {
1084
974
  docid: normalizedDocId,
1085
975
  requests: requests
1086
976
  };
1087
-
1088
977
  const json = await this.postWecomDocApi({
1089
978
  path: "/cgi-bin/wedoc/spreadsheet/batch_update",
1090
979
  actionLabel: "spreadsheet_batch_update",
1091
980
  agent, body,
1092
981
  });
1093
- return {
1094
- raw: json,
982
+ return {
983
+ raw: json,
1095
984
  docId: normalizedDocId,
1096
985
  operations: requests.length
1097
986
  };
1098
987
  }
1099
-
1100
988
  // Handle single gridData update
1101
989
  if (!gridData) {
1102
990
  throw new Error('gridData or requests is required');
1103
991
  }
1104
-
1105
992
  // Build GridData per official API
1106
993
  // gridData.rows[i].values[j] must be: {cell_value: {text} | {link: {text, url}}, cell_format?: {...}}
1107
- const rows = (gridData.rows || []).map((row: any) => ({
1108
- values: (row.values || []).map((cell: any) => {
994
+ const rows = (gridData.rows || []).map((row) => ({
995
+ values: (row.values || []).map((cell) => {
1109
996
  // If already CellData format, use as-is
1110
997
  if (cell && typeof cell === 'object' && cell.cell_value) {
1111
998
  return cell;
1112
999
  }
1113
1000
  // Support link simplified format: { url: '...', text: '...' }
1114
1001
  if (cell && typeof cell === 'object' && cell.url) {
1115
- return {
1116
- cell_value: {
1117
- link: {
1118
- url: String(cell.url),
1119
- text: String(cell.text ?? cell.url)
1120
- }
1121
- }
1002
+ return {
1003
+ cell_value: {
1004
+ link: {
1005
+ url: String(cell.url),
1006
+ text: String(cell.text ?? cell.url)
1007
+ }
1008
+ }
1122
1009
  };
1123
1010
  }
1124
1011
  // Otherwise wrap primitive as CellValue with text
1125
1012
  return { cell_value: { text: String(cell ?? '') } };
1126
1013
  })
1127
1014
  }));
1128
-
1129
1015
  // Validate range limits per API spec
1130
1016
  const rowCount = rows.length;
1131
- const rowWidths = rows.map((row: any) => row.values?.length || 0);
1017
+ const rowWidths = rows.map((row) => row.values?.length || 0);
1132
1018
  const columnCount = rowWidths.length > 0 ? Math.max(...rowWidths) : 0;
1133
- const totalCells = rowWidths.reduce((sum: number, width: number) => sum + width, 0);
1134
-
1019
+ const totalCells = rowWidths.reduce((sum, width) => sum + width, 0);
1135
1020
  if (rowCount > 1000) {
1136
1021
  throw new Error(`行数不能超过 1000,当前:${rowCount}`);
1137
1022
  }
@@ -1141,42 +1026,37 @@ export class WecomDocClient {
1141
1026
  if (totalCells > 10000) {
1142
1027
  throw new Error(`单元格总数不能超过 10000,当前:${totalCells}`);
1143
1028
  }
1144
-
1145
1029
  const finalGridData = {
1146
1030
  start_row: startRow,
1147
1031
  start_column: startColumn,
1148
1032
  rows: rows
1149
1033
  };
1150
-
1151
1034
  // Build batch_update request per official API (single operation)
1152
1035
  const body = {
1153
1036
  docid: normalizedDocId,
1154
1037
  requests: [{
1155
- update_range_request: {
1156
- sheet_id: normalizedSheetId,
1157
- grid_data: finalGridData
1158
- }
1159
- }]
1038
+ update_range_request: {
1039
+ sheet_id: normalizedSheetId,
1040
+ grid_data: finalGridData
1041
+ }
1042
+ }]
1160
1043
  };
1161
-
1162
1044
  const json = await this.postWecomDocApi({
1163
1045
  path: "/cgi-bin/wedoc/spreadsheet/batch_update",
1164
1046
  actionLabel: "spreadsheet_batch_update",
1165
1047
  agent, body,
1166
1048
  });
1167
- return {
1168
- raw: json,
1049
+ return {
1050
+ raw: json,
1169
1051
  docId: normalizedDocId,
1170
1052
  updatedCells: json.data?.responses?.[0]?.update_range_response?.updated_cells || 0
1171
1053
  };
1172
1054
  }
1173
-
1174
1055
  /**
1175
1056
  * Build CellFormat object per official API
1176
1057
  */
1177
- private buildCellFormat(formatData: any): any {
1178
- const textFormat: any = {};
1179
-
1058
+ buildCellFormat(formatData) {
1059
+ const textFormat = {};
1180
1060
  // Font properties
1181
1061
  if (formatData.font != null) {
1182
1062
  textFormat.font = String(formatData.font);
@@ -1196,7 +1076,6 @@ export class WecomDocClient {
1196
1076
  if (formatData.underline != null) {
1197
1077
  textFormat.underline = Boolean(formatData.underline);
1198
1078
  }
1199
-
1200
1079
  // Color (RGBA)
1201
1080
  if (formatData.color != null && typeof formatData.color === "object") {
1202
1081
  const color = formatData.color;
@@ -1207,16 +1086,13 @@ export class WecomDocClient {
1207
1086
  alpha: Math.min(255, Math.max(0, Number(color.alpha ?? 255)))
1208
1087
  };
1209
1088
  }
1210
-
1211
1089
  // Return empty object if no format properties
1212
1090
  if (Object.keys(textFormat).length === 0) {
1213
1091
  return null;
1214
1092
  }
1215
-
1216
1093
  return { text_format: textFormat };
1217
1094
  }
1218
-
1219
- async getSheetData(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; range: string }) {
1095
+ async getSheetData(params) {
1220
1096
  const { agent, docId, sheetId, range } = params;
1221
1097
  const body = { docid: readString(docId), sheet_id: readString(sheetId), range: readString(range) };
1222
1098
  const json = await this.postWecomDocApi({
@@ -1226,8 +1102,7 @@ export class WecomDocClient {
1226
1102
  });
1227
1103
  return { raw: json, data: json };
1228
1104
  }
1229
-
1230
- async modifySheetProperties(params: { agent: ResolvedAgentAccount; docId: string; requests: unknown[] }) {
1105
+ async modifySheetProperties(params) {
1231
1106
  const { agent, docId, requests } = params;
1232
1107
  const json = await this.postWecomDocApi({
1233
1108
  path: "/cgi-bin/wedoc/spreadsheet/batch_update",
@@ -1236,10 +1111,8 @@ export class WecomDocClient {
1236
1111
  });
1237
1112
  return { raw: json, docId: docId };
1238
1113
  }
1239
-
1240
1114
  // --- Smart Table Operations ---
1241
-
1242
- async smartTableOperate(params: { agent: ResolvedAgentAccount; docId: string; operation: string; bodyData: any }) {
1115
+ async smartTableOperate(params) {
1243
1116
  const { agent, docId, operation, bodyData } = params;
1244
1117
  const body = { docid: readString(docId), ...readObject(bodyData) };
1245
1118
  const path = `/cgi-bin/wedoc/smartsheet/${operation}`;
@@ -1250,14 +1123,15 @@ export class WecomDocClient {
1250
1123
  });
1251
1124
  return { raw: json, docId };
1252
1125
  }
1253
-
1254
- async smartTableGetSheets(params: { agent: ResolvedAgentAccount; docId: string; sheet_id?: string; need_all_type_sheet?: boolean }) {
1126
+ async smartTableGetSheets(params) {
1255
1127
  const { agent, docId, sheet_id, need_all_type_sheet } = params;
1256
- const payload: Record<string, unknown> = {
1128
+ const payload = {
1257
1129
  docid: readString(docId),
1258
1130
  };
1259
- if (sheet_id) payload.sheet_id = sheet_id;
1260
- if (need_all_type_sheet !== undefined) payload.need_all_type_sheet = need_all_type_sheet;
1131
+ if (sheet_id)
1132
+ payload.sheet_id = sheet_id;
1133
+ if (need_all_type_sheet !== undefined)
1134
+ payload.need_all_type_sheet = need_all_type_sheet;
1261
1135
  const json = await this.postWecomDocApi({
1262
1136
  path: "/cgi-bin/wedoc/smartsheet/get_sheet",
1263
1137
  actionLabel: "smartsheet_get_sheet",
@@ -1269,33 +1143,21 @@ export class WecomDocClient {
1269
1143
  sheets: readArray(json.sheet_list),
1270
1144
  };
1271
1145
  }
1272
-
1273
- async smartTableAddSheet(params: { agent: ResolvedAgentAccount; docId: string; title: string; index?: number }) {
1146
+ async smartTableAddSheet(params) {
1274
1147
  const { agent, docId, title, index } = params;
1275
1148
  return this.smartTableOperate({ agent, docId, operation: "add_sheet", bodyData: { properties: { title, index } } });
1276
1149
  }
1277
-
1278
- async smartTableDelSheet(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string }) {
1150
+ async smartTableDelSheet(params) {
1279
1151
  const { agent, docId, sheetId } = params;
1280
1152
  return this.smartTableOperate({ agent, docId, operation: "delete_sheet", bodyData: { sheet_id: sheetId } });
1281
1153
  }
1282
-
1283
- async smartTableUpdateSheet(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; title: string }) {
1154
+ async smartTableUpdateSheet(params) {
1284
1155
  const { agent, docId, sheetId, title } = params;
1285
1156
  return this.smartTableOperate({ agent, docId, operation: "update_sheet", bodyData: { properties: { sheet_id: sheetId, title } } });
1286
1157
  }
1287
- async smartTableAddView(params: {
1288
- agent: ResolvedAgentAccount;
1289
- docId: string;
1290
- sheetId: string;
1291
- view_title: string;
1292
- view_type: string;
1293
- property?: any; // ViewProperty: sort_spec, filter_spec, group_spec, etc.
1294
- property_gantt?: any; // Deprecated, use property instead
1295
- property_calendar?: any; // Deprecated, use property instead
1296
- }) {
1158
+ async smartTableAddView(params) {
1297
1159
  const { agent, docId, sheetId, view_title, view_type, property, property_gantt, property_calendar } = params;
1298
- const payload: Record<string, unknown> = {
1160
+ const payload = {
1299
1161
  docid: readString(docId),
1300
1162
  sheet_id: readString(sheetId),
1301
1163
  view_title: readString(view_title),
@@ -1305,9 +1167,10 @@ export class WecomDocClient {
1305
1167
  payload.property = property;
1306
1168
  }
1307
1169
  // Support deprecated property_gantt/property_calendar for backward compatibility
1308
- if (property_gantt) payload.property_gantt = property_gantt;
1309
- if (property_calendar) payload.property_calendar = property_calendar;
1310
-
1170
+ if (property_gantt)
1171
+ payload.property_gantt = property_gantt;
1172
+ if (property_calendar)
1173
+ payload.property_calendar = property_calendar;
1311
1174
  const json = await this.postWecomDocApi({
1312
1175
  path: "/cgi-bin/wedoc/smartsheet/add_view",
1313
1176
  actionLabel: "smartsheet_add_view",
@@ -1319,31 +1182,23 @@ export class WecomDocClient {
1319
1182
  view: json.view,
1320
1183
  };
1321
1184
  }
1322
-
1323
- async smartTableUpdateView(params: {
1324
- agent: ResolvedAgentAccount;
1325
- docId: string;
1326
- sheetId: string;
1327
- view_id: string;
1328
- view_title?: string;
1329
- property?: any; // ViewProperty: sort_spec, filter_spec, group_spec, etc.
1330
- property_gantt?: any; // Deprecated, use property instead
1331
- property_calendar?: any; // Deprecated, use property instead
1332
- }) {
1185
+ async smartTableUpdateView(params) {
1333
1186
  const { agent, docId, sheetId, view_id, view_title, property, property_gantt, property_calendar } = params;
1334
- const payload: Record<string, unknown> = {
1187
+ const payload = {
1335
1188
  docid: readString(docId),
1336
1189
  sheet_id: readString(sheetId),
1337
1190
  view_id: readString(view_id),
1338
1191
  };
1339
- if (view_title) payload.view_title = readString(view_title);
1192
+ if (view_title)
1193
+ payload.view_title = readString(view_title);
1340
1194
  if (property && typeof property === 'object') {
1341
1195
  payload.property = property;
1342
1196
  }
1343
1197
  // Support deprecated property_gantt/property_calendar for backward compatibility
1344
- if (property_gantt) payload.property_gantt = property_gantt;
1345
- if (property_calendar) payload.property_calendar = property_calendar;
1346
-
1198
+ if (property_gantt)
1199
+ payload.property_gantt = property_gantt;
1200
+ if (property_calendar)
1201
+ payload.property_calendar = property_calendar;
1347
1202
  const json = await this.postWecomDocApi({
1348
1203
  path: "/cgi-bin/wedoc/smartsheet/update_view",
1349
1204
  actionLabel: "smartsheet_update_view",
@@ -1355,14 +1210,11 @@ export class WecomDocClient {
1355
1210
  view: json.view,
1356
1211
  };
1357
1212
  }
1358
-
1359
- async smartTableDelView(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; view_ids: string[] }) {
1213
+ async smartTableDelView(params) {
1360
1214
  const { agent, docId, sheetId, view_ids } = params;
1361
-
1362
1215
  if (!Array.isArray(view_ids) || view_ids.length === 0) {
1363
1216
  throw new Error("view_ids 必须是非空数组");
1364
1217
  }
1365
-
1366
1218
  return this.postWecomDocApi({
1367
1219
  path: "/cgi-bin/wedoc/smartsheet/delete_views",
1368
1220
  actionLabel: "smartsheet_del_view",
@@ -1374,24 +1226,18 @@ export class WecomDocClient {
1374
1226
  },
1375
1227
  });
1376
1228
  }
1377
-
1378
- async smartTableGetViews(params: {
1379
- agent: ResolvedAgentAccount;
1380
- docId: string;
1381
- sheetId: string;
1382
- view_ids?: string[];
1383
- offset?: number;
1384
- limit?: number;
1385
- }) {
1229
+ async smartTableGetViews(params) {
1386
1230
  const { agent, docId, sheetId, view_ids, offset, limit } = params;
1387
- const payload: Record<string, unknown> = {
1231
+ const payload = {
1388
1232
  docid: readString(docId),
1389
1233
  sheet_id: readString(sheetId),
1390
1234
  };
1391
- if (view_ids && Array.isArray(view_ids)) payload.view_ids = view_ids;
1392
- if (offset !== undefined) payload.offset = offset;
1393
- if (limit !== undefined) payload.limit = limit;
1394
-
1235
+ if (view_ids && Array.isArray(view_ids))
1236
+ payload.view_ids = view_ids;
1237
+ if (offset !== undefined)
1238
+ payload.offset = offset;
1239
+ if (limit !== undefined)
1240
+ payload.limit = limit;
1395
1241
  const json = await this.postWecomDocApi({
1396
1242
  path: "/cgi-bin/wedoc/smartsheet/get_views",
1397
1243
  actionLabel: "smartsheet_get_views",
@@ -1406,23 +1252,14 @@ export class WecomDocClient {
1406
1252
  next: json.next,
1407
1253
  };
1408
1254
  }
1409
-
1410
- async smartTableAddFields(params: {
1411
- agent: ResolvedAgentAccount;
1412
- docId: string;
1413
- sheetId: string;
1414
- fields: any[];
1415
- autoCleanupDefaultField?: boolean; // Auto-delete leftover default field after adding new fields
1416
- }) {
1255
+ async smartTableAddFields(params) {
1417
1256
  const { agent, docId, sheetId, fields, autoCleanupDefaultField = true } = params;
1418
-
1419
1257
  // Validate fields per official API spec
1420
1258
  if (!Array.isArray(fields) || fields.length === 0) {
1421
1259
  throw new Error("fields 必须是非空数组");
1422
1260
  }
1423
-
1424
1261
  // Validate each field has required field_title and field_type
1425
- fields.forEach((field: any, index: number) => {
1262
+ fields.forEach((field, index) => {
1426
1263
  if (!field.field_title) {
1427
1264
  throw new Error(`第${index + 1}个字段:field_title 必填`);
1428
1265
  }
@@ -1444,7 +1281,6 @@ export class WecomDocClient {
1444
1281
  throw new Error(`第${index + 1}个字段:field_type 必须是有效的字段类型(见 FieldType 枚举)`);
1445
1282
  }
1446
1283
  });
1447
-
1448
1284
  const json = await this.postWecomDocApi({
1449
1285
  path: "/cgi-bin/wedoc/smartsheet/add_fields",
1450
1286
  actionLabel: "smartsheet_add_fields",
@@ -1455,38 +1291,27 @@ export class WecomDocClient {
1455
1291
  fields: fields,
1456
1292
  },
1457
1293
  });
1458
-
1459
1294
  const result = {
1460
1295
  raw: json,
1461
1296
  fields: readArray(json.fields),
1462
1297
  };
1463
-
1464
1298
  // Auto-cleanup: delete leftover default field after successfully adding new fields
1465
1299
  // This handles the case where initializeSmartTable kept 1 default field
1466
1300
  if (autoCleanupDefaultField) {
1467
1301
  await this.cleanupLeftoverDefaultField({ agent, docId, sheetId, newlyAddedFieldCount: result.fields.length });
1468
1302
  }
1469
-
1470
1303
  return result;
1471
1304
  }
1472
-
1473
1305
  /**
1474
1306
  * Cleanup leftover default field after adding new fields
1475
1307
  * When user adds new fields, we can safely delete the leftover default field from initialization
1476
1308
  */
1477
- private async cleanupLeftoverDefaultField(params: {
1478
- agent: ResolvedAgentAccount;
1479
- docId: string;
1480
- sheetId: string;
1481
- newlyAddedFieldCount: number;
1482
- }) {
1309
+ async cleanupLeftoverDefaultField(params) {
1483
1310
  const { agent, docId, sheetId, newlyAddedFieldCount } = params;
1484
-
1485
1311
  try {
1486
1312
  // Get all fields to find the leftover default field
1487
1313
  const fieldsResult = await this.smartTableGetFields({ agent, docId, sheetId, limit: 100 });
1488
1314
  const allFields = fieldsResult.fields || [];
1489
-
1490
1315
  // After adding N new fields to a table with 1 default field, we should have N+1 fields
1491
1316
  // If total = newlyAdded + 1, then there's 1 leftover default field to delete
1492
1317
  if (allFields.length === newlyAddedFieldCount + 1 && newlyAddedFieldCount > 0) {
@@ -1494,38 +1319,29 @@ export class WecomDocClient {
1494
1319
  // Default fields typically have generic titles like "文本", "数字", "日期", "单选", "人员"
1495
1320
  const defaultFieldTitles = ['文本', '数字', '日期', '单选', '人员', '文本 1', '数字 1', '日期 1', '单选 1', '人员 1'];
1496
1321
  const defaultFieldTypes = ['FIELD_TYPE_TEXT', 'FIELD_TYPE_NUMBER', 'FIELD_TYPE_DATE_TIME', 'FIELD_TYPE_SINGLE_SELECT', 'FIELD_TYPE_USER'];
1497
-
1498
- const leftoverField = allFields.find((field: any) => {
1322
+ const leftoverField = allFields.find((field) => {
1499
1323
  const isDefaultTitle = defaultFieldTitles.includes(field.field_title);
1500
1324
  const isDefaultType = defaultFieldTypes.includes(field.field_type);
1501
1325
  return isDefaultTitle && isDefaultType;
1502
- }) as any;
1503
-
1326
+ });
1504
1327
  if (leftoverField && leftoverField.field_id) {
1505
1328
  await this.smartTableDelFields({ agent, docId, sheetId, field_ids: [leftoverField.field_id] });
1506
1329
  }
1507
1330
  }
1508
- } catch (err) {
1331
+ }
1332
+ catch (err) {
1509
1333
  // Non-fatal: new fields added, just cleanup failed
1510
1334
  console.error(`[WecomDocClient] cleanupLeftoverDefaultField failed:`, err);
1511
1335
  }
1512
1336
  }
1513
-
1514
- async smartTableUpdateFields(params: {
1515
- agent: ResolvedAgentAccount;
1516
- docId: string;
1517
- sheetId: string;
1518
- fields: any[];
1519
- }) {
1337
+ async smartTableUpdateFields(params) {
1520
1338
  const { agent, docId, sheetId, fields } = params;
1521
-
1522
1339
  // Validate fields per official API spec
1523
1340
  if (!Array.isArray(fields) || fields.length === 0) {
1524
1341
  throw new Error("fields 必须是非空数组");
1525
1342
  }
1526
-
1527
1343
  // Validate each field has required field_id and field_type
1528
- fields.forEach((field: any, index: number) => {
1344
+ fields.forEach((field, index) => {
1529
1345
  if (!field.field_id) {
1530
1346
  throw new Error(`第${index + 1}个字段:field_id 必填`);
1531
1347
  }
@@ -1537,7 +1353,6 @@ export class WecomDocClient {
1537
1353
  throw new Error(`第${index + 1}个字段:field_title 或 property_* 属性至少提供一个`);
1538
1354
  }
1539
1355
  });
1540
-
1541
1356
  const json = await this.postWecomDocApi({
1542
1357
  path: "/cgi-bin/wedoc/smartsheet/update_fields",
1543
1358
  actionLabel: "smartsheet_update_fields",
@@ -1553,14 +1368,11 @@ export class WecomDocClient {
1553
1368
  fields: readArray(json.fields),
1554
1369
  };
1555
1370
  }
1556
-
1557
- async smartTableDelFields(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; field_ids: string[] }) {
1371
+ async smartTableDelFields(params) {
1558
1372
  const { agent, docId, sheetId, field_ids } = params;
1559
-
1560
1373
  if (!Array.isArray(field_ids) || field_ids.length === 0) {
1561
1374
  throw new Error("field_ids 必须是非空数组");
1562
1375
  }
1563
-
1564
1376
  return this.postWecomDocApi({
1565
1377
  path: "/cgi-bin/wedoc/smartsheet/delete_fields",
1566
1378
  actionLabel: "smartsheet_del_fields",
@@ -1572,28 +1384,22 @@ export class WecomDocClient {
1572
1384
  },
1573
1385
  });
1574
1386
  }
1575
-
1576
- async smartTableGetFields(params: {
1577
- agent: ResolvedAgentAccount;
1578
- docId: string;
1579
- sheetId: string;
1580
- view_id?: string;
1581
- field_ids?: string[];
1582
- field_titles?: string[];
1583
- offset?: number;
1584
- limit?: number;
1585
- }) {
1387
+ async smartTableGetFields(params) {
1586
1388
  const { agent, docId, sheetId, view_id, field_ids, field_titles, offset, limit } = params;
1587
- const payload: Record<string, unknown> = {
1389
+ const payload = {
1588
1390
  docid: readString(docId),
1589
1391
  sheet_id: readString(sheetId),
1590
1392
  };
1591
- if (view_id) payload.view_id = view_id;
1592
- if (field_ids && Array.isArray(field_ids)) payload.field_ids = field_ids;
1593
- if (field_titles && Array.isArray(field_titles)) payload.field_titles = field_titles;
1594
- if (offset !== undefined) payload.offset = offset;
1595
- if (limit !== undefined) payload.limit = limit;
1596
-
1393
+ if (view_id)
1394
+ payload.view_id = view_id;
1395
+ if (field_ids && Array.isArray(field_ids))
1396
+ payload.field_ids = field_ids;
1397
+ if (field_titles && Array.isArray(field_titles))
1398
+ payload.field_titles = field_titles;
1399
+ if (offset !== undefined)
1400
+ payload.offset = offset;
1401
+ if (limit !== undefined)
1402
+ payload.limit = limit;
1597
1403
  const json = await this.postWecomDocApi({
1598
1404
  path: "/cgi-bin/wedoc/smartsheet/get_fields",
1599
1405
  actionLabel: "smartsheet_get_fields",
@@ -1608,192 +1414,162 @@ export class WecomDocClient {
1608
1414
  next: json.next,
1609
1415
  };
1610
1416
  }
1611
-
1612
- async smartTableAddGroup(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; name: string; children?: string[] }) {
1417
+ async smartTableAddGroup(params) {
1613
1418
  const { agent, docId, sheetId, name, children } = params;
1614
1419
  return this.smartTableOperate({ agent, docId, operation: "add_field_group", bodyData: { sheet_id: sheetId, name, children } });
1615
1420
  }
1616
-
1617
- async smartTableDelGroup(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; field_group_id: string }) {
1421
+ async smartTableDelGroup(params) {
1618
1422
  const { agent, docId, sheetId, field_group_id } = params;
1619
1423
  return this.smartTableOperate({ agent, docId, operation: "delete_field_group", bodyData: { sheet_id: sheetId, field_group_id } });
1620
1424
  }
1621
-
1622
- async smartTableUpdateGroup(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; field_group_id: string; name?: string; children?: string[] }) {
1425
+ async smartTableUpdateGroup(params) {
1623
1426
  const { agent, docId, sheetId, field_group_id, name, children } = params;
1624
1427
  return this.smartTableOperate({ agent, docId, operation: "update_field_group", bodyData: { sheet_id: sheetId, field_group_id, name, children } });
1625
1428
  }
1626
-
1627
- async smartTableGetGroups(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string }) {
1429
+ async smartTableGetGroups(params) {
1628
1430
  const { agent, docId, sheetId } = params;
1629
1431
  return this.smartTableOperate({ agent, docId, operation: "get_field_groups", bodyData: { sheet_id: sheetId } });
1630
1432
  }
1631
-
1632
- async smartTableAddExternalRecords(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; records: any[] }) {
1433
+ async smartTableAddExternalRecords(params) {
1633
1434
  const { agent, docId, sheetId, records } = params;
1634
1435
  return this.smartTableOperate({ agent, docId, operation: "add_external_records", bodyData: { sheet_id: sheetId, records } });
1635
1436
  }
1636
-
1637
- async smartTableUpdateExternalRecords(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; records: any[] }) {
1437
+ async smartTableUpdateExternalRecords(params) {
1638
1438
  const { agent, docId, sheetId, records } = params;
1639
1439
  return this.smartTableOperate({ agent, docId, operation: "update_external_records", bodyData: { sheet_id: sheetId, records } });
1640
1440
  }
1641
-
1642
- async smartTableAddRecords(params: {
1643
- agent: ResolvedAgentAccount;
1644
- docId: string;
1645
- sheetId: string;
1646
- records: any[];
1647
- key_type?: string;
1648
- }) {
1441
+ async smartTableAddRecords(params) {
1649
1442
  const { agent, docId, sheetId, records, key_type } = params;
1650
-
1651
1443
  // Validate records format per official API spec (doc2.txt line 1594-1601)
1652
1444
  if (!Array.isArray(records) || records.length === 0) {
1653
1445
  throw new Error("records 必须是非空数组");
1654
1446
  }
1655
-
1656
1447
  // Strict validation: require correct format based on field type
1657
1448
  // Do NOT auto-convert ambiguous values to avoid corrupting user intent
1658
- const validatedRecords = records.map((record: any, recordIndex: number) => {
1449
+ const validatedRecords = records.map((record, recordIndex) => {
1659
1450
  if (!record.values || typeof record.values !== 'object' || Array.isArray(record.values)) {
1660
1451
  throw new Error(`第${recordIndex + 1}条记录:values 必须是非空对象`);
1661
1452
  }
1662
-
1663
- const validatedValues: Record<string, any> = {};
1453
+ const validatedValues = {};
1664
1454
  for (const [key, value] of Object.entries(record.values)) {
1665
1455
  // Accept both array and non-array formats based on field type
1666
1456
  // Array types: TEXT, USER, SELECT, SINGLE_SELECT, CHECKBOX, PHONE_NUMBER, EMAIL, URL, LOCATION, BARCODE, ATTACHMENT, IMAGE
1667
1457
  // Non-array types: NUMBER, DATE_TIME, PROGRESS, CURRENCY, PERCENTAGE
1668
-
1669
1458
  if (Array.isArray(value)) {
1670
1459
  // Array format - validate structure
1671
1460
  if (value.length === 0) {
1672
1461
  throw new Error(`第${recordIndex + 1}条记录字段 "${key}": 数组不能为空`);
1673
1462
  }
1674
1463
  validatedValues[key] = value;
1675
- } else if (typeof value === 'number') {
1464
+ }
1465
+ else if (typeof value === 'number') {
1676
1466
  // Non-array number - valid for NUMBER, PROGRESS, CURRENCY, PERCENTAGE
1677
1467
  validatedValues[key] = value;
1678
- } else if (typeof value === 'string' && /^\d{13}$/.test(value)) {
1468
+ }
1469
+ else if (typeof value === 'string' && /^\d{13}$/.test(value)) {
1679
1470
  // Non-array 13-digit string - valid for DATE_TIME (millisecond timestamp)
1680
1471
  validatedValues[key] = value;
1681
- } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1472
+ }
1473
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1682
1474
  // Object format - wrap in array for types like USER, TEXT object
1683
1475
  // This allows {user_id: "..."} to become [{user_id: "..."}]
1684
1476
  validatedValues[key] = [value];
1685
- } else {
1477
+ }
1478
+ else {
1686
1479
  // Reject ambiguous primitives (plain strings, booleans)
1687
1480
  // Users should explicitly use array format: [{type: "text", text: "..."}]
1688
- throw new Error(
1689
- `第${recordIndex + 1}条记录字段 "${key}": 值格式不明确。` +
1481
+ throw new Error(`第${recordIndex + 1}条记录字段 "${key}": 值格式不明确。` +
1690
1482
  `数字/日期类型直接写值 (25, "1704067200000"),` +
1691
- `文本/成员/选项类型用数组 ([{"type": "text", "text": "..."}], [{"user_id": "..."}])`
1692
- );
1483
+ `文本/成员/选项类型用数组 ([{"type": "text", "text": "..."}], [{"user_id": "..."}])`);
1693
1484
  }
1694
1485
  }
1695
-
1696
1486
  return {
1697
1487
  ...record,
1698
1488
  values: validatedValues,
1699
1489
  };
1700
1490
  });
1701
-
1702
- const bodyData: Record<string, unknown> = {
1491
+ const bodyData = {
1703
1492
  sheet_id: readString(sheetId),
1704
1493
  records: validatedRecords,
1705
1494
  };
1706
-
1707
1495
  if (key_type) {
1708
1496
  bodyData.key_type = key_type;
1709
1497
  }
1710
-
1711
1498
  return this.smartTableOperate({ agent, docId, operation: "add_records", bodyData });
1712
1499
  }
1713
-
1714
- async smartTableUpdateRecords(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; records: any[] }) {
1500
+ async smartTableUpdateRecords(params) {
1715
1501
  const { agent, docId, sheetId, records } = params;
1716
-
1717
1502
  // Strict validation: same as addRecords
1718
1503
  if (!Array.isArray(records) || records.length === 0) {
1719
1504
  throw new Error("records 必须是非空数组");
1720
1505
  }
1721
-
1722
- const validatedRecords = records.map((record: any, recordIndex: number) => {
1506
+ const validatedRecords = records.map((record, recordIndex) => {
1723
1507
  if (!record.record_id) {
1724
1508
  throw new Error(`第${recordIndex + 1}条记录缺少 record_id`);
1725
1509
  }
1726
1510
  if (!record.values || typeof record.values !== 'object' || Array.isArray(record.values)) {
1727
1511
  throw new Error(`第${recordIndex + 1}条记录:values 必须是非空对象`);
1728
1512
  }
1729
-
1730
- const validatedValues: Record<string, any> = {};
1513
+ const validatedValues = {};
1731
1514
  for (const [key, value] of Object.entries(record.values)) {
1732
1515
  if (Array.isArray(value)) {
1733
1516
  if (value.length === 0) {
1734
1517
  throw new Error(`第${recordIndex + 1}条记录字段 "${key}": 数组不能为空`);
1735
1518
  }
1736
1519
  validatedValues[key] = value;
1737
- } else if (typeof value === 'number') {
1520
+ }
1521
+ else if (typeof value === 'number') {
1738
1522
  validatedValues[key] = value;
1739
- } else if (typeof value === 'string' && /^\d{13}$/.test(value)) {
1523
+ }
1524
+ else if (typeof value === 'string' && /^\d{13}$/.test(value)) {
1740
1525
  validatedValues[key] = value;
1741
- } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1526
+ }
1527
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1742
1528
  validatedValues[key] = [value];
1743
- } else {
1744
- throw new Error(
1745
- `第${recordIndex + 1}条记录字段 "${key}": 值格式不明确。` +
1529
+ }
1530
+ else {
1531
+ throw new Error(`第${recordIndex + 1}条记录字段 "${key}": 值格式不明确。` +
1746
1532
  `数字/日期类型直接写值 (25, "1704067200000"),` +
1747
- `文本/成员/选项类型用数组 ([{"type": "text", "text": "..."}], [{"user_id": "..."}])`
1748
- );
1533
+ `文本/成员/选项类型用数组 ([{"type": "text", "text": "..."}], [{"user_id": "..."}])`);
1749
1534
  }
1750
1535
  }
1751
-
1752
1536
  return {
1753
1537
  ...record,
1754
1538
  values: validatedValues,
1755
1539
  };
1756
1540
  });
1757
-
1758
1541
  return this.smartTableOperate({ agent, docId, operation: "update_records", bodyData: { sheet_id: sheetId, records: validatedRecords } });
1759
1542
  }
1760
-
1761
- async smartTableDelRecords(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; record_ids: string[] }) {
1543
+ async smartTableDelRecords(params) {
1762
1544
  const { agent, docId, sheetId, record_ids } = params;
1763
1545
  return this.smartTableOperate({ agent, docId, operation: "delete_records", bodyData: { sheet_id: sheetId, record_ids } });
1764
1546
  }
1765
-
1766
- async smartTableGetRecords(params: {
1767
- agent: ResolvedAgentAccount;
1768
- docId: string;
1769
- sheetId: string;
1770
- view_id?: string;
1771
- record_ids?: string[];
1772
- key_type?: string;
1773
- field_titles?: string[];
1774
- field_ids?: string[];
1775
- sort?: any[];
1776
- offset?: number;
1777
- limit?: number;
1778
- ver?: number;
1779
- filter_spec?: any;
1780
- }) {
1547
+ async smartTableGetRecords(params) {
1781
1548
  const { agent, docId, sheetId, view_id, record_ids, key_type, field_titles, field_ids, sort, offset, limit, ver, filter_spec } = params;
1782
- const payload: Record<string, unknown> = {
1549
+ const payload = {
1783
1550
  docid: readString(docId),
1784
1551
  sheet_id: readString(sheetId),
1785
1552
  };
1786
- if (view_id) payload.view_id = view_id;
1787
- if (record_ids && Array.isArray(record_ids)) payload.record_ids = record_ids;
1788
- if (key_type) payload.key_type = key_type;
1789
- if (field_titles && Array.isArray(field_titles)) payload.field_titles = field_titles;
1790
- if (field_ids && Array.isArray(field_ids)) payload.field_ids = field_ids;
1791
- if (sort && Array.isArray(sort)) payload.sort = sort;
1792
- if (offset !== undefined) payload.offset = offset;
1793
- if (limit !== undefined) payload.limit = limit;
1794
- if (ver !== undefined) payload.ver = ver;
1795
- if (filter_spec && typeof filter_spec === 'object') payload.filter_spec = filter_spec;
1796
-
1553
+ if (view_id)
1554
+ payload.view_id = view_id;
1555
+ if (record_ids && Array.isArray(record_ids))
1556
+ payload.record_ids = record_ids;
1557
+ if (key_type)
1558
+ payload.key_type = key_type;
1559
+ if (field_titles && Array.isArray(field_titles))
1560
+ payload.field_titles = field_titles;
1561
+ if (field_ids && Array.isArray(field_ids))
1562
+ payload.field_ids = field_ids;
1563
+ if (sort && Array.isArray(sort))
1564
+ payload.sort = sort;
1565
+ if (offset !== undefined)
1566
+ payload.offset = offset;
1567
+ if (limit !== undefined)
1568
+ payload.limit = limit;
1569
+ if (ver !== undefined)
1570
+ payload.ver = ver;
1571
+ if (filter_spec && typeof filter_spec === 'object')
1572
+ payload.filter_spec = filter_spec;
1797
1573
  const json = await this.postWecomDocApi({
1798
1574
  path: "/cgi-bin/wedoc/smartsheet/get_records",
1799
1575
  actionLabel: "smartsheet_get_records",
@@ -1809,10 +1585,8 @@ export class WecomDocClient {
1809
1585
  ver: json.ver,
1810
1586
  };
1811
1587
  }
1812
-
1813
1588
  // --- Smartsheet Content Permissions ---
1814
-
1815
- async smartTableGetSheetPriv(params: { agent: ResolvedAgentAccount; docId: string; type: number; rule_id_list?: number[] }) {
1589
+ async smartTableGetSheetPriv(params) {
1816
1590
  const { agent, docId, type, rule_id_list } = params;
1817
1591
  const json = await this.postWecomDocApi({
1818
1592
  path: "/cgi-bin/wedoc/smartsheet/content_priv/get_sheet_priv",
@@ -1822,13 +1596,13 @@ export class WecomDocClient {
1822
1596
  });
1823
1597
  return { raw: json };
1824
1598
  }
1825
-
1826
- async smartTableUpdateSheetPriv(params: { agent: ResolvedAgentAccount; docId: string; type: number; rule_id?: number; name?: string; priv_list: any[] }) {
1599
+ async smartTableUpdateSheetPriv(params) {
1827
1600
  const { agent, docId, type, rule_id, name, priv_list } = params;
1828
- const body: any = { docid: readString(docId), type, priv_list };
1829
- if (rule_id !== undefined) body.rule_id = rule_id;
1830
- if (name !== undefined) body.name = name;
1831
-
1601
+ const body = { docid: readString(docId), type, priv_list };
1602
+ if (rule_id !== undefined)
1603
+ body.rule_id = rule_id;
1604
+ if (name !== undefined)
1605
+ body.name = name;
1832
1606
  const json = await this.postWecomDocApi({
1833
1607
  path: "/cgi-bin/wedoc/smartsheet/content_priv/update_sheet_priv",
1834
1608
  actionLabel: "smartsheet_update_sheet_priv",
@@ -1837,8 +1611,7 @@ export class WecomDocClient {
1837
1611
  });
1838
1612
  return { raw: json };
1839
1613
  }
1840
-
1841
- async smartTableCreateRule(params: { agent: ResolvedAgentAccount; docId: string; name: string }) {
1614
+ async smartTableCreateRule(params) {
1842
1615
  const { agent, docId, name } = params;
1843
1616
  const json = await this.postWecomDocApi({
1844
1617
  path: "/cgi-bin/wedoc/smartsheet/content_priv/create_rule",
@@ -1848,13 +1621,13 @@ export class WecomDocClient {
1848
1621
  });
1849
1622
  return { raw: json, rule_id: json.rule_id };
1850
1623
  }
1851
-
1852
- async smartTableModRuleMember(params: { agent: ResolvedAgentAccount; docId: string; rule_id: number; add_member_range?: any; del_member_range?: any }) {
1624
+ async smartTableModRuleMember(params) {
1853
1625
  const { agent, docId, rule_id, add_member_range, del_member_range } = params;
1854
- const body: any = { docid: readString(docId), rule_id };
1855
- if (add_member_range) body.add_member_range = add_member_range;
1856
- if (del_member_range) body.del_member_range = del_member_range;
1857
-
1626
+ const body = { docid: readString(docId), rule_id };
1627
+ if (add_member_range)
1628
+ body.add_member_range = add_member_range;
1629
+ if (del_member_range)
1630
+ body.del_member_range = del_member_range;
1858
1631
  const json = await this.postWecomDocApi({
1859
1632
  path: "/cgi-bin/wedoc/smartsheet/content_priv/mod_rule_member",
1860
1633
  actionLabel: "smartsheet_mod_rule_member",
@@ -1863,8 +1636,7 @@ export class WecomDocClient {
1863
1636
  });
1864
1637
  return { raw: json };
1865
1638
  }
1866
-
1867
- async smartTableDeleteRule(params: { agent: ResolvedAgentAccount; docId: string; rule_id_list: number[] }) {
1639
+ async smartTableDeleteRule(params) {
1868
1640
  const { agent, docId, rule_id_list } = params;
1869
1641
  const json = await this.postWecomDocApi({
1870
1642
  path: "/cgi-bin/wedoc/smartsheet/content_priv/delete_rule",
@@ -1874,10 +1646,8 @@ export class WecomDocClient {
1874
1646
  });
1875
1647
  return { raw: json };
1876
1648
  }
1877
-
1878
1649
  // --- Advanced Account Management ---
1879
-
1880
- async assignDocAdvancedAccount(params: { agent: ResolvedAgentAccount; userid_list: string[] }) {
1650
+ async assignDocAdvancedAccount(params) {
1881
1651
  const { agent, userid_list } = params;
1882
1652
  return this.postWecomDocApi({
1883
1653
  path: "/cgi-bin/meeting/vip/submit_batch_add_job",
@@ -1886,8 +1656,7 @@ export class WecomDocClient {
1886
1656
  body: { userid_list },
1887
1657
  });
1888
1658
  }
1889
-
1890
- async cancelDocAdvancedAccount(params: { agent: ResolvedAgentAccount; userid_list: string[] }) {
1659
+ async cancelDocAdvancedAccount(params) {
1891
1660
  const { agent, userid_list } = params;
1892
1661
  return this.postWecomDocApi({
1893
1662
  path: "/cgi-bin/meeting/vip/submit_batch_del_job",
@@ -1896,8 +1665,7 @@ export class WecomDocClient {
1896
1665
  body: { userid_list },
1897
1666
  });
1898
1667
  }
1899
-
1900
- async getDocAdvancedAccountList(params: { agent: ResolvedAgentAccount; cursor?: number; limit?: number }) {
1668
+ async getDocAdvancedAccountList(params) {
1901
1669
  const { agent, cursor, limit } = params;
1902
1670
  return this.postWecomDocApi({
1903
1671
  path: "/cgi-bin/meeting/vip/get_vip_user_list",
@@ -1906,14 +1674,12 @@ export class WecomDocClient {
1906
1674
  body: { cursor: cursor !== undefined ? String(cursor) : undefined, limit: limit ?? 100 },
1907
1675
  });
1908
1676
  }
1909
-
1910
1677
  // --- Material Management ---
1911
-
1912
- async uploadDocImage(params: { agent: ResolvedAgentAccount; docId: string; base64_content: string }) {
1678
+ async uploadDocImage(params) {
1913
1679
  const { agent, docId, base64_content } = params;
1914
1680
  const normalizedDocId = readString(docId);
1915
- if (!normalizedDocId) throw new Error("docId required");
1916
-
1681
+ if (!normalizedDocId)
1682
+ throw new Error("docId required");
1917
1683
  const json = await this.postWecomDocApi({
1918
1684
  path: "/cgi-bin/wedoc/image_upload",
1919
1685
  actionLabel: "upload_doc_image",
@@ -1923,7 +1689,6 @@ export class WecomDocClient {
1923
1689
  base64_content: base64_content
1924
1690
  }
1925
1691
  });
1926
-
1927
1692
  return {
1928
1693
  raw: json,
1929
1694
  url: readString(json.url),