weaver-work-cli 0.1.6 → 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 +44 -31
  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
@@ -0,0 +1,132 @@
1
+ import { ebuilderValidationError } from '../errors.js';
2
+ const FORBIDDEN_KEYS = new Set([
3
+ '__proto__',
4
+ 'constructor',
5
+ 'prototype',
6
+ 'access_token',
7
+ 'app_key',
8
+ 'app_secret',
9
+ 'authorization',
10
+ 'cookie',
11
+ 'eteamsid',
12
+ 'sedit',
13
+ 'token',
14
+ ]);
15
+ export function asObject(value, field = 'input') {
16
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
17
+ throw ebuilderValidationError('input_not_object', `${field} 必须是 JSON 对象`);
18
+ }
19
+ return value;
20
+ }
21
+ export function optionalObject(value, field) {
22
+ if (value === undefined || value === null)
23
+ return undefined;
24
+ return asObject(value, field);
25
+ }
26
+ export function requiredObject(value, field) {
27
+ return asObject(value, field);
28
+ }
29
+ export function requiredArray(value, field) {
30
+ if (!Array.isArray(value))
31
+ throw ebuilderValidationError('field_invalid', `${field} 必须是数组`);
32
+ return value;
33
+ }
34
+ export function optionalArray(value, field) {
35
+ if (value === undefined || value === null)
36
+ return undefined;
37
+ return requiredArray(value, field);
38
+ }
39
+ export function requiredString(value, field) {
40
+ if (value === undefined || value === null || value === '') {
41
+ throw ebuilderValidationError('field_required', `${field} 必填`);
42
+ }
43
+ if (typeof value === 'object')
44
+ throw ebuilderValidationError('field_invalid', `${field} 必须是字符串`);
45
+ return String(value);
46
+ }
47
+ export function optionalString(value, field) {
48
+ if (value === undefined || value === null || value === '')
49
+ return undefined;
50
+ if (typeof value === 'object')
51
+ throw ebuilderValidationError('field_invalid', `${field} 必须是字符串`);
52
+ return String(value);
53
+ }
54
+ export function idString(value, field, required = true) {
55
+ const text = required ? requiredString(value, field) : optionalString(value, field);
56
+ if (!text)
57
+ return '';
58
+ if (!/^\d+$/u.test(text)) {
59
+ throw ebuilderValidationError('field_invalid', `${field} 必须是数字 ID 字符串`);
60
+ }
61
+ return text;
62
+ }
63
+ export function enumString(value, field, allowed, fallback) {
64
+ const text = optionalString(value, field);
65
+ if (!text) {
66
+ if (fallback !== undefined)
67
+ return fallback;
68
+ throw ebuilderValidationError('field_required', `${field} 必填`);
69
+ }
70
+ if (!allowed.includes(text)) {
71
+ throw ebuilderValidationError('field_invalid', `${field} 只能是:${allowed.join('、')}`);
72
+ }
73
+ return text;
74
+ }
75
+ export function boolValue(value, field, fallback = false) {
76
+ if (value === undefined || value === null || value === '')
77
+ return fallback;
78
+ if (typeof value === 'boolean')
79
+ return value;
80
+ if (typeof value === 'string') {
81
+ const normalized = value.trim().toLowerCase();
82
+ if (['true', 'y', 'yes', '1'].includes(normalized))
83
+ return true;
84
+ if (['false', 'n', 'no', '0'].includes(normalized))
85
+ return false;
86
+ }
87
+ throw ebuilderValidationError('field_invalid', `${field} 必须是布尔值`);
88
+ }
89
+ export function intValue(value, field, options = {}) {
90
+ const raw = value === undefined || value === null || value === '' ? options.fallback : value;
91
+ if (raw === undefined)
92
+ throw ebuilderValidationError('field_required', `${field} 必填`);
93
+ const numeric = Number(raw);
94
+ if (!Number.isInteger(numeric))
95
+ throw ebuilderValidationError('field_invalid', `${field} 必须是整数`);
96
+ if (options.min !== undefined && numeric < options.min)
97
+ throw ebuilderValidationError('field_invalid', `${field} 不能小于 ${options.min}`);
98
+ if (options.max !== undefined && numeric > options.max)
99
+ throw ebuilderValidationError('field_invalid', `${field} 不能大于 ${options.max}`);
100
+ return numeric;
101
+ }
102
+ export function assertKnownKeys(input, allowed, operation) {
103
+ const unknown = Object.keys(input).filter((key) => !allowed.includes(key));
104
+ if (unknown.length > 0) {
105
+ throw ebuilderValidationError('unknown_field', `${operation} 不支持字段:${unknown.join('、')}`, { unknown });
106
+ }
107
+ }
108
+ export function assertNoForbiddenKeys(value, location = 'input') {
109
+ if (!value || typeof value !== 'object')
110
+ return;
111
+ if (Array.isArray(value)) {
112
+ value.forEach((item, index) => assertNoForbiddenKeys(item, `${location}[${index}]`));
113
+ return;
114
+ }
115
+ for (const [key, child] of Object.entries(value)) {
116
+ if (FORBIDDEN_KEYS.has(key) || FORBIDDEN_KEYS.has(key.toLowerCase())) {
117
+ throw ebuilderValidationError('field_forbidden', `禁止业务输入字段:${location}.${key}`);
118
+ }
119
+ assertNoForbiddenKeys(child, `${location}.${key}`);
120
+ }
121
+ }
122
+ export function compactJson(value) {
123
+ return JSON.stringify(value);
124
+ }
125
+ export function sha256VisibleFingerprint(value) {
126
+ const json = compactJson(value);
127
+ let hash = 0;
128
+ for (let index = 0; index < json.length; index += 1) {
129
+ hash = ((hash << 5) - hash + json.charCodeAt(index)) | 0;
130
+ }
131
+ return `len:${json.length}:h:${(hash >>> 0).toString(16)}`;
132
+ }
@@ -0,0 +1,19 @@
1
+ export function ebuilderSuccess(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.length > 0)
6
+ payload.warnings = warnings;
7
+ return payload;
8
+ }
9
+ export function schemaOf(properties, options = {}) {
10
+ return {
11
+ type: 'object',
12
+ additionalProperties: false,
13
+ properties,
14
+ ...(options.required?.length ? { required: options.required } : {}),
15
+ ...(options.description ? { description: options.description } : {}),
16
+ ...(options.oneOf ? { oneOf: options.oneOf } : {}),
17
+ ...(options.anyOf ? { anyOf: options.anyOf } : {}),
18
+ };
19
+ }
@@ -0,0 +1,277 @@
1
+ import { ebuilderConfirmationError, ebuilderValidationError } from '../errors.js';
2
+ import { ebuilderSuccess } from './types.js';
3
+ import { asObject, assertKnownKeys, assertNoForbiddenKeys, optionalArray, optionalObject, optionalString, requiredArray, requiredString, sha256VisibleFingerprint, } from './shared.js';
4
+ const CONTEXT_KEYS = [
5
+ 'context',
6
+ 'menuKeywords',
7
+ 'menu',
8
+ 'menuId',
9
+ 'url',
10
+ 'appId',
11
+ 'apiPrefix',
12
+ 'objId',
13
+ 'listId',
14
+ 'hostPageId',
15
+ 'listKind',
16
+ 'menuUrl',
17
+ 'entryUrl',
18
+ ];
19
+ export class EbuilderOpenapiPrepareOperation {
20
+ endpoint;
21
+ name;
22
+ applyName;
23
+ constructor(endpoint) {
24
+ this.endpoint = endpoint;
25
+ this.name = `ebuilder-form.openapi.${endpoint}.prepare`;
26
+ this.applyName = `ebuilder-form.openapi.${endpoint}.apply`;
27
+ }
28
+ async execute(input, env) {
29
+ const allowed = [
30
+ ...CONTEXT_KEYS,
31
+ 'dataId',
32
+ 'mainTable',
33
+ 'datas',
34
+ 'operationinfo',
35
+ ];
36
+ assertKnownKeys(input, allowed, this.name);
37
+ assertNoForbiddenKeys(input);
38
+ const prepared = await prepareOpenapi(this.endpoint, input, env.host);
39
+ const session = await env.host.getSession();
40
+ const continuation = await env.continuation.issue({
41
+ operation: this.applyName,
42
+ actor: { userId: session.userId, baseUrl: session.baseUrl, profile: session.profile },
43
+ kind: 'openapi',
44
+ endpoint: this.endpoint,
45
+ datajson: { ...prepared.datajson, __context: prepared.context },
46
+ target: prepared.target,
47
+ fingerprint: sha256VisibleFingerprint(prepared.datajson),
48
+ });
49
+ return ebuilderSuccess(this.name, {
50
+ context: prepared.context,
51
+ target: prepared.target,
52
+ requestPreview: { datajson: prepared.datajson },
53
+ continuation,
54
+ expiresInSeconds: 600,
55
+ }, { status: 'PREPARED' }, prepared.warnings);
56
+ }
57
+ }
58
+ export class EbuilderOpenapiApplyOperation {
59
+ endpoint;
60
+ name;
61
+ constructor(endpoint) {
62
+ this.endpoint = endpoint;
63
+ this.name = `ebuilder-form.openapi.${endpoint}.apply`;
64
+ }
65
+ async execute(input, env) {
66
+ assertKnownKeys(input, ['confirm', 'continuation'], this.name);
67
+ if (input.confirm !== true)
68
+ throw ebuilderConfirmationError(this.name);
69
+ const record = await env.continuation.verify(requiredString(input.continuation, 'continuation'), this.name);
70
+ assertContinuation(record, await env.host.getSession(), 'openapi', this.endpoint);
71
+ const datajson = asObject(record.datajson, 'continuation.datajson');
72
+ const result = await env.host.openapiWrite(this.endpoint, datajson, this.name);
73
+ return ebuilderSuccess(this.name, summarizeWriteResult(result, env.host, this.endpoint === 'delete'), { status: 'COMPLETE' });
74
+ }
75
+ }
76
+ export class EbuilderCustomPrepareOperation {
77
+ endpoint;
78
+ name;
79
+ applyName;
80
+ constructor(endpoint) {
81
+ this.endpoint = endpoint;
82
+ this.name = `ebuilder-form.custom.${endpoint}.prepare`;
83
+ this.applyName = `ebuilder-form.custom.${endpoint}.apply`;
84
+ }
85
+ async execute(input, env) {
86
+ const allowed = [
87
+ ...CONTEXT_KEYS,
88
+ 'interfacePk',
89
+ 'dataIds',
90
+ 'datas',
91
+ 'operationinfo',
92
+ ];
93
+ assertKnownKeys(input, allowed, this.name);
94
+ assertNoForbiddenKeys(input);
95
+ const prepared = await prepareCustom(this.endpoint, input, env.host);
96
+ const session = await env.host.getSession();
97
+ const continuation = await env.continuation.issue({
98
+ operation: this.applyName,
99
+ actor: { userId: session.userId, baseUrl: session.baseUrl, profile: session.profile },
100
+ kind: 'custom',
101
+ endpoint: this.endpoint,
102
+ interfacePk: prepared.interfacePk,
103
+ datajson: { ...prepared.datajson, __context: prepared.context },
104
+ target: prepared.target,
105
+ fingerprint: sha256VisibleFingerprint(prepared.datajson),
106
+ });
107
+ return ebuilderSuccess(this.name, {
108
+ context: prepared.context,
109
+ interfacePk: prepared.interfacePk,
110
+ target: prepared.target,
111
+ requestPreview: { datajson: prepared.datajson },
112
+ continuation,
113
+ expiresInSeconds: 600,
114
+ }, { status: 'PREPARED' }, prepared.warnings);
115
+ }
116
+ }
117
+ export class EbuilderCustomApplyOperation {
118
+ endpoint;
119
+ name;
120
+ constructor(endpoint) {
121
+ this.endpoint = endpoint;
122
+ this.name = `ebuilder-form.custom.${endpoint}.apply`;
123
+ }
124
+ async execute(input, env) {
125
+ assertKnownKeys(input, ['confirm', 'continuation'], this.name);
126
+ if (input.confirm !== true)
127
+ throw ebuilderConfirmationError(this.name);
128
+ const record = await env.continuation.verify(requiredString(input.continuation, 'continuation'), this.name);
129
+ assertContinuation(record, await env.host.getSession(), 'custom', this.endpoint);
130
+ const datajson = asObject(record.datajson, 'continuation.datajson');
131
+ const interfacePk = requiredString(record.interfacePk, 'continuation.interfacePk');
132
+ const result = await env.host.customWrite(this.endpoint, interfacePk, datajson, this.name);
133
+ return ebuilderSuccess(this.name, summarizeWriteResult(result, env.host, this.endpoint === 'delete'), { status: 'COMPLETE' });
134
+ }
135
+ }
136
+ async function prepareOpenapi(endpoint, input, host) {
137
+ const context = await host.resolveContext(input);
138
+ const operationinfo = optionalObject(input.operationinfo, 'operationinfo') || {};
139
+ if (endpoint === 'add') {
140
+ const datas = requiredArray(input.datas, 'datas');
141
+ return {
142
+ context,
143
+ datajson: {
144
+ datas,
145
+ header: { objId: context.objId },
146
+ operationinfo: {
147
+ fieldNoFindIgnore: 'false',
148
+ checkRight: 'true',
149
+ doAction: 'false',
150
+ printCost: 'true',
151
+ ...operationinfo,
152
+ },
153
+ },
154
+ target: { endpoint, objId: context.objId, count: datas.length },
155
+ warnings: fieldKeyWarnings(datas),
156
+ };
157
+ }
158
+ if (endpoint === 'update') {
159
+ const datas = requiredArray(input.datas, 'datas');
160
+ return {
161
+ context,
162
+ datajson: {
163
+ datas,
164
+ header: { objId: context.objId },
165
+ operationinfo: {
166
+ updateType: 'ids',
167
+ needAdd: 'false',
168
+ fieldNoFindIgnore: 'false',
169
+ checkRight: 'true',
170
+ doAction: 'false',
171
+ printCost: 'true',
172
+ ...operationinfo,
173
+ },
174
+ },
175
+ target: { endpoint, objId: context.objId, dataIds: collectDataIds(datas) },
176
+ warnings: fieldKeyWarnings(datas),
177
+ };
178
+ }
179
+ const mainTable = {
180
+ ...(optionalObject(input.mainTable, 'mainTable') || {}),
181
+ ...(input.dataId ? { id: requiredString(input.dataId, 'dataId') } : {}),
182
+ };
183
+ if (Object.keys(mainTable).length === 0) {
184
+ throw ebuilderValidationError('delete_target_required', '删除必须提供 dataId 或 mainTable 条件');
185
+ }
186
+ return {
187
+ context,
188
+ datajson: {
189
+ header: { objId: context.objId },
190
+ mainTable,
191
+ operationinfo: {
192
+ fieldNoFindIgnore: 'false',
193
+ checkRight: 'true',
194
+ doAction: 'false',
195
+ printCost: 'true',
196
+ ...operationinfo,
197
+ },
198
+ },
199
+ target: { endpoint, objId: context.objId, mainTable },
200
+ warnings: ['删除不可逆;apply 前必须确认 objId、删除条件和当前操作人均正确'],
201
+ };
202
+ }
203
+ async function prepareCustom(endpoint, input, host) {
204
+ const context = await host.resolveContext(input);
205
+ const interfacePk = requiredString(input.interfacePk, 'interfacePk');
206
+ const operationinfo = {
207
+ printCost: false,
208
+ asyncPostProcess: false,
209
+ asyncMark: false,
210
+ ...(optionalObject(input.operationinfo, 'operationinfo') || {}),
211
+ };
212
+ const session = await host.getSession();
213
+ if (endpoint === 'delete') {
214
+ const dataIds = requiredArray(input.dataIds, 'dataIds').map((item) => requiredString(item, 'dataIds[]'));
215
+ return {
216
+ context,
217
+ interfacePk,
218
+ datajson: {
219
+ datas: dataIds.map((id) => ({ mainTable: { id } })),
220
+ header: { operator: session.userId },
221
+ },
222
+ target: { endpoint, interfacePk, dataIds },
223
+ warnings: ['删除不可逆;apply 前必须确认 interfacePk、目标 dataId 和当前操作人均正确'],
224
+ };
225
+ }
226
+ const datas = requiredArray(input.datas, 'datas');
227
+ return {
228
+ context,
229
+ interfacePk,
230
+ datajson: {
231
+ operationinfo,
232
+ datas,
233
+ header: { operator: session.userId },
234
+ },
235
+ target: { endpoint, interfacePk, count: datas.length, dataIds: collectDataIds(datas) },
236
+ warnings: fieldKeyWarnings(datas),
237
+ };
238
+ }
239
+ function assertContinuation(record, session, kind, endpoint) {
240
+ if (record.kind !== kind || record.endpoint !== endpoint) {
241
+ throw ebuilderValidationError('continuation_mismatch', 'continuation 的接口族或 operation 与当前 apply 不一致');
242
+ }
243
+ const actor = asObject(record.actor, 'continuation.actor');
244
+ if (actor.userId !== session.userId || actor.baseUrl !== session.baseUrl) {
245
+ throw ebuilderValidationError('continuation_actor_changed', 'continuation 签发时的登录环境或用户与当前会话不一致,请重新执行 prepare');
246
+ }
247
+ }
248
+ function summarizeWriteResult(result, host, deleted) {
249
+ const context = asObject(result.context || {}, 'context');
250
+ const payload = asObject(result.result || {}, 'result');
251
+ const dataIds = Array.isArray(payload.dataIds) ? payload.dataIds : [];
252
+ return {
253
+ context,
254
+ result: payload,
255
+ records: dataIds.map((dataId) => ({
256
+ dataId,
257
+ url: host.buildDataUrl(context, dataId, deleted),
258
+ })),
259
+ listUrl: host.buildListUrl(context),
260
+ };
261
+ }
262
+ function collectDataIds(datas) {
263
+ return datas
264
+ .map((item) => optionalString(asObject(item, 'datas[]').mainTable?.id, 'mainTable.id'))
265
+ .filter((item) => Boolean(item));
266
+ }
267
+ function fieldKeyWarnings(datas) {
268
+ const warnings = [];
269
+ const text = JSON.stringify(datas);
270
+ if (/"\d{16,}"/u.test(text)) {
271
+ warnings.push('检测到长数字字段键;如果字段定义中 name 等于 id,请确认写入键已改用 config.dataKey,否则 eBuilder 可能返回成功但字段值为空');
272
+ }
273
+ if (optionalArray(datas, 'datas')?.some((item) => JSON.stringify(item).includes('"fieldNoFindIgnore"'))) {
274
+ warnings.push('fieldNoFindIgnore 应放在 operationinfo;字段名错误不能通过忽略字段绕过');
275
+ }
276
+ return warnings;
277
+ }
@@ -0,0 +1,8 @@
1
+ import { getEbuilderFormOperation } from './operations/registry.js';
2
+ import { asObject, assertNoForbiddenKeys } from './operations/shared.js';
3
+ export async function executeEbuilderFormOperation(operation, rawInput, context) {
4
+ const handler = getEbuilderFormOperation(operation);
5
+ const input = asObject(rawInput);
6
+ assertNoForbiddenKeys(input);
7
+ return handler.execute(input, context);
8
+ }
@@ -1,6 +1,6 @@
1
1
  import { ehrValidationError } from '../errors.js';
