weaver-work-cli 0.1.5 → 0.1.7

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 (225) hide show
  1. package/README.md +14 -9
  2. package/dist/cmd/skills/index.js +60 -0
  3. package/dist/internal/e10/auth/commands.js +48 -32
  4. package/dist/internal/e10/auth/session.js +44 -3
  5. package/dist/internal/e10/auth/xiaoe.js +363 -11
  6. package/dist/internal/e10/context.js +4 -2
  7. package/dist/internal/skills/detector.js +42 -0
  8. package/dist/internal/skills/install.js +50 -3
  9. package/dist/shortcuts/archive/render/search-format.js +12 -3
  10. package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
  11. package/dist/shortcuts/ebuilder-form/errors.js +54 -0
  12. package/dist/shortcuts/ebuilder-form/host.js +1238 -0
  13. package/dist/shortcuts/ebuilder-form/index.js +108 -0
  14. package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
  15. package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
  16. package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
  17. package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
  18. package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
  19. package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
  20. package/dist/shortcuts/ebuilder-form/operations.js +8 -0
  21. package/dist/shortcuts/ehr/operations/salary.js +2 -140
  22. package/dist/shortcuts/im/continuation.js +70 -0
  23. package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
  24. package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
  25. package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
  26. package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
  27. package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
  28. package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
  29. package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
  30. package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
  31. package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
  32. package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
  33. package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
  34. package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
  35. package/dist/shortcuts/im/operations/shared.js +442 -0
  36. package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
  37. package/dist/shortcuts/im/operations/user-remind.js +201 -0
  38. package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
  39. package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
  40. package/dist/shortcuts/index.js +10 -2
  41. package/dist/shortcuts/invoice/manifest.js +113 -10
  42. package/dist/shortcuts/invoice/operations/import.js +39 -14
  43. package/dist/shortcuts/invoice/operations/reim.js +38 -18
  44. package/dist/shortcuts/invoice/operations/shared.js +35 -6
  45. package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
  46. package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
  47. package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
  48. package/dist/shortcuts/okr/errors.js +70 -0
  49. package/dist/shortcuts/okr/host.js +169 -0
  50. package/dist/shortcuts/okr/index.js +155 -0
  51. package/dist/shortcuts/okr/manifest.js +70 -0
  52. package/dist/shortcuts/okr/operations/link.js +305 -0
  53. package/dist/shortcuts/okr/operations/read.js +481 -0
  54. package/dist/shortcuts/okr/operations/registry.js +31 -0
  55. package/dist/shortcuts/okr/operations/shared.js +381 -0
  56. package/dist/shortcuts/okr/operations/types.js +88 -0
  57. package/dist/shortcuts/okr/operations/write.js +838 -0
  58. package/dist/shortcuts/okr/operations.js +8 -0
  59. package/dist/shortcuts/project/continuation.js +70 -0
  60. package/dist/shortcuts/project/errors.js +53 -0
  61. package/dist/shortcuts/project/host.js +160 -0
  62. package/dist/shortcuts/project/index.js +106 -0
  63. package/dist/shortcuts/project/manifest.js +36 -0
  64. package/dist/shortcuts/project/operations/read.js +551 -0
  65. package/dist/shortcuts/project/operations/registry.js +31 -0
  66. package/dist/shortcuts/project/operations/shared.js +68 -0
  67. package/dist/shortcuts/project/operations/types.js +39 -0
  68. package/dist/shortcuts/project/operations/write.js +266 -0
  69. package/dist/shortcuts/project/operations.js +8 -0
  70. package/dist/shortcuts/workflow/continuation.js +508 -0
  71. package/dist/shortcuts/workflow/endpoints.js +149 -0
  72. package/dist/shortcuts/workflow/errors.js +112 -0
  73. package/dist/shortcuts/workflow/host.js +224 -0
  74. package/dist/shortcuts/workflow/index.js +116 -0
  75. package/dist/shortcuts/workflow/manifest.js +30 -0
  76. package/dist/shortcuts/workflow/operations/form.js +4159 -0
  77. package/dist/shortcuts/workflow/operations/read.js +3778 -0
  78. package/dist/shortcuts/workflow/operations/registry.js +33 -0
  79. package/dist/shortcuts/workflow/operations/shared.js +167 -0
  80. package/dist/shortcuts/workflow/operations/types.js +44 -0
  81. package/dist/shortcuts/workflow/operations/write.js +1991 -0
  82. package/dist/shortcuts/workflow/operations.js +119 -0
  83. package/dist/shortcuts/workflow/state.js +182 -0
  84. package/docs/SKILL.md +5 -3
  85. package/docs/_catalog.md +10 -1
  86. package/docs/agent-invoice.md +2 -2
  87. package/docs/agent-skill-install.md +2 -2
  88. package/docs/e10-auth.md +3 -1
  89. package/docs/ebuilder-form.md +38 -0
  90. package/docs/im.md +104 -0
  91. package/docs/invoice.md +5 -5
  92. package/docs/okr.md +122 -0
  93. package/docs/operation-manual.md +10 -7
  94. package/docs/project.md +69 -0
  95. package/docs/skill-review-2026-09-19.md +68 -0
  96. package/package.json +3 -2
  97. package/scripts/package-skill.mjs +38 -11
  98. package/scripts/sync-connector-skills.mjs +172 -0
  99. package/skill-template/business-info.json +357 -16
  100. package/skill-template/domains/ebuilder-form.md +5 -0
  101. package/skill-template/domains/okr.md +18 -0
  102. package/skill-template/domains/shijingran.md +3 -0
  103. package/skill-template/domains/skill-maker.md +4 -4
  104. package/skill-template/domains/workflow.md +22 -0
  105. package/skill-template/domains/yepiaotong.md +4 -2
  106. package/skill-template/domains/yimiaoban.md +13 -10
  107. package/skill-template/product.json +5 -0
  108. package/skill-template/skill-template.md +1 -0
  109. package/skills/weaver-e10-calendar/SKILL.md +5 -1
  110. package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
  111. package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
  112. package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
  113. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
  114. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
  115. package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
  116. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
  117. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
  118. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
  119. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
  120. package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
  121. package/skills/weaver-e10-esb/SKILL.md +1 -1
  122. package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
  123. package/skills/weaver-e10-hrm/SKILL.md +1 -1
  124. package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
  125. package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
  126. package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
  127. package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
  128. package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
  129. package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
  130. package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
  131. package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
  132. package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
  133. package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
  134. package/skills/weaver-e10-jucailin/SKILL.md +2 -2
  135. package/skills/weaver-e10-mail/SKILL.md +22 -6
  136. package/skills/weaver-e10-meeting/SKILL.md +22 -1
  137. package/skills/weaver-e10-okr/SKILL.md +139 -0
  138. package/skills/weaver-e10-okr/references/okr-align.md +59 -0
  139. package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
  140. package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
  141. package/skills/weaver-e10-okr/references/okr-query.md +105 -0
  142. package/skills/weaver-e10-okr/references/okr-write.md +94 -0
  143. package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
  144. package/skills/weaver-e10-plan/SKILL.md +3 -3
  145. package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
  146. package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
  147. package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
  148. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
  149. package/skills/weaver-e10-shared/SKILL.md +79 -11
  150. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
  151. package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
  152. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
  153. package/skills/weaver-e10-shijingran/SKILL.md +83 -0
  154. package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
  155. package/skills/weaver-e10-shijingran/references/operations.md +154 -0
  156. package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
  157. package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
  158. package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
  159. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
  160. package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
  161. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
  162. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
  163. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
  164. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
  165. package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
  166. package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
  167. package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
  168. package/skills/weaver-e10-workflow/SKILL.md +250 -0
  169. package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
  170. package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
  171. package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
  172. package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
  173. package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
  174. package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
  175. package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
  176. package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
  177. package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
  178. package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
  179. package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
  180. package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
  181. package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
  182. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
  183. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
  184. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
  185. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
  186. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
  187. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
  188. package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
  189. package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
  190. package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
  191. package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
  192. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
  193. package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
  194. package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
  195. package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
  196. package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
  197. package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
  198. package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
  199. package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
  200. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
  201. package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
  202. package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
  203. package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
  204. package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
  205. package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
  206. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
  207. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
  208. package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
  209. package/docs/yimiaoban.md +0 -86
  210. package/skills/weaver-e10-calendar/product.json +0 -8
  211. package/skills/weaver-e10-esb/product.json +0 -8
  212. package/skills/weaver-e10-hrm/product.json +0 -8
  213. package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
  214. package/skills/weaver-e10-jucailin/product.json +0 -8
  215. package/skills/weaver-e10-mail/product.json +0 -8
  216. package/skills/weaver-e10-meeting/product.json +0 -8
  217. package/skills/weaver-e10-plan/product.json +0 -8
  218. package/skills/weaver-e10-qiyecheng/product.json +0 -8
  219. package/skills/weaver-e10-skill-maker/product.json +0 -8
  220. package/skills/weaver-e10-wenshuding/product.json +0 -8
  221. package/skills/weaver-e10-yepiaotong/product.json +0 -8
  222. package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
  223. package/skills/weaver-e10-yimiaoban/product.json +0 -8
  224. package/skills/weaver-e10-ziguanjia/product.json +0 -8
  225. /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
