weaver-work-cli 0.1.7 → 0.1.20

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 (264) hide show
  1. package/README.md +11 -11
  2. package/dist/cmd/action.js +76 -0
  3. package/dist/cmd/index.js +2 -0
  4. package/dist/cmd/refs/index.js +103 -0
  5. package/dist/cmd/register.js +31 -0
  6. package/dist/cmd/root.js +1 -0
  7. package/dist/cmd/setup/index.js +25 -5
  8. package/dist/cmd/skills/index.js +37 -5
  9. package/dist/core/connector.js +54 -0
  10. package/dist/core/context.js +2 -1
  11. package/dist/core/input.js +48 -0
  12. package/dist/core/output.js +298 -3
  13. package/dist/core/refs.js +432 -0
  14. package/dist/core/state.js +26 -0
  15. package/dist/internal/e10/auth/commands.js +2 -2
  16. package/dist/internal/e10/auth/session.js +14 -0
  17. package/dist/internal/e10/auth/xiaoe.js +116 -51
  18. package/dist/internal/e10/request-runtime.js +5 -0
  19. package/dist/internal/skills/install.js +148 -28
  20. package/dist/internal/skills/reader.js +12 -9
  21. package/dist/shortcuts/blog/continuation.js +74 -0
  22. package/dist/shortcuts/blog/errors.js +60 -0
  23. package/dist/shortcuts/blog/host.js +175 -0
  24. package/dist/shortcuts/blog/index.js +324 -0
  25. package/dist/shortcuts/blog/manifest.js +415 -0
  26. package/dist/shortcuts/blog/operations/comment.js +125 -0
  27. package/dist/shortcuts/blog/operations/comments.js +31 -0
  28. package/dist/shortcuts/blog/operations/edit.js +131 -0
  29. package/dist/shortcuts/blog/operations/export.js +46 -0
  30. package/dist/shortcuts/blog/operations/like.js +80 -0
  31. package/dist/shortcuts/blog/operations/publish.js +157 -0
  32. package/dist/shortcuts/blog/operations/query-user.js +58 -0
  33. package/dist/shortcuts/blog/operations/query.js +96 -0
  34. package/dist/shortcuts/blog/operations/registry.js +54 -0
  35. package/dist/shortcuts/blog/operations/remind.js +176 -0
  36. package/dist/shortcuts/blog/operations/shared.js +330 -0
  37. package/dist/shortcuts/blog/operations/types.js +16 -0
  38. package/dist/shortcuts/blog/operations.js +9 -0
  39. package/dist/shortcuts/caizhilian/continuation.js +70 -0
  40. package/dist/shortcuts/caizhilian/errors.js +53 -0
  41. package/dist/shortcuts/caizhilian/host.js +190 -0
  42. package/dist/shortcuts/caizhilian/index.js +186 -0
  43. package/dist/shortcuts/caizhilian/manifest.js +42 -0
  44. package/dist/shortcuts/caizhilian/operations/api.js +801 -0
  45. package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
  46. package/dist/shortcuts/caizhilian/operations/files.js +306 -0
  47. package/dist/shortcuts/caizhilian/operations/form.js +170 -0
  48. package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
  49. package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
  50. package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +346 -0
  51. package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
  52. package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
  53. package/dist/shortcuts/caizhilian/operations/handlers/folder.js +969 -0
  54. package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
  55. package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
  56. package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
  57. package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
  58. package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
  59. package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
  60. package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
  61. package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
  62. package/dist/shortcuts/caizhilian/operations/specs.js +623 -0
  63. package/dist/shortcuts/caizhilian/operations/types.js +57 -0
  64. package/dist/shortcuts/caizhilian/operations.js +21 -0
  65. package/dist/shortcuts/contract/continuation.js +70 -0
  66. package/dist/shortcuts/contract/errors.js +62 -0
  67. package/dist/shortcuts/contract/host.js +205 -0
  68. package/dist/shortcuts/contract/index.js +318 -0
  69. package/dist/shortcuts/contract/manifest.js +596 -0
  70. package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
  71. package/dist/shortcuts/contract/operations/form-info.js +371 -0
  72. package/dist/shortcuts/contract/operations/read.js +118 -0
  73. package/dist/shortcuts/contract/operations/registry.js +43 -0
  74. package/dist/shortcuts/contract/operations/request.js +300 -0
  75. package/dist/shortcuts/contract/operations/shared.js +133 -0
  76. package/dist/shortcuts/contract/operations/subject.js +157 -0
  77. package/dist/shortcuts/contract/operations/template.js +81 -0
  78. package/dist/shortcuts/contract/operations/types.js +7 -0
  79. package/dist/shortcuts/contract/operations/workflow.js +52 -0
  80. package/dist/shortcuts/contract/operations/write.js +131 -0
  81. package/dist/shortcuts/contract/operations.js +8 -0
  82. package/dist/shortcuts/hrm/index.js +83 -2
  83. package/dist/shortcuts/hrm/operations/employee.js +160 -30
  84. package/dist/shortcuts/hrm/operations/org.js +17 -3
  85. package/dist/shortcuts/hrm/operations/position.js +18 -2
  86. package/dist/shortcuts/index.js +5 -1
  87. package/dist/shortcuts/invoice/index.js +74 -36
  88. package/dist/shortcuts/invoice/manifest.js +674 -4
  89. package/dist/shortcuts/invoice/markdown.js +172 -0
  90. package/dist/shortcuts/invoice/operations/add.js +3 -3
  91. package/dist/shortcuts/invoice/operations/delete.js +2 -2
  92. package/dist/shortcuts/invoice/operations/get.js +10 -2
  93. package/dist/shortcuts/invoice/operations/import.js +2 -2
  94. package/dist/shortcuts/invoice/operations/registry.js +8 -1
  95. package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
  96. package/dist/shortcuts/invoice/operations/reim.js +85 -2
  97. package/dist/shortcuts/invoice/operations/shared.js +75 -8
  98. package/dist/shortcuts/invoice/operations/update.js +2 -2
  99. package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
  100. package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
  101. package/dist/shortcuts/invoice/projection.js +328 -0
  102. package/dist/shortcuts/meeting/host.js +18 -1
  103. package/dist/shortcuts/meeting/manifest.js +30 -18
  104. package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
  105. package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
  106. package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
  107. package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
  108. package/dist/shortcuts/meeting/operations/env.js +8 -1
  109. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  110. package/dist/shortcuts/meeting/operations/signs.js +216 -2
  111. package/dist/shortcuts/workflow/endpoints.js +2 -1
  112. package/dist/shortcuts/workflow/errors.js +8 -2
  113. package/dist/shortcuts/workflow/host.js +6 -3
  114. package/dist/shortcuts/workflow/operations/form.js +931 -533
  115. package/dist/shortcuts/workflow/operations/read.js +462 -81
  116. package/dist/shortcuts/workflow/operations/shared.js +44 -0
  117. package/dist/shortcuts/workflow/operations/write.js +760 -56
  118. package/dist/shortcuts/workflow/operations.js +36 -10
  119. package/docs/_catalog.md +6 -0
  120. package/docs/agent-invoice.md +5 -1
  121. package/docs/agent-io-channel-design.md +468 -0
  122. package/docs/agent-skill-install.md +9 -4
  123. package/docs/blog.md +123 -0
  124. package/docs/caizhilian.md +304 -0
  125. package/docs/contract.md +91 -0
  126. package/docs/e10-auth.md +2 -2
  127. package/docs/hrm.md +19 -9
  128. package/docs/invoice.md +5 -1
  129. package/docs/meeting.md +1 -1
  130. package/docs/operation-manual.md +15 -3
  131. package/docs/skill-review-2026-09-19.md +2 -2
  132. package/package.json +2 -2
  133. package/scripts/package-skill.mjs +33 -20
  134. package/skill-template/business-info.json +113 -14
  135. package/skill-template/domains/blog.md +24 -0
  136. package/skill-template/domains/caizhilian.md +22 -0
  137. package/skill-template/domains/jinchengda.md +23 -0
  138. package/skill-template/domains/meeting.md +1 -1
  139. package/skill-template/domains/workflow.md +1 -1
  140. package/skill-template/skill-template.md +1 -1
  141. package/skills/weaver-e10-blog/SKILL.md +111 -0
  142. package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
  143. package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
  144. package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
  145. package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
  146. package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
  147. package/skills/weaver-e10-blog/references/blog-export.md +42 -0
  148. package/skills/weaver-e10-blog/references/blog-like.md +50 -0
  149. package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
  150. package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
  151. package/skills/weaver-e10-blog/references/blog-query.md +49 -0
  152. package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
  153. package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
  154. package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
  155. package/skills/weaver-e10-caizhilian/SKILL.md +216 -0
  156. package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
  157. package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
  158. package/skills/weaver-e10-caizhilian/references/document.md +111 -0
  159. package/skills/weaver-e10-caizhilian/references/file-collection.md +136 -0
  160. package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
  161. package/skills/weaver-e10-caizhilian/references/files.md +120 -0
  162. package/skills/weaver-e10-caizhilian/references/folder.md +118 -0
  163. package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
  164. package/skills/weaver-e10-caizhilian/references/setting-attach.md +410 -0
  165. package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
  166. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
  167. package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
  168. package/skills/weaver-e10-caizhilian/references/source-manifest.json +5079 -0
  169. package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
  170. package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
  171. package/skills/weaver-e10-calendar/SKILL.md +4 -3
  172. package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
  173. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
  174. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
  175. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
  176. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
  177. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
  178. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
  179. package/skills/weaver-e10-esb/SKILL.md +2 -2
  180. package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
  181. package/skills/weaver-e10-hrm/SKILL.md +54 -6
  182. package/skills/weaver-e10-hrm/references/employee.md +89 -15
  183. package/skills/weaver-e10-hrm/references/org.md +7 -4
  184. package/skills/weaver-e10-hrm/references/position.md +14 -7
  185. package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
  186. package/skills/weaver-e10-jinchengda/SKILL.md +166 -0
  187. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
  188. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
  189. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
  190. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
  191. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
  192. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
  193. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
  194. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
  195. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
  196. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
  197. package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
  198. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
  199. package/skills/weaver-e10-jucailin/SKILL.md +4 -3
  200. package/skills/weaver-e10-mail/SKILL.md +4 -11
  201. package/skills/weaver-e10-meeting/SKILL.md +66 -32
  202. package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
  203. package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
  204. package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
  205. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
  206. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
  207. package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
  208. package/skills/weaver-e10-okr/SKILL.md +6 -11
  209. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  210. package/skills/weaver-e10-plan/SKILL.md +3 -3
  211. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
  212. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
  213. package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
  214. package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
  215. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
  216. package/skills/weaver-e10-shared/SKILL.md +36 -25
  217. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +10 -31
  218. package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
  219. package/skills/weaver-e10-shijingran/SKILL.md +2 -1
  220. package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
  221. package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
  222. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
  223. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
  224. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
  225. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
  226. package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
  227. package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
  228. package/skills/weaver-e10-workflow/SKILL.md +32 -37
  229. package/skills/weaver-e10-workflow/references/source-manifest.json +40 -77
  230. package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
  231. package/skills/weaver-e10-workflow/references/workflow-create.md +79 -109
  232. package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
  233. package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
  234. package/skills/weaver-e10-workflow/references/workflow-query.md +37 -20
  235. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  236. package/skills/weaver-e10-workflow/references/workflow-view.md +4 -4
  237. package/skills/weaver-e10-yepiaotong/SKILL.md +75 -39
  238. package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
  239. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
  240. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +5 -5
  241. package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
  242. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  243. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  244. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  245. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  246. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  247. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +90 -10
  248. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  249. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  250. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  251. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  252. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  253. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  254. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  255. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  256. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  257. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  258. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  259. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  260. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  261. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  262. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  263. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  264. package/scripts/sync-connector-skills.mjs +0 -172