2
- import { READ, SCHEMA_DATE, SCHEMA_ID, SCHEMA_ID_LIST, SCHEMA_MONTH, SCHEMA_STRING_LIST, ehrSuccess, schemaOf, } from './types.js';
3
- import { assertEnum, assertMonth, assertMonthRange, compact, normalizePaged, pickDefined, resolveSecondAuthToken, toIdList, toNumber, toPageQuery, } from './shared.js';
2
+ import { READ, SCHEMA_ID, SCHEMA_MONTH, ehrSuccess, schemaOf, } from './types.js';
3
+ import { assertMonth, compact, normalizePaged, resolveSecondAuthToken, toNumber, toPageQuery, } from './shared.js';
4
4
  /**
5
5
  * 薪资所属月范围。页面实际传 2 个元素 [开始月, 结束月]。
6
6
  * salaryMonth 超过 2 项时服务端仅保留第 1 项,因此在入口侧直接拒绝。
@@ -173,147 +173,9 @@ const salaryAdjustmentList = {
173
173
  return ehrSuccess('ehr.salary.adjustment.list', data, { page });
174
174
  },
175
175
  };
176
- const salarySiaccountQuery = {
177
- name: 'ehr.salary.siaccount.query',
178
- resource: 'salary',
179
- method: 'siaccount.query',
180
- action: 'ehr.salary.siaccount.query',
181
- summary: '社保台账详情查询(需薪酬社保台账权限);接口不分页且返回高度敏感数据,必须提供范围条件',
182
- capability: 'salary',
183
- risk: READ,
184
- inputSchema: schemaOf({
185
- startBillMonth: SCHEMA_MONTH,
186
- endBillMonth: SCHEMA_MONTH,
187
- paymentOrganizationIds: SCHEMA_ID_LIST,
188
- paymentAgencyIds: SCHEMA_ID_LIST,
189
- salaryAcctOrgIds: SCHEMA_ID_LIST,
190
- employeeIds: SCHEMA_ID_LIST,
191
- billStatus: { type: 'integer', enum: [0, 1], description: '0 未归档,1 已归档' },
192
- paymentStatusList: {
193
- type: 'array',
194
- items: { type: 'integer', enum: [0, 1, 2, 3, 4] },
195
- description: '0 正常缴纳,1 补缴,2 代缴,3 退差,4 补差',
196
- },
197
- }),
198
- handler: async (args, { host }) => {
199
- // 注意:这是月份区间(YYYY-MM),必须用 assertMonthRange;
200
- // 误用 assertDateRange 会按 YYYY-MM-DD 校验而报 date_invalid。
201
- assertMonthRange(args, 'startBillMonth', 'endBillMonth');
202
- const employeeIds = toIdList(args.employeeIds, 'employeeIds');
203
- const paymentOrganizationIds = toIdList(args.paymentOrganizationIds, 'paymentOrganizationIds');
204
- const paymentAgencyIds = toIdList(args.paymentAgencyIds, 'paymentAgencyIds');
205
- const salaryAcctOrgIds = toIdList(args.salaryAcctOrgIds, 'salaryAcctOrgIds');
206
- // 接口不分页且返回高度敏感数据,缺少任何范围条件都可能造成大范围敏感数据返回。
207
- const hasScope = Boolean((args.startBillMonth && args.endBillMonth)
208
- || employeeIds?.length
209
- || paymentOrganizationIds?.length
210
- || paymentAgencyIds?.length
211
- || salaryAcctOrgIds?.length);
212
- if (!hasScope) {
213
- throw ehrValidationError('siaccount_scope_required', '社保台账查询不分页且返回敏感数据,必须至少提供月份范围或人员/组织 ID 之一以收窄范围');
214
- }
215
- // 枚举必须在运行时校验:inputSchema 只是文档。
216
- // 否则非法值(如 billStatus=9)会被服务端静默忽略并返回空数组,
217
- // 调用方会误判为"没有数据",比直接报错更危险。
218
- const BILL_STATUS = ['0', '1'];
219
- const PAYMENT_STATUS = ['0', '1', '2', '3', '4'];
220
- const billStatus = args.billStatus === undefined
221
- ? undefined
222
- : Number(assertEnum(args.billStatus, BILL_STATUS, 'billStatus'));
223
- let paymentStatusList;
224
- if (args.paymentStatusList !== undefined) {
225
- if (!Array.isArray(args.paymentStatusList)) {
226
- throw ehrValidationError('enum_invalid', 'paymentStatusList 必须是数组');
227
- }
228
- paymentStatusList = args.paymentStatusList.map((item, index) => Number(assertEnum(item, PAYMENT_STATUS, `paymentStatusList[${index}]`)));
229
- }
230
- const body = {
231
- startBillMonth: args.startBillMonth,
232
- endBillMonth: args.endBillMonth,
233
- billStatus,
234
- paymentStatusList,
235
- paymentOrganizationIds,
236
- paymentAgencyIds,
237
- salaryAcctOrgIds,
238
- employeeIds,
239
- };
240
- const response = await host.request({
241
- method: 'POST',
242
- path: '/api/bs/hrmsalary/siaccount/detail/query',
243
- body: compact(body),
244
- });
245
- return ehrSuccess('ehr.salary.siaccount.query', normalizePaged(response).data);
246
- },
247
- };
248
- const salaryEmployeeList = {
249
- name: 'ehr.salary.employee.list',
250
- resource: 'salary',
251
- method: 'employee.list',
252
- action: 'ehr.salary.employee.list',
253
- summary: '员工薪资明细列表(需薪酬统计权限);按薪资所属月、人员、组织等条件分页查询',
254
- capability: 'salary',
255
- risk: READ,
256
- inputSchema: schemaOf({
257
- current: SCHEMA_ID,
258
- pageSize: SCHEMA_ID,
259
- keyword: { type: 'string' },
260
- username: { type: 'string' },
261
- jobNum: { type: 'string' },
262
- idNo: { type: 'string', description: '身份证号,敏感字段' },
263
- year: { type: 'integer', minimum: 1000, maximum: 9999 },
264
- salaryMonth: { type: 'array', items: SCHEMA_MONTH, maxItems: 2, description: '1 项单月,2 项闭区间;超过 2 项服务端仅保留第 1 项' },
265
- employeeType: { type: 'string', enum: ['ORGANIZATION', 'EXT_EMPLOYEE'] },
266
- salarySendType: { type: 'string', enum: ['normal', 'import'] },
267
- sortField: { type: 'string' },
268
- departmentIds: SCHEMA_ID_LIST,
269
- positionIds: SCHEMA_ID_LIST,
270
- ids: SCHEMA_ID_LIST,
271
- taxAgentIds: SCHEMA_ID_LIST,
272
- salarySobIds: SCHEMA_ID_LIST,
273
- personnelStatuss: SCHEMA_STRING_LIST,
274
- incomeCategories: SCHEMA_STRING_LIST,
275
- hiredate: { type: 'array', items: SCHEMA_DATE, minItems: 2, maxItems: 2, description: '入职日期闭区间,必须正好 2 项' },
276
- }),
277
- handler: async (args, { host }) => {
278
- const body = {
279
- ...toPageQuery(args),
280
- ...pickDefined(args, [
281
- 'keyword',
282
- 'username',
283
- 'jobNum',
284
- 'idNo',
285
- 'employeeType',
286
- 'salarySendType',
287
- 'sortField',
288
- 'personnelStatuss',
289
- 'incomeCategories',
290
- 'hiredate',
291
- ]),
292
- departmentIds: toIdList(args.departmentIds, 'departmentIds'),
293
- positionIds: toIdList(args.positionIds, 'positionIds'),
294
- ids: toIdList(args.ids, 'ids'),
295
- taxAgentIds: toIdList(args.taxAgentIds, 'taxAgentIds'),
296
- salarySobIds: toIdList(args.salarySobIds, 'salarySobIds'),
297
- };
298
- if (args.year !== undefined)
299
- body.year = toNumber(args.year, 'year');
300
- if (args.salaryMonth !== undefined) {
301
- body.salaryMonth = requireMonthRange(args.salaryMonth, 'salaryMonth');
302
- }
303
- const response = await host.request({
304
- method: 'POST',
305
- path: '/api/bs/salaryreport/salary/statistics/employee/salaryList',
306
- body: compact(body),
307
- });
308
- const { data, page } = normalizePaged(response);
309
- return ehrSuccess('ehr.salary.employee.list', data, { page });
310
- },
311
- };
312
176
  export const salaryOperations = [
313
177
  salaryBillList,
314
178
  salaryBillGet,
315
179
  salaryWelfareList,
316
180
  salaryAdjustmentList,
317
- salarySiaccountQuery,
318
- salaryEmployeeList,
319
181
  ];
@@ -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 { imValidationError } 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, 'im-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 createImContinuationSigner(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 imValidationError('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 imValidationError('continuation_invalid', 'continuation 签名校验失败');
54
+ let value;
55
+ try {
56
+ value = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
57
+ }
58
+ catch {
59
+ throw imValidationError('continuation_invalid', 'continuation 内容无法解析');
60
+ }
61
+ if (value.operation !== expectedOperation) {
62
+ throw imValidationError('continuation_mismatch', 'continuation 不属于当前操作');
63
+ }
64
+ if (!Number.isSafeInteger(value.expiresAt) || value.expiresAt < Date.now()) {
65
+ throw imValidationError('continuation_expired', 'continuation 已过期');
66
+ }
67
+ return value;
68
+ },
69
+ };
70
+ }