@@ -1,18 +1,18 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { ymbConfirmationError, ymbValidationError } from '../errors.js';
3
- import { ymbSuccess } from './types.js';
2
+ import { imConfirmationError, imValidationError } from '../errors.js';
3
+ import { imSuccess } from './types.js';
4
4
  import { assertNoForbiddenKeys } from './shared.js';
5
5
  function fingerprint(value) {
6
6
  return createHash('sha256').update(JSON.stringify(value ?? null)).digest('hex').slice(0, 32);
7
7
  }
8
8
  function requireConfirm(action) {
9
- throw ymbConfirmationError(action, { hint: '重新调用时传入 confirm=true 与 prepare 返回的 continuation' });
9
+ throw imConfirmationError(action, { hint: '重新调用时传入 confirm=true 与 prepare 返回的 continuation' });
10
10
  }
11
11
  async function verifyLease(continuationToken, action, env) {
12
12
  const lease = await env.continuation.verify(continuationToken, action);
13
13
  const session = await env.host.getSession();
14
14
  if (lease.baseUrl !== session.baseUrl || lease.profile !== session.profile || lease.userId !== session.userId) {
15
- throw ymbValidationError('context_mismatch', '确认上下文已变化(租户或登录账号不同),请重新执行 prepare');
15
+ throw imValidationError('context_mismatch', '确认上下文已变化(租户或登录账号不同),请重新执行 prepare');
16
16
  }
17
17
  return lease;
18
18
  }
@@ -30,7 +30,7 @@ export function makeWritePair(options) {
30
30
  snapshotFingerprint: fingerprint(plan.snapshot),
31
31
  plan: plan.snapshot,
32
32
  });