@@ -0,0 +1,570 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { caizhilianValidationError } from '../../errors.js';
3
+ import { updateDocBasicField } from '../api.js';
4
+ import { issueWriteLease } from '../confirm.js';
5
+ import { IMAGE_ALLOWED_EXT, assertAllowedExt, assertFileUnchanged, downloadAttachment, readFileMetadata, resolveSavePathArg, uploadFileToServer, } from '../files.js';
6
+ import { fingerprint, isPlainObject, pyBool, timestampToDateStr, validateRequiredParams } from '../shared.js';
7
+ import { caizhilianPrepared, caizhilianSuccess } from '../types.js';
8
+ /**
9
+ * 文件域操作(1:1 对应源 scripts/document_plugins.py 的附件/封面部分
10
+ * + scripts/doc_compare_plugins.py 的文件对象查询/上传/比对提交部分):
11
+ * UploadAttachmentToDocPlugin、ReplaceDocumentAttachmentPlugin、
12
+ * DownloadDocAttachmentPlugin、DownloadDocAttachmentToPdfPlugin、
13
+ * SetDocCoverPlugin、RemoveDocCoverPlugin、UploadCoverImagePlugin、
14
+ * DocCompareQueryFileObjectsPlugin、DocCompareUploadFilePlugin、DocCompareUploadComparedPlugin。
15
+ *
16
+ * 写操作按 weaver-work-cli 规范拆成 prepare(只读本地文件元数据 + 组装上传表单 + 签发票据)
17
+ * 与 apply(真正产生副作用:上传 / 更新文档字段 / 提交比对),
18
+ * 上传表单字段与源 upload_file_to_server 的 extra_body 逐字段一致。
19
+ */
20
+ // ── 源常量 ────────────────────────────────────────────────
21
+ /** 源 upload_file_to_server 的默认 upload_path。 */
22
+ const MODULE_UPLOAD_PATH = '/api/file/module/upload';
23
+ /** 源 ReplaceDocumentAttachmentPlugin 的 upload_path。 */
24
+ const REUPLOAD_PATH = '/api/file/reUpload';
25
+ /** 源附件上传固定字段 customParam。 */
26
+ const FROM_PC_DOC_PARAM = '{"fromPCDoc":1}';
27
+ /** 源 SetDocCoverPlugin / UploadCoverImagePlugin 的 extra_body。 */
28
+ const COVER_UPLOAD_EXTRA_BODY = { createDoc: 'false' };
29
+ /** 源 DocCompareUploadFilePlugin 的 extra_body。 */
30
+ const COMPARE_UPLOAD_EXTRA_BODY = { module: 'docCompared' };
31
+ const QUERY_FILE_OBJECTS_PATH = '/api/file/queryFileObjects';
32
+ const COMPARE_SUBMIT_PATH = '/api/docCompare/uploadCompared';
33
+ const TO_PDF_CHECK_PATH = '/api/file/download/toPdf';
34
+ const DOC_UPDATE_PATH = '/api/doc/document/update';
35
+ /** 源 doc_compare_plugins._COMPARE_OPTIONS:13 个比对选项全部固定为 0(键序与源一致)。 */
36
+ const COMPARE_OPTIONS = {
37
+ ignoreImages: 0,
38
+ ignorePageHeader: 0,
39
+ ignoreSpace: 0,
40
+ ignorePunctuation: 0,
41
+ conversionAngle: 0,
42
+ conversionStyle: 0,
43
+ conversionSpecialSymbols: 0,
44
+ ignoreWatermark: 0,
45
+ ignoreSeals: 0,
46
+ parseTable: 0,
47
+ ignoreHandwriting: 0,
48
+ toLowerCase: 0,
49
+ ignoreAddendum: 0,
50
+ };
51
+ const ATTACHMENT_UPLOAD_APPLY = 'caizhilian.attachment.upload.apply';
52
+ const ATTACHMENT_REPLACE_APPLY = 'caizhilian.attachment.replace.apply';
53
+ const COVER_UPLOAD_APPLY = 'caizhilian.cover.upload.apply';
54
+ const COVER_SET_APPLY = 'caizhilian.cover.set.apply';
55
+ const COVER_REMOVE_APPLY = 'caizhilian.cover.remove.apply';
56
+ const COMPARE_UPLOAD_APPLY = 'caizhilian.compare.upload-file.apply';
57
+ const COMPARE_SUBMIT_APPLY = 'caizhilian.compare.submit.apply';
58
+ // ── Python 语义小工具(源脚本直译)────────────────────────
59
+ /** 源 `str(args.get(key) or "")`:Python 假值 → 空串,其余 str() 原样(不 strip)。 */
60
+ function pyArgOrEmpty(args, key) {
61
+ const value = args[key];
62
+ return pyBool(value) ? String(value) : '';
63
+ }
64
+ /** 源 `str(args.get(key) or "").strip()`。 */
65
+ function pyArgText(args, key) {
66
+ return pyArgOrEmpty(args, key).trim();
67
+ }
68
+ /**
69
+ * 源 `str(args.get(key, "")).strip()`(下载/比对类插件写法):
70
+ * 键缺失按空串处理;CLI schema 已禁止 null,故不复刻 Python 的 str(None)='None'。
71
+ */
72
+ function pyArgStripped(args, key) {
73
+ const value = args[key];
74
+ return value === undefined || value === null ? '' : String(value).trim();
75
+ }
76
+ /** 源 get_nested_value(data, "a.b"):路径不存在时返回 undefined。 */
77
+ function nestedValue(response, ...keys) {
78
+ let current = response;
79
+ for (const key of keys) {
80
+ if (!current || typeof current !== 'object')
81
+ return undefined;
82
+ current = current[key];
83
+ }
84
+ return current;
85
+ }
86
+ /** 本地文件预览(prepare 阶段只读,不上传)。 */
87
+ function filePreview(meta) {
88
+ return { file_name: meta.name, file_size: meta.size, file_md5: meta.md5, ext_name: meta.extName };
89
+ }
90
+ /** apply 前校验上传表单与 prepare 快照一致(CLI 侧防漂移,源无此保护)。 */
91
+ function assertBodyUnchanged(lease, body, action) {
92
+ const expected = typeof lease.body_fingerprint === 'string' ? lease.body_fingerprint : '';
93
+ if (expected && expected !== fingerprint(body)) {
94
+ throw caizhilianValidationError('plan_changed', `${action} 的请求体与 prepare 时不一致,请重新执行 prepare`);
95
+ }
96
+ }
97
+ /** prepare 阶段的上传预览(真正上传延后到 apply)。 */
98
+ function uploadPreview(extraBody, options = {}) {
99
+ return {
100
+ path: options.uploadPath ?? MODULE_UPLOAD_PATH,
101
+ extra_body: extraBody,
102
+ ...(options.allowedExt ? { allowed_ext: [...options.allowedExt].sort() } : {}),
103
+ ...(options.requiredPermission ? { required_permission: options.requiredPermission } : {}),
104
+ ...(options.permissionDeniedMessage ? { permission_denied_message: options.permissionDeniedMessage } : {}),
105
+ ...(options.extraResultFields ? { extra_result_fields: options.extraResultFields } : {}),
106
+ deferred_to_apply: true,
107
+ };
108
+ }
109
+ // ── caizhilian.attachment.download / download-as-pdf ──────
110
+ const DOWNLOAD_ACTION = 'caizhilian.attachment.download';
111
+ const DOWNLOAD_PDF_ACTION = 'caizhilian.attachment.download-as-pdf';
112
+ /** 源 DownloadDocAttachmentPlugin / DownloadDocAttachmentToPdfPlugin 的公共入参解析。 */
113
+ function resolveDownloadArgs(args) {
114
+ const attachmentId = pyArgStripped(args, 'attachment_id');
115
+ const docId = pyArgStripped(args, 'doc_id');
116
+ validateRequiredParams(args, ['attachment_id', 'doc_id']);
117
+ return { attachmentId, docId };
118
+ }
119
+ /** 源 DownloadDocAttachmentPlugin.execute。 */
120
+ async function downloadAttachmentHandler(args, env) {
121
+ const { attachmentId, docId } = resolveDownloadArgs(args);
122
+ const savePath = resolveSavePathArg(args.save_path);
123
+ const result = await downloadAttachment(env.host, {
124
+ attachmentId,
125
+ docId,
126
+ savePath,
127
+ operation: DOWNLOAD_ACTION,
128
+ });
129
+ return caizhilianSuccess(DOWNLOAD_ACTION, result, { doc_id: docId, attachment_id: attachmentId });
130
+ }
131
+ /** 源 `to_pdf is True or to_pdf in ("1", "true", 1, True)`。 */
132
+ function isPdfCapable(value) {
133
+ return value === true || value === 1 || value === '1' || value === 'true';
134
+ }
135
+ /** 源 DownloadDocAttachmentToPdfPlugin.execute:先 toPdf 预检,再带 downloadToPdf=true 下载。 */
136
+ async function downloadAsPdfHandler(args, env) {
137
+ const { attachmentId, docId } = resolveDownloadArgs(args);
138
+ const savePath = resolveSavePathArg(args.save_path);
139
+ const encodedId = attachmentId.includes('%') ? attachmentId : encodeURIComponent(attachmentId);
140
+ const check = await env.host.request({
141
+ method: 'GET',
142
+ path: `${TO_PDF_CHECK_PATH}?fileId=${encodedId}`,
143
+ operation: DOWNLOAD_PDF_ACTION,
144
+ });
145
+ if (!isPdfCapable(nestedValue(check, 'data', 'toPdf'))) {
146
+ throw caizhilianValidationError('pdf_not_supported', '该附件不支持转为 PDF');
147
+ }
148
+ const result = await downloadAttachment(env.host, {
149
+ attachmentId,
150
+ docId,
151
+ savePath,
152
+ extraQuery: { downloadToPdf: 'true' },
153
+ operation: DOWNLOAD_PDF_ACTION,
154
+ });
155
+ return caizhilianSuccess(DOWNLOAD_PDF_ACTION, result, { doc_id: docId, attachment_id: attachmentId });
156
+ }
157
+ /** 源 UploadAttachmentToDocPlugin.execute 的入参解析 + extra_body 组装。 */
158
+ async function planAttachmentUpload(args) {
159
+ validateRequiredParams(args, ['doc_id', 'file_path']);
160
+ const filePath = pyArgText(args, 'file_path');
161
+ const docId = pyArgText(args, 'doc_id');
162
+ /** 源 folder_id 不做 strip。 */
163
+ const folderId = pyArgOrEmpty(args, 'folder_id');
164
+ const meta = await readFileMetadata(filePath);
165
+ return {
166
+ docId,
167
+ folderId,
168
+ meta,
169
+ extraBody: {
170
+ docId,
171
+ customParam: FROM_PC_DOC_PARAM,
172
+ authModule: 'document',
173
+ refId: docId,
174
+ folderId,
175
+ },
176
+ };
177
+ }
178
+ const attachmentUploadHandler = {
179
+ targetKeys: ['doc_id', 'file_path'],
180
+ async prepare(args, env) {
181
+ const action = 'caizhilian.attachment.upload';
182
+ const plan = await planAttachmentUpload(args);
183
+ const lease = await issueWriteLease(env, ATTACHMENT_UPLOAD_APPLY, args, {
184
+ doc_id: plan.docId,
185
+ folder_id: plan.folderId,
186
+ file_path: plan.meta.path,
187
+ file_md5: plan.meta.md5,
188
+ file_size: plan.meta.size,
189
+ body_fingerprint: fingerprint(plan.extraBody),
190
+ });
191
+ return caizhilianPrepared(action, {
192
+ doc_id: plan.docId,
193
+ folder_id: plan.folderId,
194
+ ...filePreview(plan.meta),
195
+ upload: uploadPreview(plan.extraBody, {
196
+ requiredPermission: 'upload',
197
+ permissionDeniedMessage: '无上传权限:当前用户无权向该文档上传附件',
198
+ extraResultFields: ['uploadTime'],
199
+ }),
200
+ }, lease);
201
+ },
202
+ async apply(args, env, lease) {
203
+ const action = ATTACHMENT_UPLOAD_APPLY;
204
+ const plan = await planAttachmentUpload(args);
205
+ assertFileUnchanged(plan.meta, lease, 'file_path');
206
+ assertBodyUnchanged(lease, plan.extraBody, action);
207
+ const uploaded = await uploadFileToServer(env.host, plan.meta.path, {
208
+ extraBody: plan.extraBody,
209
+ requiredPermission: 'upload',
210
+ permissionDeniedMessage: '无上传权限:当前用户无权向该文档上传附件',
211
+ extraResultFields: ['uploadTime'],
212
+ operation: action,
213
+ preloadedMeta: plan.meta,
214
+ });
215
+ // 源 data["upload_time"] = data.pop("uploadTime", 0):改名并保持键序在最后。
216
+ const { uploadTime, ...rest } = uploaded;
217
+ return caizhilianSuccess(action, { ...rest, upload_time: uploadTime ?? 0 }, { doc_id: plan.docId });
218
+ },
219
+ };
220
+ /** 源 ReplaceDocumentAttachmentPlugin.execute:三个 ID 逐个校验(源不校验 file_path)。 */
221
+ async function planAttachmentReplace(args) {
222
+ const filePath = pyArgOrEmpty(args, 'file_path');
223
+ const docId = pyArgOrEmpty(args, 'doc_id');
224
+ const attachmentId = pyArgOrEmpty(args, 'attachment_id');
225
+ const folderId = pyArgOrEmpty(args, 'folder_id');
226
+ if (!docId)
227
+ throw caizhilianValidationError('field_required', '缺少必填参数: doc_id');
228
+ if (!attachmentId)
229
+ throw caizhilianValidationError('field_required', '缺少必填参数: attachment_id');
230
+ if (!folderId)
231
+ throw caizhilianValidationError('field_required', '缺少必填参数: folder_id');
232
+ const meta = await readFileMetadata(filePath);
233
+ return {
234
+ docId,
235
+ attachmentId,
236
+ folderId,
237
+ meta,
238
+ extraBody: {
239
+ docId,
240
+ editLinkType: 'editPage',
241
+ customParam: FROM_PC_DOC_PARAM,
242
+ authModule: 'document',
243
+ refId: docId,
244
+ employeePartTimeId: '',
245
+ folderId,
246
+ folderIdForStorageRule: 'true',
247
+ oldFileId: attachmentId,
248
+ },
249
+ };
250
+ }
251
+ const attachmentReplaceHandler = {
252
+ targetKeys: ['doc_id', 'attachment_id', 'file_path'],
253
+ async prepare(args, env) {
254
+ const action = 'caizhilian.attachment.replace';
255
+ const plan = await planAttachmentReplace(args);
256
+ const lease = await issueWriteLease(env, ATTACHMENT_REPLACE_APPLY, args, {
257
+ doc_id: plan.docId,
258
+ attachment_id: plan.attachmentId,
259
+ folder_id: plan.folderId,
260
+ file_path: plan.meta.path,
261
+ file_md5: plan.meta.md5,
262
+ file_size: plan.meta.size,
263
+ body_fingerprint: fingerprint(plan.extraBody),
264
+ });
265
+ return caizhilianPrepared(action, {
266
+ doc_id: plan.docId,
267
+ attachment_id: plan.attachmentId,
268
+ folder_id: plan.folderId,
269
+ ...filePreview(plan.meta),
270
+ upload: uploadPreview(plan.extraBody, {
271
+ uploadPath: REUPLOAD_PATH,
272
+ requiredPermission: 'reupload',
273
+ permissionDeniedMessage: '无替换附件上传权限',
274
+ extraResultFields: ['version'],
275
+ }),
276
+ }, lease);
277
+ },
278
+ async apply(args, env, lease) {
279
+ const action = ATTACHMENT_REPLACE_APPLY;
280
+ const plan = await planAttachmentReplace(args);
281
+ assertFileUnchanged(plan.meta, lease, 'file_path');
282
+ assertBodyUnchanged(lease, plan.extraBody, action);
283
+ const uploaded = await uploadFileToServer(env.host, plan.meta.path, {
284
+ extraBody: plan.extraBody,
285
+ uploadPath: REUPLOAD_PATH,
286
+ requiredPermission: 'reupload',
287
+ permissionDeniedMessage: '无替换附件上传权限',
288
+ extraResultFields: ['version'],
289
+ operation: action,
290
+ preloadedMeta: plan.meta,
291
+ });
292
+ return caizhilianSuccess(action, uploaded, { doc_id: plan.docId, attachment_id: plan.attachmentId });
293
+ },
294
+ };
295
+ // ── caizhilian.cover.upload / cover.set / cover.remove ────
296
+ /** 源 UploadCoverImagePlugin / SetDocCoverPlugin 的图片读取 + 扩展名校验。 */
297
+ async function planCoverImage(args) {
298
+ const meta = await readFileMetadata(args.file_path);
299
+ assertAllowedExt(meta, IMAGE_ALLOWED_EXT);
300
+ return meta;
301
+ }
302
+ const coverUploadHandler = {
303
+ targetKeys: ['file_path'],
304
+ async prepare(args, env) {
305
+ const action = 'caizhilian.cover.upload';
306
+ const meta = await planCoverImage(args);
307
+ const lease = await issueWriteLease(env, COVER_UPLOAD_APPLY, args, {
308
+ file_path: meta.path,
309
+ file_md5: meta.md5,
310
+ file_size: meta.size,
311
+ body_fingerprint: fingerprint(COVER_UPLOAD_EXTRA_BODY),
312
+ });
313
+ return caizhilianPrepared(action, {
314
+ ...filePreview(meta),
315
+ upload: uploadPreview(COVER_UPLOAD_EXTRA_BODY, { allowedExt: IMAGE_ALLOWED_EXT }),
316
+ }, lease);
317
+ },
318
+ async apply(args, env, lease) {
319
+ const action = COVER_UPLOAD_APPLY;
320
+ const meta = await planCoverImage(args);
321
+ assertFileUnchanged(meta, lease, 'file_path');
322
+ assertBodyUnchanged(lease, COVER_UPLOAD_EXTRA_BODY, action);
323
+ const uploaded = await uploadFileToServer(env.host, meta.path, {
324
+ allowedExt: IMAGE_ALLOWED_EXT,
325
+ extraBody: COVER_UPLOAD_EXTRA_BODY,
326
+ operation: action,
327
+ preloadedMeta: meta,
328
+ });
329
+ return caizhilianSuccess(action, uploaded);
330
+ },
331
+ };
332
+ /** 源 SetDocCoverPlugin.execute:doc_id 空值文案是「doc_id 不能为空」。 */
333
+ async function planCoverSet(args) {
334
+ const docId = pyArgText(args, 'doc_id');
335
+ if (!docId)
336
+ throw caizhilianValidationError('field_required', 'doc_id 不能为空');
337
+ const meta = await planCoverImage(args);
338
+ return { docId, meta };
339
+ }
340
+ /** 源 _update_doc_basic_field 的请求体(封面字段固定为 coverPictureId)。 */
341
+ function coverUpdateBody(docId, coverPictureId) {
342
+ return {
343
+ id: docId,
344
+ filed: 'coverPictureId',
345
+ document: { coverPictureId, shareOut: false },
346
+ };
347
+ }
348
+ const coverSetHandler = {
349
+ targetKeys: ['doc_id', 'file_path'],
350
+ async prepare(args, env) {
351
+ const action = 'caizhilian.cover.set';
352
+ const plan = await planCoverSet(args);
353
+ const lease = await issueWriteLease(env, COVER_SET_APPLY, args, {
354
+ doc_id: plan.docId,
355
+ file_path: plan.meta.path,
356
+ file_md5: plan.meta.md5,
357
+ file_size: plan.meta.size,
358
+ body_fingerprint: fingerprint(COVER_UPLOAD_EXTRA_BODY),
359
+ });
360
+ return caizhilianPrepared(action, {
361
+ doc_id: plan.docId,
362
+ ...filePreview(plan.meta),
363
+ upload: uploadPreview(COVER_UPLOAD_EXTRA_BODY, { allowedExt: IMAGE_ALLOWED_EXT }),
364
+ update: {
365
+ method: 'POST',
366
+ path: `${DOC_UPDATE_PATH}/${encodeURIComponent(plan.docId)}`,
367
+ filed: 'coverPictureId',
368
+ doc_field: 'coverPictureId',
369
+ value_source: 'upload.file_id',
370
+ deferred_to_apply: true,
371
+ },
372
+ }, lease);
373
+ },
374
+ async apply(args, env, lease) {
375
+ const action = COVER_SET_APPLY;
376
+ const plan = await planCoverSet(args);
377
+ assertFileUnchanged(plan.meta, lease, 'file_path');
378
+ assertBodyUnchanged(lease, COVER_UPLOAD_EXTRA_BODY, action);
379
+ const uploaded = await uploadFileToServer(env.host, plan.meta.path, {
380
+ allowedExt: IMAGE_ALLOWED_EXT,
381
+ extraBody: COVER_UPLOAD_EXTRA_BODY,
382
+ operation: action,
383
+ preloadedMeta: plan.meta,
384
+ });
385
+ await updateDocBasicField(env, {
386
+ docId: plan.docId,
387
+ filed: 'coverPictureId',
388
+ argKey: 'cover_picture_id',
389
+ docField: 'coverPictureId',
390
+ allowEmpty: true,
391
+ fixedValue: String(uploaded.file_id),
392
+ }, action);
393
+ return caizhilianSuccess(action, { doc_id: plan.docId, file_id: uploaded.file_id, name: uploaded.name }, { body: coverUpdateBody(plan.docId, String(uploaded.file_id)) });
394
+ },
395
+ };
396
+ /** 源 RemoveDocCoverPlugin.execute:只有 doc_id 一个入参,封面字段置空串。 */
397
+ const coverRemoveHandler = {
398
+ targetKeys: ['doc_id'],
399
+ async prepare(args, env) {
400
+ const action = 'caizhilian.cover.remove';
401
+ validateRequiredParams(args, ['doc_id']);
402
+ const docId = pyArgStripped(args, 'doc_id');
403
+ const lease = await issueWriteLease(env, COVER_REMOVE_APPLY, args, { doc_id: docId });
404
+ return caizhilianPrepared(action, {
405
+ doc_id: docId,
406
+ update: {
407
+ method: 'POST',
408
+ path: `${DOC_UPDATE_PATH}/${encodeURIComponent(docId)}`,
409
+ body: coverUpdateBody(docId, ''),
410
+ },
411
+ }, lease);
412
+ },
413
+ async apply(args, env, _lease) {
414
+ const action = COVER_REMOVE_APPLY;
415
+ validateRequiredParams(args, ['doc_id']);
416
+ const result = await updateDocBasicField(env, {
417
+ docId: args.doc_id,
418
+ filed: 'coverPictureId',
419
+ argKey: 'cover_picture_id',
420
+ docField: 'coverPictureId',
421
+ allowEmpty: true,
422
+ fixedValue: '',
423
+ }, action);
424
+ return caizhilianSuccess(action, result);
425
+ },
426
+ };
427
+ // ── caizhilian.compare.list-file-objects ──────────────────
428
+ const LIST_FILE_OBJECTS_ACTION = 'caizhilian.compare.list-file-objects';
429
+ /** 源 DocCompareQueryFileObjectsPlugin.execute。 */
430
+ async function listFileObjectsHandler(args, env) {
431
+ validateRequiredParams(args, ['doc_id']);
432
+ const docId = pyArgStripped(args, 'doc_id');
433
+ const response = await env.host.request({
434
+ method: 'POST',
435
+ path: QUERY_FILE_OBJECTS_PATH,
436
+ body: [{ docId: String(docId) }],
437
+ operation: LIST_FILE_OBJECTS_ACTION,
438
+ });
439
+ const rawList = nestedValue(response, 'data');
440
+ if (!Array.isArray(rawList)) {
441
+ throw caizhilianValidationError('response_shape', '响应结构异常: data 段应为文件对象列表');
442
+ }
443
+ const attachments = [];
444
+ for (const item of rawList) {
445
+ if (!isPlainObject(item))
446
+ continue;
447
+ attachments.push({
448
+ docId: item.docId ?? null,
449
+ file_id: item.attachmentId ?? null,
450
+ ext_name: item.type ?? null,
451
+ file_name: item.fileName ?? null,
452
+ size: item.size ?? null,
453
+ updateTime: timestampToDateStr(item.updateTime),
454
+ });
455
+ }
456
+ return caizhilianSuccess(LIST_FILE_OBJECTS_ACTION, { attachments }, { doc_id: docId });
457
+ }
458
+ // ── caizhilian.compare.upload-file ────────────────────────
459
+ /** 源 DocCompareUploadFilePlugin.execute 的入参解析。 */
460
+ async function planCompareUpload(args) {
461
+ validateRequiredParams(args, ['file_path']);
462
+ return readFileMetadata(pyArgStripped(args, 'file_path'));
463
+ }
464
+ const compareUploadFileHandler = {
465
+ targetKeys: ['file_path'],
466
+ async prepare(args, env) {
467
+ const action = 'caizhilian.compare.upload-file';
468
+ const meta = await planCompareUpload(args);
469
+ const lease = await issueWriteLease(env, COMPARE_UPLOAD_APPLY, args, {
470
+ file_path: meta.path,
471
+ file_md5: meta.md5,
472
+ file_size: meta.size,
473
+ body_fingerprint: fingerprint(COMPARE_UPLOAD_EXTRA_BODY),
474
+ });
475
+ return caizhilianPrepared(action, {
476
+ ...filePreview(meta),
477
+ upload: uploadPreview(COMPARE_UPLOAD_EXTRA_BODY, {
478
+ requiredPermission: 'upload',
479
+ permissionDeniedMessage: '无上传权限:当前用户无权向文档比对模块上传文件',
480
+ }),
481
+ }, lease);
482
+ },
483
+ async apply(args, env, lease) {
484
+ const action = COMPARE_UPLOAD_APPLY;
485
+ const meta = await planCompareUpload(args);
486
+ assertFileUnchanged(meta, lease, 'file_path');
487
+ assertBodyUnchanged(lease, COMPARE_UPLOAD_EXTRA_BODY, action);
488
+ const uploaded = await uploadFileToServer(env.host, meta.path, {
489
+ extraBody: COMPARE_UPLOAD_EXTRA_BODY,
490
+ requiredPermission: 'upload',
491
+ permissionDeniedMessage: '无上传权限:当前用户无权向文档比对模块上传文件',
492
+ operation: action,
493
+ preloadedMeta: meta,
494
+ });
495
+ return caizhilianSuccess(action, uploaded);
496
+ },
497
+ };
498
+ // ── caizhilian.compare.submit ─────────────────────────────
499
+ /** 源 doc_compare_plugins._generate_queueing_uuid。 */
500
+ function generateQueueingUuid() {
501
+ const raw = randomUUID().replace(/-/g, '').toUpperCase();
502
+ return `GUID_${raw.slice(0, 8)}_${raw.slice(8, 12)}_${raw.slice(12, 16)}_${raw.slice(16, 20)}_${raw.slice(20)}`;
503
+ }
504
+ /** 源 DocCompareUploadComparedPlugin 的请求体(键序与源一致)。 */
505
+ function buildCompareSubmitBody(leftFileId, rightFileId, queueingUuid) {
506
+ return {
507
+ leftEntryIds: String(leftFileId),
508
+ rightEntryIds: String(rightFileId),
509
+ queueingUUID: queueingUuid,
510
+ ...COMPARE_OPTIONS,
511
+ };
512
+ }
513
+ /** 源 DocCompareUploadComparedPlugin.execute 的入参解析。 */
514
+ function planCompareSubmit(args) {
515
+ validateRequiredParams(args, ['left_file_id', 'right_file_id']);
516
+ return {
517
+ leftFileId: pyArgStripped(args, 'left_file_id'),
518
+ rightFileId: pyArgStripped(args, 'right_file_id'),
519
+ };
520
+ }
521
+ const compareSubmitHandler = {
522
+ targetKeys: ['left_file_id', 'right_file_id'],
523
+ async prepare(args, env) {
524
+ const action = 'caizhilian.compare.submit';
525
+ const plan = planCompareSubmit(args);
526
+ const queueingUuid = generateQueueingUuid();
527
+ const body = buildCompareSubmitBody(plan.leftFileId, plan.rightFileId, queueingUuid);
528
+ const lease = await issueWriteLease(env, COMPARE_SUBMIT_APPLY, args, {
529
+ left_file_id: plan.leftFileId,
530
+ right_file_id: plan.rightFileId,
531
+ queueing_uuid: queueingUuid,
532
+ body_fingerprint: fingerprint(body),
533
+ });
534
+ return caizhilianPrepared(action, {
535
+ left_file_id: plan.leftFileId,
536
+ right_file_id: plan.rightFileId,
537
+ request: { method: 'POST', path: COMPARE_SUBMIT_PATH, body },
538
+ note: '比对结果链接为外部制品(compare_link),CLI 只返回链接,不代下载',
539
+ }, lease);
540
+ },
541
+ async apply(args, env, lease) {
542
+ const action = COMPARE_SUBMIT_APPLY;
543
+ const plan = planCompareSubmit(args);
544
+ const queueingUuid = typeof lease.queueing_uuid === 'string' && lease.queueing_uuid ? lease.queueing_uuid : generateQueueingUuid();
545
+ const body = buildCompareSubmitBody(plan.leftFileId, plan.rightFileId, queueingUuid);
546
+ assertBodyUnchanged(lease, body, action);
547
+ const response = await env.host.request({
548
+ method: 'POST',
549
+ path: COMPARE_SUBMIT_PATH,
550
+ body,
551
+ operation: action,
552
+ });
553
+ const raw = nestedValue(response, 'data', 'compareLink');
554
+ const compareLink = pyBool(raw) ? raw : '';
555
+ return caizhilianSuccess(action, { compare_link: compareLink }, { left_file_id: plan.leftFileId, right_file_id: plan.rightFileId });
556
+ },
557
+ };
558
+ // ── 处理器汇总 ────────────────────────────────────────────
559
+ export const caizhilianFileopsHandlers = {
560
+ 'caizhilian.attachment.upload': attachmentUploadHandler,
561
+ 'caizhilian.attachment.replace': attachmentReplaceHandler,
562
+ 'caizhilian.attachment.download': { execute: downloadAttachmentHandler },
563
+ 'caizhilian.attachment.download-as-pdf': { execute: downloadAsPdfHandler },
564
+ 'caizhilian.cover.upload': coverUploadHandler,
565
+ 'caizhilian.cover.set': coverSetHandler,
566
+ 'caizhilian.cover.remove': coverRemoveHandler,
567
+ 'caizhilian.compare.list-file-objects': { execute: listFileObjectsHandler },
568
+ 'caizhilian.compare.upload-file': compareUploadFileHandler,
569
+ 'caizhilian.compare.submit': compareSubmitHandler,
570
+ };