weaver-work-cli 0.1.8 → 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 (263) 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 -589
  115. package/dist/shortcuts/workflow/operations/read.js +128 -205
  116. package/dist/shortcuts/workflow/operations/shared.js +21 -0
  117. package/dist/shortcuts/workflow/operations/write.js +750 -52
  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 +32 -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 +39 -75
  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 +38 -20
  235. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  236. package/skills/weaver-e10-workflow/references/workflow-view.md +3 -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-output-view.md +135 -0
  241. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  242. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  243. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  244. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  245. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  246. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +89 -9
  247. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  248. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  249. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  250. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  251. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  252. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  253. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  254. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  255. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  256. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  257. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  258. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  259. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  260. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  261. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  262. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  263. package/scripts/sync-connector-skills.mjs +0 -172
@@ -0,0 +1,57 @@
1
+ export function caizhilianSuccess(action, data, meta, warnings) {
2
+ const payload = { ok: true, action, data };
3
+ if (meta && Object.keys(meta).length > 0)
4
+ payload.meta = meta;
5
+ if (warnings && warnings.length > 0)
6
+ payload.warnings = warnings;
7
+ return payload;
8
+ }
9
+ /** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
10
+ export function schemaOf(properties, options = {}) {
11
+ return {
12
+ type: 'object',
13
+ additionalProperties: false,
14
+ properties,
15
+ ...(options.required?.length ? { required: options.required } : {}),
16
+ ...(options.description ? { description: options.description } : {}),
17
+ };
18
+ }
19
+ /** apply 入参 = prepare 入参 + confirm + continuation。 */
20
+ export function applySchemaOf(base, required = []) {
21
+ const properties = { ...(base.properties || {}) };
22
+ delete properties.confirm;
23
+ delete properties.continuation;
24
+ return {
25
+ type: 'object',
26
+ additionalProperties: false,
27
+ properties: {
28
+ ...properties,
29
+ confirm: { const: true, description: '必须为 true(CLI 用 --yes)' },
30
+ continuation: { type: 'string', minLength: 1, description: 'prepare 返回的确认票据' },
31
+ },
32
+ required: [...new Set([...required, 'confirm', 'continuation'])],
33
+ ...(base.description ? { description: base.description } : {}),
34
+ };
35
+ }
36
+ export const SCHEMA_DOC_ID = { type: 'string', pattern: '^\\d+$', description: '文档 ID' };
37
+ export const SCHEMA_FOLDER_ID = { type: 'string', pattern: '^\\d+$', description: '文件夹 ID' };
38
+ export const SCHEMA_PAGE_NO = { type: 'integer', minimum: 1, default: 1, description: '页码(1-indexed)' };
39
+ export const SCHEMA_PAGE_SIZE = { type: 'integer', minimum: 1, description: '每页条数' };
40
+ export const SCHEMA_FILE_PATH = { type: 'string', minLength: 1, description: '本地文件绝对路径' };
41
+ export function isWriteHandler(entry) {
42
+ return typeof entry.prepare === 'function';
43
+ }
44
+ /**
45
+ * prepare 阶段统一返回体:预览 + continuation + 有效期。
46
+ * apply 必须回传该 continuation 且 confirm=true。
47
+ */
48
+ export function caizhilianPrepared(action, preview, lease, warnings) {
49
+ // handler 传入的是 canonical 名(不含 .prepare),两种形态都要指向 <name>.apply。
50
+ const applyAction = action.endsWith('.prepare') ? `${action.slice(0, -'.prepare'.length)}.apply` : `${action}.apply`;
51
+ return caizhilianSuccess(action, {
52
+ ...preview,
53
+ continuation: lease.continuation,
54
+ expires_in_ms: lease.expiresInMs,
55
+ hint: `确认无误后调用 ${applyAction}(必须回传本次 continuation 且 confirm=true,CLI 加 --yes)`,
56
+ }, { status: 'PREPARED', requiresConfirmation: true, expires_in_ms: lease.expiresInMs }, warnings);
57
+ }
@@ -0,0 +1,21 @@
1
+ import { caizhilianManifest } from './manifest.js';
2
+ import { getCaizhilianOperation } from './operations/registry.js';
3
+ import { asObject, assertKnownKeys, assertNoForbiddenKeys, assertNoLossyIntegers, normalizeAliases, } from './operations/shared.js';
4
+ /** 顶层输入白名单直接取自 manifest:schema 即运行时约束,避免两处规则漂移。 */
5
+ const ALLOWED_KEYS = Object.fromEntries(caizhilianManifest.operations.map((operation) => [
6
+ operation.name,
7
+ Object.keys(operation.inputSchema.properties ?? {}),
8
+ ]));
9
+ export async function executeCaizhilianOperation(operation, rawInput, context) {
10
+ const definition = getCaizhilianOperation(operation);
11
+ const input = asObject(rawInput);
12
+ // 归一化必须早于校验与 handler:旧字段映射到 canonical,冲突直接报错。
13
+ const normalized = definition.deprecatedInputAliases
14
+ ? normalizeAliases(input, definition.deprecatedInputAliases)
15
+ : input;
16
+ assertNoForbiddenKeys(normalized);
17
+ assertKnownKeys(normalized, ALLOWED_KEYS[operation] ?? [], operation);
18
+ // 大整数(ID)用 JSON 数字传入会在解析阶段丢精度,必须在进入 handler 前拦下。
19
+ assertNoLossyIntegers(normalized);
20
+ return definition.handler(normalized, context);
21
+ }
@@ -0,0 +1,70 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { homedir } from 'node:os';
5
+ import { resolve } from 'node:path';
6
+ import { contractValidationError } from './errors.js';
7
+ function stateRoot(ctx) {
8
+ const configured = ctx?.config?.path();
9
+ if (configured)
10
+ return resolve(configured, '..');
11
+ return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
12
+ }
13
+ async function loadKey(ctx) {
14
+ const keysDir = resolve(stateRoot(ctx), 'keys');
15
+ const filename = resolve(keysDir, 'contract-continuation.key');
16
+ await mkdir(keysDir, { recursive: true, mode: 0o700 });
17
+ if (existsSync(filename))
18
+ return readFile(filename);
19
+ const key = randomBytes(32);
20
+ try {
21
+ await writeFile(filename, key, { flag: 'wx', mode: 0o600 });
22
+ if (process.platform !== 'win32')
23
+ await chmod(filename, 0o600).catch(() => undefined);
24
+ return key;
25
+ }
26
+ catch {
27
+ return readFile(filename);
28
+ }
29
+ }
30
+ function encode(value) {
31
+ return Buffer.from(JSON.stringify(value)).toString('base64url');
32
+ }
33
+ function signature(payload, key) {
34
+ return createHmac('sha256', key).update(payload).digest('base64url');
35
+ }
36
+ export function createContractContinuationSigner(ctx) {
37
+ return {
38
+ async issue(payload, ttlMs = 10 * 60_000) {
39
+ const issuedAt = Date.now();
40
+ const body = encode({ ...payload, issuedAt, expiresAt: issuedAt + ttlMs });
41
+ const sig = signature(body, await loadKey(ctx));
42
+ return `${body}.${sig}`;
43
+ },
44
+ async verify(token, expectedOperation) {
45
+ if (typeof token !== 'string' || !token.includes('.')) {
46
+ throw contractValidationError('continuation_invalid', 'continuation 无效');
47
+ }
48
+ const [body, provided] = token.split('.');
49
+ const expected = signature(body, await loadKey(ctx));
50
+ const valid = provided.length === expected.length
51
+ && timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
52
+ if (!valid)
53
+ throw contractValidationError('continuation_invalid', 'continuation 签名校验失败');
54
+ let value;
55
+ try {
56
+ value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
57
+ }
58
+ catch {
59
+ throw contractValidationError('continuation_invalid', 'continuation 内容无法解析');
60
+ }
61
+ if (value.operation !== expectedOperation) {
62
+ throw contractValidationError('continuation_mismatch', 'continuation 不属于当前操作');
63
+ }
64
+ if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
65
+ throw contractValidationError('continuation_expired', 'continuation 已过期');
66
+ }
67
+ return value;
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,62 @@
1
+ import { CliError, ExitCode } from '../../core/errors.js';
2
+ export class ContractCliError extends CliError {
3
+ type;
4
+ subtype;
5
+ code;
6
+ retryable;
7
+ stage;
8
+ details;
9
+ partialData;
10
+ constructor(type, subtype, message, options = {}) {
11
+ super(message, options.exitCode ?? ExitCode.InternalError);
12
+ this.name = 'ContractCliError';
13
+ this.type = type;
14
+ this.subtype = subtype;
15
+ this.code = options.code;
16
+ this.retryable = options.retryable ?? false;
17
+ this.stage = options.stage;
18
+ this.details = options.details;
19
+ this.partialData = options.partialData;
20
+ }
21
+ }
22
+ export function contractValidationError(subtype, message, details) {
23
+ return new ContractCliError('validation', subtype, message, {
24
+ details,
25
+ exitCode: ExitCode.UsageError,
26
+ });
27
+ }
28
+ export function contractAuthError(subtype, message, details) {
29
+ return new ContractCliError('authentication', subtype, message, {
30
+ details,
31
+ exitCode: ExitCode.AuthError,
32
+ });
33
+ }
34
+ export function contractNetworkError(subtype, message, options = {}) {
35
+ return new ContractCliError('network', subtype, message, {
36
+ ...options,
37
+ retryable: options.retryable ?? true,
38
+ exitCode: ExitCode.NetworkError,
39
+ });
40
+ }
41
+ export function contractConfirmationError(action, details) {
42
+ return new ContractCliError('confirmation', 'required', '该操作需要明确确认', {
43
+ details: { action, ...details },
44
+ exitCode: ExitCode.ConfirmationRequired,
45
+ });
46
+ }
47
+ export function contractPolicyError(subtype, message, details) {
48
+ return new ContractCliError('policy', subtype, message, {
49
+ details,
50
+ exitCode: ExitCode.PolicyError,
51
+ });
52
+ }
53
+ export function contractPartialError(message, partialData, details) {
54
+ return new ContractCliError('partial', 'write_uncertain', message, {
55
+ details,
56
+ partialData,
57
+ exitCode: ExitCode.Partial,
58
+ });
59
+ }
60
+ export function isContractTimeoutError(error) {
61
+ return error instanceof ContractCliError && error.type === 'network' && error.subtype === 'timeout';
62
+ }
@@ -0,0 +1,205 @@
1
+ import { requireE10Session } from '../../internal/e10/context.js';
2
+ import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
+ import { ContractCliError, contractAuthError, contractNetworkError, contractValidationError, } from './errors.js';
4
+ const DEFAULT_TIMEOUT_MS = 30_000;
5
+ function assertPath(requestPath) {
6
+ if (typeof requestPath !== 'string' || !requestPath.startsWith('/') || requestPath.startsWith('//')) {
7
+ throw contractValidationError('path_invalid', '请求路径必须是相对绝对路径');
8
+ }
9
+ if (/^https?:/i.test(requestPath) || /[\r\n]/.test(requestPath) || requestPath.includes('/../')) {
10
+ throw contractValidationError('path_invalid', '请求路径不允许跨域或路径逃逸');
11
+ }
12
+ if (!requestPath.startsWith('/api/')) {
13
+ throw contractValidationError('path_not_allowed', '今承达合同管理只能调用 /api/ 路径');
14
+ }
15
+ }
16
+ function withQuery(baseUrl, requestPath, query = {}) {
17
+ const url = new URL(`${baseUrl}${requestPath}`);
18
+ for (const [key, value] of Object.entries(query)) {
19
+ if (value === undefined || value === null || value === '')
20
+ continue;
21
+ if (Array.isArray(value)) {
22
+ for (const item of value)
23
+ url.searchParams.append(key, String(item));
24
+ }
25
+ else {
26
+ url.searchParams.set(key, String(value));
27
+ }
28
+ }
29
+ return url;
30
+ }
31
+ async function fetchWithTimeout(url, options, timeoutMs) {
32
+ const controller = new AbortController();
33
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
34
+ try {
35
+ return await fetch(url, { ...options, signal: controller.signal, redirect: 'manual' });
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error && error.name === 'AbortError') {
39
+ throw contractNetworkError('timeout', `请求超时(${timeoutMs}ms)`, { retryable: false });
40
+ }
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ throw contractNetworkError('transport', `请求失败:${message}`, { retryable: true });
43
+ }
44
+ finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ function textOf(value, keys) {
49
+ for (const key of keys) {
50
+ const candidate = value?.[key];
51
+ if (typeof candidate === 'string' && candidate.trim())
52
+ return candidate;
53
+ }
54
+ return undefined;
55
+ }
56
+ /**
57
+ * 源资料里各接口的成功码字段并不统一,适配层按路径分别判定,不凭 HTTP 200 判定业务成功。
58
+ * - /api/ebd/contract/**:顶层 code = 0;部分租户/版本(实测 10.0.9807.01)返回 code = 200,
59
+ * 因此 0 与 200 均视为成功,并辅以 status/fail 判定
60
+ * - /api/ebuilder/form/obj/getObjsByTag、/api/bs/ebuilder/app/tags/getAppIds、
61
+ * /api/ebuilder/common/ds/fields、/api/ebuilder/common/ds/options:顶层 code = 200 且 status 不为 false
62
+ * - /api/ebuilder/form/dataset/v2/**:message.code = "0"
63
+ * - /api/workflow/**:message.errcode = "0"(getFieldOption 上游文档示例为 200,同样视为成功)
64
+ * - /api/workflow/core/flowPage/**、/api/workflow/core/form/**、/api/ebuilder/flow/form/**
65
+ * (loadBaseParam / loadFrameParam / getFormLayout 三段式表单接口):这些响应**没有 message 节点**,
66
+ * 成功与否要看 `fail`/`status` 与显式 errcode;只有 errcode 出现且非 0/200 才算失败。
67
+ * 沿用 weaver-e10-workflow 的 extractApiBusinessError 语义。
68
+ */
69
+ export function contractBusinessStatus(path, value) {
70
+ const root = (value ?? {});
71
+ const messageNode = root.message ?? root.data?.message ?? {};
72
+ if (path.startsWith('/api/ebd/') || path === '/api/ebd/contract/clauseReference/selectData') {
73
+ const code = root.code ?? messageNode.errcode ?? messageNode.code;
74
+ const ok = (code === 0 || code === '0' || code === 200 || code === '200') && root.status !== false && root.fail !== true;
75
+ return { ok, code: code === undefined ? undefined : String(code), message: textOf(root, ['msg', 'message']) ?? textOf(messageNode, ['errmsg', 'msg']) };
76
+ }
77
+ if (path === '/api/ebuilder/form/obj/getObjsByTag'
78
+ || path === '/api/bs/ebuilder/app/tags/getAppIds'
79
+ || path === '/api/ebuilder/common/ds/fields'
80
+ || path === '/api/ebuilder/common/ds/options') {
81
+ const code = root.code;
82
+ const ok = (code === 200 || code === '200') && root.status !== false && root.fail !== true;
83
+ return { ok, code: code === undefined ? undefined : String(code), message: textOf(root, ['msg']) };
84
+ }
85
+ if (path.startsWith('/api/ebuilder/form/dataset/v2/')) {
86
+ const code = messageNode.code ?? root.code;
87
+ const ok = code === '0' || code === 0;
88
+ return { ok, code: code === undefined ? undefined : String(code), message: textOf(messageNode, ['msg']) ?? textOf(root, ['msg']) };
89
+ }
90
+ if (path.startsWith('/api/workflow/core/flowPage/')
91
+ || path.startsWith('/api/workflow/core/form/')
92
+ || path.startsWith('/api/ebuilder/flow/form/')) {
93
+ const errcode = String(messageNode.errcode ?? root.errcode ?? '').trim();
94
+ if (errcode && !['0', '200'].includes(errcode)) {
95
+ return { ok: false, code: errcode, message: textOf(messageNode, ['errmsg', 'msg']) ?? textOf(root, ['msg', 'message']) };
96
+ }
97
+ if (root.fail === true || root.status === false) {
98
+ return {
99
+ ok: false,
100
+ code: textOf(root, ['code']) ?? '',
101
+ message: textOf(root, ['msg', 'message', 'error', 'errorMsg']) ?? '泛微接口返回业务失败',
102
+ };
103
+ }
104
+ return { ok: true, code: errcode || undefined, message: textOf(messageNode, ['errmsg', 'msg']) };
105
+ }
106
+ const errcode = messageNode.errcode ?? root.errcode;
107
+ const ok = errcode === '0' || errcode === 0 || errcode === '200' || errcode === 200;
108
+ return { ok, code: errcode === undefined ? undefined : String(errcode), message: textOf(messageNode, ['errmsg', 'msg']) };
109
+ }
110
+ function inferProfile(ctx) {
111
+ const options = ctx.getGlobalOptions();
112
+ return options.profile || getActiveProfile();
113
+ }
114
+ function requestHeaders(session) {
115
+ return {
116
+ Accept: 'application/json, text/plain, */*',
117
+ Cookie: session.cookieHeader(),
118
+ eteamsid: session.eteamsId,
119
+ 'User-Agent': session.uaHeader(),
120
+ Origin: session.getBaseUrl(),
121
+ };
122
+ }
123
+ export function createContractHost(ctx, dependencies = {}) {
124
+ let e10Session = null;
125
+ let sessionContext = null;
126
+ const requireSession = dependencies.requireSession || requireE10Session;
127
+ async function ensureSession() {
128
+ if (!e10Session) {
129
+ e10Session = await requireSession(ctx);
130
+ sessionContext = {
131
+ baseUrl: e10Session.getBaseUrl(),
132
+ profile: inferProfile(ctx),
133
+ userId: e10Session.userId,
134
+ };
135
+ }
136
+ return e10Session;
137
+ }
138
+ async function getSession() {
139
+ await ensureSession();
140
+ return sessionContext;
141
+ }
142
+ async function contractRequest(request) {
143
+ const { method = 'POST', path, query = {}, body, form, userIdField, timeoutMs = DEFAULT_TIMEOUT_MS, } = request;
144
+ assertPath(path);
145
+ const session = await ensureSession();
146
+ const context = await getSession();
147
+ const headers = requestHeaders(session);
148
+ const finalQuery = { ...query };
149
+ let finalBody = body;
150
+ const init = { method, headers };
151
+ if (userIdField) {
152
+ if (!context.userId) {
153
+ throw contractAuthError('user_id_missing', '当前会话没有可用的用户 ID,请确认 E10 登录态');
154
+ }
155
+ if (method === 'GET')
156
+ finalQuery[userIdField] = context.userId;
157
+ else
158
+ finalBody = { ...finalBody, [userIdField]: context.userId };
159
+ }
160
+ if (form) {
161
+ const data = new FormData();
162
+ for (const [key, value] of Object.entries(form))
163
+ data.append(key, value);
164
+ init.body = data;
165
+ }
166
+ else if (finalBody !== undefined) {
167
+ headers['Content-Type'] = 'application/json;charset=UTF-8';
168
+ init.body = JSON.stringify(finalBody);
169
+ }
170
+ const url = withQuery(context.baseUrl, path, finalQuery);
171
+ const response = await fetchWithTimeout(url, init, timeoutMs);
172
+ if (response.status === 401 || response.status === 302) {
173
+ throw contractAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status, path });
174
+ }
175
+ const text = await response.text();
176
+ let value = {};
177
+ try {
178
+ value = text ? JSON.parse(text) : {};
179
+ }
180
+ catch {
181
+ throw contractNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, {
182
+ retryable: false,
183
+ details: { path, status: response.status },
184
+ });
185
+ }
186
+ if (response.status < 200 || response.status >= 300) {
187
+ throw new ContractCliError('api', 'http_error', `泛微接口 HTTP ${response.status}`, {
188
+ code: response.status,
189
+ details: { path, status: response.status },
190
+ exitCode: 1,
191
+ });
192
+ }
193
+ const status = contractBusinessStatus(path, value);
194
+ if (!status.ok) {
195
+ const rawPreview = typeof text === 'string' ? text.slice(0, 500) : '';
196
+ throw new ContractCliError('api', 'business_error', status.message || '泛微接口返回业务失败', {
197
+ code: status.code,
198
+ details: { path, code: status.code, raw: rawPreview },
199
+ exitCode: 1,
200
+ });
201
+ }
202
+ return value;
203
+ }
204
+ return { contractRequest, getSession };
205
+ }