33
- return ymbSuccess(`${options.action}.prepare`, {
33
+ return imSuccess(`${options.action}.prepare`, {
34
34
  summary: plan.summary,
35
35
  continuation: token,
36
36
  hint: '确认无误后调用 ' + options.action + '.apply,传 confirm:true 与 continuation',
@@ -43,10 +43,10 @@ export function makeWritePair(options) {
43
43
  const lease = await verifyLease(String(args.continuation ?? ''), options.action, env);
44
44
  const plan = lease.plan;
45
45
  if (lease.snapshotFingerprint !== fingerprint(plan)) {
46
- throw ymbValidationError('target_changed', '写入目标/内容与 prepare 时不一致,请重新执行 prepare');
46
+ throw imValidationError('target_changed', '写入目标/内容与 prepare 时不一致,请重新执行 prepare');
47
47
  }
48
48
  const data = await options.execute(plan, env);
49
- return ymbSuccess(`${options.action}.apply`, data);
49
+ return imSuccess(`${options.action}.apply`, data);
50
50
  },
51
51
  };
52
52
  }
@@ -1,7 +1,7 @@
1
- import { getYmbOperation } from './operations/registry.js';
1
+ import { getImOperation } from './operations/registry.js';
2
2
  import { asObject, assertNoForbiddenKeys } from './operations/shared.js';
3
- export async function executeYmbOperation(operation, rawInput, context) {
4
- const definition = getYmbOperation(operation);
3
+ export async function executeImOperation(operation, rawInput, context) {
4
+ const definition = getImOperation(operation);
5
5
  const input = asObject(rawInput);
6
6
  assertNoForbiddenKeys(input);
7
7
  return definition.handler(input, context);
@@ -1,3 +1,4 @@
1
+ import { projectShortcut } from './project/index.js';
1
2
  import { archiveShortcut } from './archive/index.js';
2
3
  import { calendarShortcut } from './calendar/index.js';
3
4
  import { ehrShortcut } from './ehr/index.js';
@@ -7,10 +8,13 @@ import { hrmShortcut } from './hrm/index.js';
7
8
  import { invoiceShortcut } from './invoice/index.js';
8
9
  import { mailShortcut } from './mail/index.js';
9
10
  import { meetingShortcut } from './meeting/index.js';
11
+ import { okrShortcut } from './okr/index.js';
10
12
  import { planShortcut } from './plan/index.js';
11
13
  import { jiuchuanhuiShortcut } from './jiuchuanhui/index.js';
12
14
  import { assetShortcut } from './asset/index.js';
13
- import { yimiaobanShortcut } from './yimiaoban/index.js';
15
+ import { ebuilderFormShortcut } from './ebuilder-form/index.js';
16
+ import { imShortcut } from './im/index.js';
17
+ import { workflowShortcut } from './workflow/index.js';
14
18
  export const builtinShortcuts = [
15
19
  invoiceShortcut,
16
20
  ehrShortcut,
@@ -20,9 +24,13 @@ export const builtinShortcuts = [
20
24
  hrmShortcut,
21
25
  mailShortcut,
22
26
  meetingShortcut,
27
+ okrShortcut,
23
28
  planShortcut,
24
29
  esbShortcut,
30
+ ebuilderFormShortcut,
25
31
  jiuchuanhuiShortcut,
26
32
  assetShortcut,
27
- yimiaobanShortcut
33
+ imShortcut,
34
+ projectShortcut,
35
+ workflowShortcut
28
36
  ];
@@ -52,7 +52,96 @@ const listProperties = {
52
52
  };
53
53
  const positiveId = { type: 'string', pattern: '^[1-9]\\d*$' };
54
54
  const positiveIdArray = { type: 'array', minItems: 1, maxItems: 100, items: positiveId };
55
+ const jsonObject = { type: 'object', additionalProperties: true };
55
56
  const requestPayload = { type: 'object', minProperties: 1 };
57
+ const reimFieldValue = {
58
+ type: 'object',
59
+ additionalProperties: false,
60
+ properties: {
61
+ fieldId: { type: 'string', minLength: 1 },
62
+ fieldName: { type: 'string' },
63
+ value: {},
64
+ optionValue: {},
65
+ unable_to_fill: { type: 'boolean' },
66
+ need_search: { type: 'boolean' },
67
+ },
68
+ required: ['fieldId'],
69
+ };
70
+ const reimNeedUserConfirm = {
71
+ type: 'object',
72
+ additionalProperties: false,
73
+ properties: {
74
+ fieldId: { type: 'string', minLength: 1 },
75
+ fieldName: { type: 'string' },
76
+ dataIndex: { type: ['integer', 'null'], minimum: 1 },
77
+ reason: { type: 'string', minLength: 1 },
78
+ candidates: {
79
+ type: 'array',
80
+ items: {
81
+ type: 'object',
82
+ additionalProperties: true,
83
+ properties: {
84
+ id: { type: 'string', minLength: 1 },
85
+ name: { type: 'string' },
86
+ },
87
+ },
88
+ },
89
+ },
90
+ required: ['fieldId', 'reason'],
91
+ };
92
+ const reimUnableToFill = {
93
+ type: 'object',
94
+ additionalProperties: false,
95
+ properties: {
96
+ fieldId: { type: 'string', minLength: 1 },
97
+ fieldName: { type: 'string' },
98
+ dataIndex: { type: ['integer', 'null'], minimum: 1 },
99
+ reason: { type: 'string', minLength: 1 },
100
+ },
101
+ required: ['fieldId', 'reason'],
102
+ };
103
+ const reimDetailRow = {
104
+ type: 'object',
105
+ additionalProperties: false,
106
+ properties: {
107
+ dataIndex: { type: 'integer', minimum: 1 },
108
+ subFormId: { type: 'string', minLength: 1 },
109
+ invoiceId: { type: 'string', minLength: 1 },
110
+ fields: { type: 'array', items: reimFieldValue },
111
+ },
112
+ required: ['dataIndex', 'subFormId', 'invoiceId', 'fields'],
113
+ };
114
+ const reimFlowCreateProperties = {
115
+ work_flow_id: { type: 'string', minLength: 1, description: '目标报销流程的工作流 ID。' },
116
+ mainFormId: { type: 'string', minLength: 1, description: '报销单主表 formId,来自 invoice.reim.form.structure。' },
117
+ request_name: { type: 'string', minLength: 1, description: '流程标题。' },
118
+ main_fields: { type: 'array', items: reimFieldValue, description: '主表字段值列表。' },
119
+ detail_rows: { type: 'array', minItems: 1, items: reimDetailRow, description: '明细表行数据。' },
120
+ need_user_confirm: { type: 'array', items: reimNeedUserConfirm, description: '需用户确认的字段。' },
121
+ unable_to_fill: { type: 'array', items: reimUnableToFill, description: '无法自动填写的字段。' },
122
+ };
123
+ const reimFlowCreateRequired = ['work_flow_id', 'mainFormId', 'request_name', 'main_fields', 'detail_rows'];
124
+ const reimFlowUpdateProperties = {
125
+ requestId: { type: 'string', minLength: 1, description: '要更新的流程 requestId。' },
126
+ mainFormId: { type: 'string', minLength: 1, description: '报销单主表 formId。' },
127
+ main_fields: { type: 'array', minItems: 1, items: reimFieldValue, description: '需要更新的主表字段列表。' },
128
+ detail_rows: { type: 'array', minItems: 1, items: reimDetailRow, description: '需要更新或新增的明细行数据。' },
129
+ deleteDetailRows: {
130
+ type: 'array',
131
+ minItems: 1,
132
+ items: {
133
+ type: 'object',
134
+ additionalProperties: true,
135
+ properties: {
136
+ detailId: { type: 'string', minLength: 1 },
137
+ dataIndex: { type: 'integer', minimum: 1 },
138
+ },
139
+ },
140
+ description: '需要删除的明细行。',
141
+ },
142
+ need_user_confirm: { type: 'array', items: reimNeedUserConfirm, description: '需用户确认的字段。' },
143
+ unable_to_fill: { type: 'array', items: reimUnableToFill, description: '无法自动填写的字段。' },
144
+ };
56
145
  export const invoiceOperations = [
57
146
  {
58
147
  name: 'invoice.list',
@@ -133,6 +222,7 @@ export const invoiceOperations = [
133
222
  additionalProperties: false,
134
223
  properties: {
135
224
  file: { type: 'string', minLength: 1 },
225
+ url: { type: 'string', format: 'uri' },
136
226
  validate: {
137
227
  type: 'boolean',
138
228
  description: '是否在导入时请求服务端查验。正常上传发票流程默认应传 true;只有用户明确要求跳过查验时才传 false。',
@@ -141,8 +231,12 @@ export const invoiceOperations = [
141
231
  type: 'boolean',
142
232
  description: '是否同步到 OA。validate=true 时必须为 true;正常导入推荐传 true。',
143
233
  },
234
+ fieldKey: { type: 'string', minLength: 1, description: '给成功发票设置的单个字段名;必须与 fieldValue 同时提供。' },
235
+ fieldValue: { type: 'string', description: 'fieldKey 对应字段值;必须与 fieldKey 同时提供。' },
236
+ filedMap: { ...jsonObject, description: '批量设置成功发票的默认字段。保持 JSON 对象输入,CLI 仅在 multipart 提交前序列化。' },
144
237
  },
145
- required: ['file', 'validate', 'syncToOa'],
238
+ required: ['validate', 'syncToOa'],
239
+ oneOf: [{ required: ['file'] }, { required: ['url'] }],
146
240
  },
147
241
  },
148
242
  {
@@ -153,10 +247,17 @@ export const invoiceOperations = [
153
247
  additionalProperties: false,
154
248
  properties: {
155
249
  file: { type: 'string', minLength: 1 },
250
+ url: { type: 'string', format: 'uri' },
251
+ validate: { type: 'boolean' },
252
+ syncToOa: { type: 'boolean' },
253
+ fieldKey: { type: 'string', minLength: 1 },
254
+ fieldValue: { type: 'string' },
255
+ filedMap: { ...jsonObject, description: '批量设置成功发票的默认字段。保持 JSON 对象输入,CLI 仅在 multipart 提交前序列化。' },
156
256
  continuation: { type: 'string', minLength: 1 },
157
257
  confirm: { const: true },
158
258
  },
159
- required: ['file', 'continuation', 'confirm'],
259
+ required: ['continuation', 'confirm'],
260
+ oneOf: [{ required: ['file'] }, { required: ['url'] }],
160
261
  },
161
262
  },
162
263
  {
@@ -391,8 +492,8 @@ export const invoiceOperations = [
391
492
  inputSchema: {
392
493
  type: 'object',
393
494
  additionalProperties: false,
394
- properties: { payload: requestPayload },
395
- required: ['payload'],
495
+ properties: reimFlowCreateProperties,
496
+ required: reimFlowCreateRequired,
396
497
  },
397
498
  },
398
499
  {
@@ -402,11 +503,11 @@ export const invoiceOperations = [
402
503
  type: 'object',
403
504
  additionalProperties: false,
404
505
  properties: {
405
- payload: requestPayload,
506
+ ...reimFlowCreateProperties,
406
507
  continuation: { type: 'string', minLength: 1 },
407
508
  confirm: { const: true },
408
509
  },
409
- required: ['payload', 'continuation', 'confirm'],
510
+ required: [...reimFlowCreateRequired, 'continuation', 'confirm'],
410
511
  },
411
512
  },
412
513
  {
@@ -416,8 +517,9 @@ export const invoiceOperations = [
416
517
  inputSchema: {
417
518
  type: 'object',
418
519
  additionalProperties: false,
419
- properties: { payload: requestPayload },
420
- required: ['payload'],
520
+ properties: reimFlowUpdateProperties,
521
+ required: ['requestId', 'mainFormId'],
522
+ anyOf: [{ required: ['main_fields'] }, { required: ['detail_rows'] }, { required: ['deleteDetailRows'] }],
421
523
  },
422
524
  },
423
525
  {
@@ -427,11 +529,12 @@ export const invoiceOperations = [
427
529
  type: 'object',
428
530
  additionalProperties: false,
429
531
  properties: {
430
- payload: requestPayload,
532
+ ...reimFlowUpdateProperties,
431
533
  continuation: { type: 'string', minLength: 1 },
432
534
  confirm: { const: true },
433
535
  },
434
- required: ['payload', 'continuation', 'confirm'],
536
+ required: ['requestId', 'mainFormId', 'continuation', 'confirm'],
537
+ anyOf: [{ required: ['main_fields'] }, { required: ['detail_rows'] }, { required: ['deleteDetailRows'] }],
435
538
  },
436
539
  },
437
540
  {
@@ -1,18 +1,21 @@
1
1
  import { InvoiceCliError, invoiceConfirmationError, invoicePartialError, invoiceValidationError, isInvoiceNetworkError } from '../errors.js';
2
- import { asPositiveString, detailInfo, getDetail, importFile, importOptions, preview, sessionMatches, workflow, } from './shared.js';
2
+ import { asPositiveString, detailInfo, getDetail, importOptions, importSource, objectFingerprint, preview, sessionMatches, workflow, } from './shared.js';
3
3
  export class InvoiceImportPrepareOperation {
4
4
  name = 'invoice.import.prepare';
5
5
  async execute(input, { host, continuation }) {
6
6
  const options = importOptions(input);
7
- const prepared = await importFile(input);
7
+ const prepared = await importSource(input);
8
8
  const session = await host.getSession();
9
9
  const filePreview = {
10
- name: prepared.file.name,
11
- size: prepared.size,
12
- sha256: prepared.sha256,
10
+ source: prepared.kind,
11
+ ...(prepared.kind === 'file'
12
+ ? { name: prepared.file.name, size: prepared.size, sha256: prepared.sha256 }
13
+ : { url: prepared.url }),
13
14
  validate: options.validate,
14
15
  validationSkipped: !options.validate,
15
16
  syncToOa: options.syncToOa,
17
+ fieldKey: options.fieldKey,
18
+ filedMap: options.filedMap,
16
19
  targetScope: 'personal',
17
20
  };
18
21
  const continuationToken = await continuation.issue({
@@ -21,7 +24,8 @@ export class InvoiceImportPrepareOperation {
21
24
  baseUrl: session.baseUrl,
22
25
  profile: session.profile,
23
26
  userId: session.userId,
24
- sha256: prepared.sha256,
27
+ sourceKind: prepared.kind,
28
+ sourceFingerprint: prepared.fingerprint,
25
29
  options,
26
30
  preview: filePreview,
27
31
  });
@@ -42,24 +46,45 @@ export class InvoiceImportApplyOperation {
42
46
  if (input.confirm !== true)
43
47
  throw invoiceConfirmationError('invoice.import.apply', { hint: '重新调用时传入 confirm=true' });
44
48
  const lease = await continuation.verify(input.continuation, 'invoice.import.apply');
45
- if (!lease.options || typeof lease.options.validate !== 'boolean' || typeof lease.options.syncToOa !== 'boolean' || typeof lease.sha256 !== 'string') {
49
+ if (!lease.options || typeof lease.options.validate !== 'boolean' || typeof lease.options.syncToOa !== 'boolean' || typeof lease.sourceFingerprint !== 'string') {
46
50
  throw invoiceValidationError('continuation_invalid', '导入 continuation 缺少有效文件指纹或选项');
47
51
  }
48
52
  const session = await host.getSession();
49
53
  if (!sessionMatches(lease, session))
50
54
  throw invoiceValidationError('context_mismatch', '确认上下文已变化,请重新准备导入');
51
- const prepared = await importFile(input);
52
- if (prepared.sha256 !== lease.sha256)
53
- throw invoiceValidationError('target_changed', '导入文件与确认时不一致,请重新准备导入');
54
- const fields = { allSuccess: lease.options.validate ? '0' : '1' };
55
- if (!lease.options.validate)
56
- fields.filedMap = JSON.stringify({ isToOa: lease.options.syncToOa ? '1' : '0' });
55
+ const prepared = await importSource(input);
56
+ if (prepared.fingerprint !== lease.sourceFingerprint)
57
+ throw invoiceValidationError('target_changed', '导入来源与确认时不一致,请重新准备导入');
58
+ const options = importOptions({
59
+ ...input,
60
+ validate: input.validate ?? lease.options.validate,
61
+ syncToOa: input.syncToOa ?? lease.options.syncToOa,
62
+ fieldKey: input.fieldKey ?? lease.options.fieldKey,
63
+ fieldValue: input.fieldValue ?? lease.options.fieldValue,
64
+ filedMap: input.filedMap ?? lease.options.filedMap,
65
+ });
66
+ if (objectFingerprint(options) !== objectFingerprint(lease.options))
67
+ throw invoiceValidationError('target_changed', '导入选项与确认时不一致,请重新准备导入');
68
+ const fields = {
69
+ ...prepared.fields,
70
+ allSuccess: lease.options.validate ? '0' : '1',
71
+ };
72
+ if (lease.options.fieldKey !== undefined) {
73
+ fields.fieldKey = lease.options.fieldKey;
74
+ fields.fieldValue = lease.options.fieldValue || '';
75
+ }
76
+ const filedMap = { ...(lease.options.filedMap || {}) };
77
+ if (!lease.options.validate && filedMap.isToOa === undefined) {
78
+ filedMap.isToOa = lease.options.syncToOa ? '1' : '0';
79
+ }
80
+ if (Object.keys(filedMap).length)
81
+ fields.filedMap = JSON.stringify(filedMap);
57
82
  let result;
58
83
  try {
59
84
  result = await host.invoiceMultipartRequest({
60
85
  path: '/api/app/inc/biz/addInvoiceByFile',
61
86
  fields,
62
- file: prepared.file,
87
+ file: prepared.kind === 'file' ? prepared.file : undefined,
63
88
  timeoutMs: 90_000,
64
89
  });
65
90
  }
@@ -80,27 +80,47 @@ function stringifyEmployeeSuperiorsIds(value) {
80
80
  function employeeSuperiorsEnvelope(value) {
81
81
  return resultEnvelope(stringifyEmployeeSuperiorsIds(value), 'invoice.reim.employee-superiors');
82
82
  }
83
- function ensureFlowCreatePayload(payload) {
84
- const value = structuredClone(asObject(payload, 'payload'));
85
- asNonEmptyString(value.work_flow_id, 'payload.work_flow_id');
86
- asNonEmptyString(value.mainFormId, 'payload.mainFormId');
87
- asNonEmptyString(value.request_name, 'payload.request_name');
83
+ const flowCreateFields = ['work_flow_id', 'mainFormId', 'request_name', 'main_fields', 'detail_rows', 'need_user_confirm', 'unable_to_fill'];
84
+ const flowUpdateFields = ['requestId', 'mainFormId', 'main_fields', 'detail_rows', 'deleteDetailRows', 'need_user_confirm', 'unable_to_fill'];
85
+ const flowMetaFields = new Set(['continuation', 'confirm']);
86
+ function flowPayloadFromInput(input, allowedFields) {
87
+ const source = asObject(input, 'input');
88
+ if (source.payload !== undefined) {
89
+ return { value: structuredClone(asObject(source.payload, 'payload')), prefix: 'payload.' };
90
+ }
91
+ const allowed = new Set(allowedFields);
92
+ const value = {};
93
+ for (const [key, item] of Object.entries(source)) {
94
+ if (flowMetaFields.has(key))
95
+ continue;
96
+ if (!allowed.has(key))
97
+ throw invoiceValidationError('input_invalid', `${key} 不是支持的报销单字段`);
98
+ value[key] = item;
99
+ }
100
+ return { value: structuredClone(value), prefix: '' };
101
+ }
102
+ function ensureFlowCreatePayload(input) {
103
+ const { value, prefix } = flowPayloadFromInput(input, flowCreateFields);
104
+ asNonEmptyString(value.work_flow_id, `${prefix}work_flow_id`);
105
+ asNonEmptyString(value.mainFormId, `${prefix}mainFormId`);
106
+ asNonEmptyString(value.request_name, `${prefix}request_name`);
88
107
  if (!Array.isArray(value.main_fields))
89
- throw invoiceValidationError('input_invalid', 'payload.main_fields 必须是数组');
108
+ throw invoiceValidationError('input_invalid', `${prefix}main_fields 必须是数组`);
90
109
  if (!Array.isArray(value.detail_rows) || value.detail_rows.length === 0) {
91
- throw invoiceValidationError('input_invalid', 'payload.detail_rows 必须是非空数组');
110
+ throw invoiceValidationError('input_invalid', `${prefix}detail_rows 必须是非空数组`);
92
111
  }
93
112
  return value;
94
113
  }
95
- function ensureFlowUpdatePayload(payload) {
96
- const value = structuredClone(asObject(payload, 'payload'));
97
- asNonEmptyString(value.requestId, 'payload.requestId');
98
- asNonEmptyString(value.mainFormId, 'payload.mainFormId');
114
+ function ensureFlowUpdatePayload(input) {
115
+ const { value, prefix } = flowPayloadFromInput(input, flowUpdateFields);
116
+ asNonEmptyString(value.requestId, `${prefix}requestId`);
117
+ asNonEmptyString(value.mainFormId, `${prefix}mainFormId`);
99
118
  const hasMain = Array.isArray(value.main_fields) && value.main_fields.length > 0;
100
119
  const hasDetails = Array.isArray(value.detail_rows) && value.detail_rows.length > 0;
101
120
  const hasDeletes = Array.isArray(value.deleteDetailRows) && value.deleteDetailRows.length > 0;
102
121
  if (!hasMain && !hasDetails && !hasDeletes) {
103
- throw invoiceValidationError('input_invalid', 'payload 必须包含 main_fields、detail_rows deleteDetailRows 至少一类变更');
122
+ const target = prefix ? `${prefix}必须` : 'input 必须';
123
+ throw invoiceValidationError('input_invalid', `${target}包含 main_fields、detail_rows 或 deleteDetailRows 至少一类变更`);
104
124
  }
105
125
  return value;
106
126
  }
@@ -257,7 +277,7 @@ export class InvoiceReimFieldSearchOperation {
257
277
  export class InvoiceReimFlowCreatePrepareOperation {
258
278
  name = 'invoice.reim.flow.create.prepare';
259
279
  async execute(input, { host, continuation }) {
260
- const payload = ensureFlowCreatePayload(input.payload);
280
+ const payload = ensureFlowCreatePayload(input);
261
281
  const session = await host.getSession();
262
282
  const continuationToken = await continuation.issue({
263
283
  operation: 'invoice.reim.flow.create.apply',
@@ -285,9 +305,9 @@ export class InvoiceReimFlowCreateApplyOperation {
285
305
  if (input.confirm !== true)
286
306
  throw invoiceConfirmationError('invoice.reim.flow.create.apply', { hint: '重新调用时传入 confirm=true' });
287
307
  const lease = await continuation.verify(input.continuation, 'invoice.reim.flow.create.apply');
288
- const payload = ensureFlowCreatePayload(input.payload);
308
+ const payload = ensureFlowCreatePayload(input);
289
309
  if (typeof lease.fingerprint !== 'string')
290
- throw invoiceValidationError('continuation_invalid', '报销单创建 continuation 缺少 payload 指纹');
310
+ throw invoiceValidationError('continuation_invalid', '报销单创建 continuation 缺少报销单指纹');
291
311
  if (objectFingerprint(payload) !== lease.fingerprint)
292
312
  throw invoiceValidationError('target_changed', '报销单载荷与确认时不一致,请重新准备');
293
313
  const session = await host.getSession();
@@ -331,7 +351,7 @@ export class InvoiceReimFlowCreateApplyOperation {
331
351
  export class InvoiceReimFlowUpdatePrepareOperation {
332
352
  name = 'invoice.reim.flow.update.prepare';
333
353
  async execute(input, { host, continuation }) {
334
- const payload = ensureFlowUpdatePayload(input.payload);
354
+ const payload = ensureFlowUpdatePayload(input);
335
355
  const session = await host.getSession();
336
356
  const continuationToken = await continuation.issue({
337
357
  operation: 'invoice.reim.flow.update.apply',
@@ -359,9 +379,9 @@ export class InvoiceReimFlowUpdateApplyOperation {
359
379
  if (input.confirm !== true)
360
380
  throw invoiceConfirmationError('invoice.reim.flow.update.apply', { hint: '重新调用时传入 confirm=true' });
361
381
  const lease = await continuation.verify(input.continuation, 'invoice.reim.flow.update.apply');
362
- const payload = ensureFlowUpdatePayload(input.payload);
382
+ const payload = ensureFlowUpdatePayload(input);
363
383
  if (typeof lease.fingerprint !== 'string')
364
- throw invoiceValidationError('continuation_invalid', '报销单更新 continuation 缺少 payload 指纹');
384
+ throw invoiceValidationError('continuation_invalid', '报销单更新 continuation 缺少报销单指纹');
365
385
  if (objectFingerprint(payload) !== lease.fingerprint)
366
386
  throw invoiceValidationError('target_changed', '报销单更新载荷与确认时不一致,请重新准备');
367
387
  const session = await host.getSession();
@@ -557,18 +557,28 @@ async function normalizeFileSource(source) {
557
557
  file: { path: source.path, name: basename(source.path), mimeType: MIME_TYPES[extension] },
558
558
  };
559
559
  }
560
- export async function importFile(input) {
561
- if (input.url !== undefined)
562
- throw invoiceValidationError('import_url_unsupported', '确定性导入当前只接受本地 file,不接受远程 URL');
563
- const source = uploadSource({ file: input.file });
560
+ export async function importSource(input) {
561
+ const source = uploadSource(input);
564
562
  const prepared = await normalizeFileSource(source);
563
+ if (source.kind === 'url') {
564
+ return {
565
+ kind: 'url',
566
+ fields: { url: source.url },
567
+ url: source.url,
568
+ fingerprint: objectFingerprint({ kind: 'url', url: source.url }),
569
+ };
570
+ }
565
571
  if (!prepared.file)
566
572
  throw invoiceValidationError('file_required', '必须提供本地 file 路径');
567
573
  const content = await readFile(prepared.file.path);
574
+ const sha256 = createHash('sha256').update(content).digest('hex');
568
575
  return {
576
+ kind: 'file',
577
+ fields: {},
569
578
  file: prepared.file,
570
579
  size: content.length,
571
- sha256: createHash('sha256').update(content).digest('hex'),
580
+ sha256,
581
+ fingerprint: objectFingerprint({ kind: 'file', sha256 }),
572
582
  };
573
583
  }
574
584
  export function importOptions(input) {
@@ -578,7 +588,26 @@ export function importOptions(input) {
578
588
  if (input.validate && !input.syncToOa) {
579
589
  throw invoiceValidationError('import_option_conflict', 'validate=true 分支由服务端固定同步 OA,不能设置 syncToOa=false');
580
590
  }
581
- return { validate: input.validate, syncToOa: input.validate ? true : input.syncToOa };
591
+ const fieldKey = input.fieldKey === undefined || input.fieldKey === null || input.fieldKey === ''
592
+ ? undefined
593
+ : asNonEmptyString(input.fieldKey, 'fieldKey');
594
+ const fieldValue = input.fieldValue === undefined || input.fieldValue === null
595
+ ? undefined
596
+ : String(input.fieldValue);
597
+ if ((fieldKey === undefined) !== (fieldValue === undefined)) {
598
+ throw invoiceValidationError('import_field_pair_required', 'fieldKey 与 fieldValue 必须同时提供');
599
+ }
600
+ const filedMap = input.filedMap === undefined || input.filedMap === null
601
+ ? undefined
602
+ : asObject(input.filedMap, 'filedMap');
603
+ if (filedMap)
604
+ assertNoForbiddenKeys(filedMap, 'filedMap');
605
+ return {
606
+ validate: input.validate,
607
+ syncToOa: input.validate ? true : input.syncToOa,
608
+ ...(fieldKey === undefined ? {} : { fieldKey, fieldValue }),
609
+ ...(filedMap === undefined ? {} : { filedMap }),
610
+ };
582
611
  }
583
612
  function normalizeUploadResponse(value, source) {
584
613
  if (value?.fail === true) {
@@ -206,8 +206,8 @@ export const jiuchuanhuiOperations = [
206
206
  inputSchema: { type: 'object', additionalProperties: true, properties: pageProps },
207
207
  },
208
208
  ...writePair('jiuchuanhui.customer.update', '客户修改', { form: formProp }, ['form'], { path: '/api/ebuilder/form/formdata/v2/updateFormData/customer_update_api' }),
209
- ...writePair('jiuchuanhui.customer.transfer', '客户转移', esbWriteProps(['customerId', 'toUserId', 'remark']), ['customerId', 'toUserId'], { uniqueIndent: '1E44ED7DA9EB46329818E682C2914928' }),
210
- ...writePair('jiuchuanhui.customer.open-sea.release', '客户释放公海', esbWriteProps(['customerId', 'openSeaId', 'remark']), ['customerId', 'openSeaId'], { uniqueIndent: '0E29AD0E708D4964B071D293288B92D7' }),
209
+ ...writePair('jiuchuanhui.customer.transfer', '客户转移', esbWriteProps(['customerId', 'employee_MappingId', 'employeeName']), ['customerId'], { uniqueIndent: '1E44ED7DA9EB46329818E682C2914928' }),
210
+ ...writePair('jiuchuanhui.customer.open-sea.release', '客户释放公海', esbWriteProps(['customerId', 'openSeaId']), ['customerId', 'openSeaId'], { uniqueIndent: '0E29AD0E708D4964B071D293288B92D7' }),
211
211
  ...writePair('jiuchuanhui.customer.open-sea.claim', '领取公海客户', esbWriteProps(['customer_id']), ['customer_id'], { uniqueIndent: 'D587CDC892FE41118DD073475C01A2CE' }),
212
212
  {
213
213
  name: 'jiuchuanhui.customer.duplicate.check',
@@ -311,7 +311,7 @@ export const jiuchuanhuiOperations = [
311
311
  ...writePair('jiuchuanhui.clue.update', '线索修改', { form: formProp }, ['form'], { path: '/api/ebuilder/form/formdata/v2/updateFormData/ebclue_ai_update_api' }),
312
312
  ...writePair('jiuchuanhui.clue.status-change', '线索状态变更', esbWriteProps(['clueid', 'type', 'newmanager', 'desc']), ['clueid', 'type'], { uniqueIndent: '257CC8DE42234F9191439B229839B8A7' }),
313
313
  ...writePair('jiuchuanhui.clue.to-customer', '线索转已有客户', esbWriteProps(['clueId', 'customerId']), ['clueId', 'customerId'], { uniqueIndent: '00914C6415EB455DB7FCCC32DBE6E0C3' }),
314
- ...writePair('jiuchuanhui.clue.contact-record.create', '新建线索跟进记录', esbWriteProps(['clueId', 'content', 'clueStatus_MappingId']), ['clueId', 'content', 'clueStatus_MappingId'], { uniqueIndent: 'B3DD120601344320A2B50A042B450681' }),
314
+ ...writePair('jiuchuanhui.clue.contact-record.create', '新建线索跟进记录', esbWriteProps(['clueId', 'content', 'clueStatus_MappingId']), ['clueId', 'content'], { uniqueIndent: 'B3DD120601344320A2B50A042B450681' }),
315
315
  // ==================== 联系计划 ====================
316
316
  ...writePair('jiuchuanhui.contact-plan.create', '创建联系计划', { form: formProp }, ['form'], { path: '/api/ebuilder/form/formdata/v2/saveFormData/ebcontact_plan_create_api' }),
317
317
  ...writePair('jiuchuanhui.contact-plan.complete', '完成联系计划', esbWriteProps(['planId']), ['planId'], { uniqueIndent: '2595D3556CFC414F85B1A4A8D806EFE5' }),
@@ -33,9 +33,9 @@ function buildRegistry() {
33
33
  new DatajsonWritePrepareOperation('jiuchuanhui.customer.update.prepare', 'jiuchuanhui.customer.update.apply'),
34
34
  new DatajsonWriteApplyOperation('jiuchuanhui.customer.update.apply', '/api/ebuilder/form/formdata/v2/updateFormData/customer_update_api'),
35
35
  // ===== 客户 ESB 写操作 =====
36
- new EsbWritePrepareOperation('jiuchuanhui.customer.transfer.prepare', 'jiuchuanhui.customer.transfer.apply', '1E44ED7DA9EB46329818E682C2914928', ['customerId', 'toUserId', 'remark']),
36
+ new EsbWritePrepareOperation('jiuchuanhui.customer.transfer.prepare', 'jiuchuanhui.customer.transfer.apply', '1E44ED7DA9EB46329818E682C2914928', ['customerId', 'employee_MappingId', 'employeeName']),
37
37
  new EsbWriteApplyOperation('jiuchuanhui.customer.transfer.apply', '1E44ED7DA9EB46329818E682C2914928'),
38
- new EsbWritePrepareOperation('jiuchuanhui.customer.open-sea.release.prepare', 'jiuchuanhui.customer.open-sea.release.apply', '0E29AD0E708D4964B071D293288B92D7', ['customerId', 'openSeaId', 'remark']),
38
+ new EsbWritePrepareOperation('jiuchuanhui.customer.open-sea.release.prepare', 'jiuchuanhui.customer.open-sea.release.apply', '0E29AD0E708D4964B071D293288B92D7', ['customerId', 'openSeaId']),
39
39
  new EsbWriteApplyOperation('jiuchuanhui.customer.open-sea.release.apply', '0E29AD0E708D4964B071D293288B92D7'),
40
40
  new EsbWritePrepareOperation('jiuchuanhui.customer.open-sea.claim.prepare', 'jiuchuanhui.customer.open-sea.claim.apply', 'D587CDC892FE41118DD073475C01A2CE', ['customer_id']),
41
41
  new EsbWriteApplyOperation('jiuchuanhui.customer.open-sea.claim.apply', 'D587CDC892FE41118DD073475C01A2CE'),
@@ -3,16 +3,16 @@ import { existsSync } from 'node:fs';
3
3
  import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
4
4
  import { homedir } from 'node:os';
5
5
  import { resolve } from 'node:path';
6
- import { ymbValidationError } from './errors.js';
7
- function stateRoot(ctx) {
6
+ import { okrValidationError } from './errors.js';
7
+ export function okrStateRoot(ctx) {
8
8
  const configured = ctx?.config?.path();
9
9
  if (configured)
10
10
  return resolve(configured, '..');
11
11
  return process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve(homedir(), '.weaver-work-cli');
12
12
  }
13
13
  async function loadKey(ctx) {
14
- const keysDir = resolve(stateRoot(ctx), 'keys');
15
- const filename = resolve(keysDir, 'yimiaoban-continuation.key');
14
+ const keysDir = resolve(okrStateRoot(ctx), 'keys');
15
+ const filename = resolve(keysDir, 'okr-continuation.key');
16
16
  await mkdir(keysDir, { recursive: true, mode: 0o700 });
17
17
  if (existsSync(filename))
18
18
  return readFile(filename);
@@ -33,7 +33,7 @@ function encode(value) {
33
33
  function signature(payload, key) {
34
34
  return createHmac('sha256', key).update(payload).digest('base64url');
35
35
  }
36
- export function createYmbContinuationSigner(ctx) {
36
+ export function createOkrContinuationSigner(ctx) {
37
37
  return {
38
38
  async issue(payload, ttlMs = 10 * 60_000) {
39
39
  const issuedAt = Date.now();
@@ -43,26 +43,26 @@ export function createYmbContinuationSigner(ctx) {
43
43
  },
44
44
  async verify(token, expectedOperation) {
45
45
  if (typeof token !== 'string' || !token.includes('.')) {
46
- throw ymbValidationError('continuation_invalid', 'continuation 无效');
46
+ throw okrValidationError('continuation_invalid', 'continuation 无效');
47
47
  }
48
48
  const [body, provided] = token.split('.');
49
49
  const expected = signature(body, await loadKey(ctx));
50
50
  const valid = provided.length === expected.length
51
51
  && timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
52
52
  if (!valid)
53
- throw ymbValidationError('continuation_invalid', 'continuation 签名校验失败');
53
+ throw okrValidationError('continuation_invalid', 'continuation 签名校验失败');
54
54
  let value;
55
55
  try {
56
56
  value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
57
57
  }
58
58
  catch {
59
- throw ymbValidationError('continuation_invalid', 'continuation 内容无法解析');
59
+ throw okrValidationError('continuation_invalid', 'continuation 内容无法解析');
60
60
  }
61
61
  if (value.operation !== expectedOperation) {
62
- throw ymbValidationError('continuation_mismatch', 'continuation 不属于当前操作');
62
+ throw okrValidationError('continuation_mismatch', 'continuation 不属于当前操作');
63
63
  }
64
64
  if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
65
- throw ymbValidationError('continuation_expired', 'continuation 已过期');
65
+ throw okrValidationError('continuation_expired', 'continuation 已过期');
66
66
  }
67
67
  return value;
68
68
  },