weaver-work-cli 0.1.7 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +11 -11
  2. package/dist/cmd/action.js +76 -0
  3. package/dist/cmd/index.js +2 -0
  4. package/dist/cmd/refs/index.js +103 -0
  5. package/dist/cmd/register.js +31 -0
  6. package/dist/cmd/root.js +1 -0
  7. package/dist/cmd/setup/index.js +25 -5
  8. package/dist/cmd/skills/index.js +37 -5
  9. package/dist/core/connector.js +54 -0
  10. package/dist/core/context.js +2 -1
  11. package/dist/core/input.js +48 -0
  12. package/dist/core/output.js +298 -3
  13. package/dist/core/refs.js +432 -0
  14. package/dist/core/state.js +26 -0
  15. package/dist/internal/e10/auth/commands.js +2 -2
  16. package/dist/internal/e10/auth/session.js +14 -0
  17. package/dist/internal/e10/auth/xiaoe.js +116 -51
  18. package/dist/internal/e10/request-runtime.js +5 -0
  19. package/dist/internal/skills/install.js +148 -28
  20. package/dist/internal/skills/reader.js +12 -9
  21. package/dist/shortcuts/blog/continuation.js +74 -0
  22. package/dist/shortcuts/blog/errors.js +60 -0
  23. package/dist/shortcuts/blog/host.js +175 -0
  24. package/dist/shortcuts/blog/index.js +324 -0
  25. package/dist/shortcuts/blog/manifest.js +415 -0
  26. package/dist/shortcuts/blog/operations/comment.js +125 -0
  27. package/dist/shortcuts/blog/operations/comments.js +31 -0
  28. package/dist/shortcuts/blog/operations/edit.js +131 -0
  29. package/dist/shortcuts/blog/operations/export.js +46 -0
  30. package/dist/shortcuts/blog/operations/like.js +80 -0
  31. package/dist/shortcuts/blog/operations/publish.js +157 -0
  32. package/dist/shortcuts/blog/operations/query-user.js +58 -0
  33. package/dist/shortcuts/blog/operations/query.js +96 -0
  34. package/dist/shortcuts/blog/operations/registry.js +54 -0
  35. package/dist/shortcuts/blog/operations/remind.js +176 -0
  36. package/dist/shortcuts/blog/operations/shared.js +330 -0
  37. package/dist/shortcuts/blog/operations/types.js +16 -0
  38. package/dist/shortcuts/blog/operations.js +9 -0
  39. package/dist/shortcuts/caizhilian/continuation.js +70 -0
  40. package/dist/shortcuts/caizhilian/errors.js +53 -0
  41. package/dist/shortcuts/caizhilian/host.js +190 -0
  42. package/dist/shortcuts/caizhilian/index.js +186 -0
  43. package/dist/shortcuts/caizhilian/manifest.js +42 -0
  44. package/dist/shortcuts/caizhilian/operations/api.js +801 -0
  45. package/dist/shortcuts/caizhilian/operations/confirm.js +75 -0
  46. package/dist/shortcuts/caizhilian/operations/files.js +306 -0
  47. package/dist/shortcuts/caizhilian/operations/form.js +170 -0
  48. package/dist/shortcuts/caizhilian/operations/handlers/collab.js +611 -0
  49. package/dist/shortcuts/caizhilian/operations/handlers/document.js +890 -0
  50. package/dist/shortcuts/caizhilian/operations/handlers/file-collection.js +346 -0
  51. package/dist/shortcuts/caizhilian/operations/handlers/file-upload.js +192 -0
  52. package/dist/shortcuts/caizhilian/operations/handlers/fileops.js +570 -0
  53. package/dist/shortcuts/caizhilian/operations/handlers/folder.js +969 -0
  54. package/dist/shortcuts/caizhilian/operations/handlers/search.js +318 -0
  55. package/dist/shortcuts/caizhilian/operations/handlers/setting-attach.js +294 -0
  56. package/dist/shortcuts/caizhilian/operations/handlers/setting-backup.js +423 -0
  57. package/dist/shortcuts/caizhilian/operations/handlers/setting-encrypt.js +238 -0
  58. package/dist/shortcuts/caizhilian/operations/handlers/setting-storage.js +364 -0
  59. package/dist/shortcuts/caizhilian/operations/handlers/system.js +106 -0
  60. package/dist/shortcuts/caizhilian/operations/registry.js +151 -0
  61. package/dist/shortcuts/caizhilian/operations/shared.js +408 -0
  62. package/dist/shortcuts/caizhilian/operations/specs.js +623 -0
  63. package/dist/shortcuts/caizhilian/operations/types.js +57 -0
  64. package/dist/shortcuts/caizhilian/operations.js +21 -0
  65. package/dist/shortcuts/contract/continuation.js +70 -0
  66. package/dist/shortcuts/contract/errors.js +62 -0
  67. package/dist/shortcuts/contract/host.js +205 -0
  68. package/dist/shortcuts/contract/index.js +318 -0
  69. package/dist/shortcuts/contract/manifest.js +596 -0
  70. package/dist/shortcuts/contract/operations/clause-reference.js +78 -0
  71. package/dist/shortcuts/contract/operations/form-info.js +371 -0
  72. package/dist/shortcuts/contract/operations/read.js +118 -0
  73. package/dist/shortcuts/contract/operations/registry.js +43 -0
  74. package/dist/shortcuts/contract/operations/request.js +300 -0
  75. package/dist/shortcuts/contract/operations/shared.js +133 -0
  76. package/dist/shortcuts/contract/operations/subject.js +157 -0
  77. package/dist/shortcuts/contract/operations/template.js +81 -0
  78. package/dist/shortcuts/contract/operations/types.js +7 -0
  79. package/dist/shortcuts/contract/operations/workflow.js +52 -0
  80. package/dist/shortcuts/contract/operations/write.js +131 -0
  81. package/dist/shortcuts/contract/operations.js +8 -0
  82. package/dist/shortcuts/hrm/index.js +83 -2
  83. package/dist/shortcuts/hrm/operations/employee.js +160 -30
  84. package/dist/shortcuts/hrm/operations/org.js +17 -3
  85. package/dist/shortcuts/hrm/operations/position.js +18 -2
  86. package/dist/shortcuts/index.js +5 -1
  87. package/dist/shortcuts/invoice/index.js +74 -36
  88. package/dist/shortcuts/invoice/manifest.js +674 -4
  89. package/dist/shortcuts/invoice/markdown.js +172 -0
  90. package/dist/shortcuts/invoice/operations/add.js +3 -3
  91. package/dist/shortcuts/invoice/operations/delete.js +2 -2
  92. package/dist/shortcuts/invoice/operations/get.js +10 -2
  93. package/dist/shortcuts/invoice/operations/import.js +2 -2
  94. package/dist/shortcuts/invoice/operations/registry.js +8 -1
  95. package/dist/shortcuts/invoice/operations/reim-auto.js +719 -0
  96. package/dist/shortcuts/invoice/operations/reim.js +85 -2
  97. package/dist/shortcuts/invoice/operations/shared.js +75 -8
  98. package/dist/shortcuts/invoice/operations/update.js +2 -2
  99. package/dist/shortcuts/invoice/operations/upload-and-validate.js +93 -0
  100. package/dist/shortcuts/invoice/operations/validate-preview.js +2 -1
  101. package/dist/shortcuts/invoice/projection.js +328 -0
  102. package/dist/shortcuts/meeting/host.js +18 -1
  103. package/dist/shortcuts/meeting/manifest.js +30 -18
  104. package/dist/shortcuts/meeting/operations/eb/catalog.js +390 -0
  105. package/dist/shortcuts/meeting/operations/eb/conflict.js +389 -0
  106. package/dist/shortcuts/meeting/operations/eb/factory.js +389 -0
  107. package/dist/shortcuts/meeting/operations/eb/index.js +22 -0
  108. package/dist/shortcuts/meeting/operations/env.js +8 -1
  109. package/dist/shortcuts/meeting/operations/registry.js +2 -0
  110. package/dist/shortcuts/meeting/operations/signs.js +216 -2
  111. package/dist/shortcuts/workflow/endpoints.js +2 -1
  112. package/dist/shortcuts/workflow/errors.js +8 -2
  113. package/dist/shortcuts/workflow/host.js +6 -3
  114. package/dist/shortcuts/workflow/operations/form.js +931 -533
  115. package/dist/shortcuts/workflow/operations/read.js +462 -81
  116. package/dist/shortcuts/workflow/operations/shared.js +44 -0
  117. package/dist/shortcuts/workflow/operations/write.js +760 -56
  118. package/dist/shortcuts/workflow/operations.js +36 -10
  119. package/docs/_catalog.md +6 -0
  120. package/docs/agent-invoice.md +5 -1
  121. package/docs/agent-io-channel-design.md +468 -0
  122. package/docs/agent-skill-install.md +9 -4
  123. package/docs/blog.md +123 -0
  124. package/docs/caizhilian.md +304 -0
  125. package/docs/contract.md +91 -0
  126. package/docs/e10-auth.md +2 -2
  127. package/docs/hrm.md +19 -9
  128. package/docs/invoice.md +5 -1
  129. package/docs/meeting.md +1 -1
  130. package/docs/operation-manual.md +15 -3
  131. package/docs/skill-review-2026-09-19.md +2 -2
  132. package/package.json +2 -2
  133. package/scripts/package-skill.mjs +33 -20
  134. package/skill-template/business-info.json +113 -14
  135. package/skill-template/domains/blog.md +24 -0
  136. package/skill-template/domains/caizhilian.md +22 -0
  137. package/skill-template/domains/jinchengda.md +23 -0
  138. package/skill-template/domains/meeting.md +1 -1
  139. package/skill-template/domains/workflow.md +1 -1
  140. package/skill-template/skill-template.md +1 -1
  141. package/skills/weaver-e10-blog/SKILL.md +111 -0
  142. package/skills/weaver-e10-blog/references/blog-comment.md +51 -0
  143. package/skills/weaver-e10-blog/references/blog-comments.md +39 -0
  144. package/skills/weaver-e10-blog/references/blog-default-permission.md +38 -0
  145. package/skills/weaver-e10-blog/references/blog-disabled-capabilities.md +11 -0
  146. package/skills/weaver-e10-blog/references/blog-edit.md +51 -0
  147. package/skills/weaver-e10-blog/references/blog-export.md +42 -0
  148. package/skills/weaver-e10-blog/references/blog-like.md +50 -0
  149. package/skills/weaver-e10-blog/references/blog-publish.md +52 -0
  150. package/skills/weaver-e10-blog/references/blog-query-user.md +45 -0
  151. package/skills/weaver-e10-blog/references/blog-query.md +49 -0
  152. package/skills/weaver-e10-blog/references/blog-remind.md +51 -0
  153. package/skills/weaver-e10-blog/references/blog-resolve-users.md +38 -0
  154. package/skills/weaver-e10-blog/references/source-manifest.json +1540 -0
  155. package/skills/weaver-e10-caizhilian/SKILL.md +216 -0
  156. package/skills/weaver-e10-caizhilian/references/agent-entry.md +106 -0
  157. package/skills/weaver-e10-caizhilian/references/collaboration.md +112 -0
  158. package/skills/weaver-e10-caizhilian/references/document.md +111 -0
  159. package/skills/weaver-e10-caizhilian/references/file-collection.md +136 -0
  160. package/skills/weaver-e10-caizhilian/references/file-upload.md +309 -0
  161. package/skills/weaver-e10-caizhilian/references/files.md +120 -0
  162. package/skills/weaver-e10-caizhilian/references/folder.md +118 -0
  163. package/skills/weaver-e10-caizhilian/references/org-browser.md +104 -0
  164. package/skills/weaver-e10-caizhilian/references/setting-attach.md +410 -0
  165. package/skills/weaver-e10-caizhilian/references/setting-backup.md +372 -0
  166. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +399 -0
  167. package/skills/weaver-e10-caizhilian/references/setting-storage.md +603 -0
  168. package/skills/weaver-e10-caizhilian/references/source-manifest.json +5079 -0
  169. package/skills/weaver-e10-caizhilian/references/withheld.md +93 -0
  170. package/skills/weaver-e10-caizhilian/references/write-protocol.md +120 -0
  171. package/skills/weaver-e10-calendar/SKILL.md +4 -3
  172. package/skills/weaver-e10-ebuilder-form/SKILL.md +4 -9
  173. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +0 -9
  174. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +1 -5
  175. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +1 -5
  176. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +1 -5
  177. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +1 -5
  178. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +1 -5
  179. package/skills/weaver-e10-esb/SKILL.md +2 -2
  180. package/skills/weaver-e10-esb/references/source-manifest.json +9 -9
  181. package/skills/weaver-e10-hrm/SKILL.md +54 -6
  182. package/skills/weaver-e10-hrm/references/employee.md +89 -15
  183. package/skills/weaver-e10-hrm/references/org.md +7 -4
  184. package/skills/weaver-e10-hrm/references/position.md +14 -7
  185. package/skills/weaver-e10-hrm/references/source-manifest.json +1 -1
  186. package/skills/weaver-e10-jinchengda/SKILL.md +166 -0
  187. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +82 -0
  188. package/skills/weaver-e10-jinchengda/references/contract-clause-recommender.md +69 -0
  189. package/skills/weaver-e10-jinchengda/references/contract-clause-reference-save.md +78 -0
  190. package/skills/weaver-e10-jinchengda/references/contract-disabled-capabilities.md +27 -0
  191. package/skills/weaver-e10-jinchengda/references/contract-request-status.md +71 -0
  192. package/skills/weaver-e10-jinchengda/references/contract-request-write.md +94 -0
  193. package/skills/weaver-e10-jinchengda/references/contract-signing-subjects.md +89 -0
  194. package/skills/weaver-e10-jinchengda/references/contract-subject-create.md +77 -0
  195. package/skills/weaver-e10-jinchengda/references/contract-template-selector.md +66 -0
  196. package/skills/weaver-e10-jinchengda/references/contract-workflow-discovery.md +94 -0
  197. package/skills/weaver-e10-jinchengda/references/source-manifest.json +259 -0
  198. package/skills/weaver-e10-jiuchuanhui/SKILL.md +5 -11
  199. package/skills/weaver-e10-jucailin/SKILL.md +4 -3
  200. package/skills/weaver-e10-mail/SKILL.md +4 -11
  201. package/skills/weaver-e10-meeting/SKILL.md +66 -32
  202. package/skills/weaver-e10-meeting/references/meeting-change.md +5 -2
  203. package/skills/weaver-e10-meeting/references/meeting-create.md +2 -1
  204. package/skills/weaver-e10-meeting/references/meeting-eb.md +114 -0
  205. package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +9 -8
  206. package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +35 -7
  207. package/skills/weaver-e10-meeting/references/meeting-update.md +1 -1
  208. package/skills/weaver-e10-okr/SKILL.md +6 -11
  209. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  210. package/skills/weaver-e10-plan/SKILL.md +3 -3
  211. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +1 -1
  212. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +1 -1
  213. package/skills/weaver-e10-plan/references/source-manifest.json +13 -13
  214. package/skills/weaver-e10-qiyecheng/SKILL.md +4 -10
  215. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +9 -9
  216. package/skills/weaver-e10-shared/SKILL.md +36 -25
  217. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +10 -31
  218. package/skills/weaver-e10-shared/references/json-output-contract.md +20 -11
  219. package/skills/weaver-e10-shijingran/SKILL.md +2 -1
  220. package/skills/weaver-e10-shijingran/references/source-manifest.json +9 -9
  221. package/skills/weaver-e10-skill-maker/SKILL.md +4 -10
  222. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +5 -3
  223. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +12 -27
  224. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +17 -5
  225. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +4 -27
  226. package/skills/weaver-e10-wenshuding/SKILL.md +5 -16
  227. package/skills/weaver-e10-wenshuding/references/source-manifest.json +7 -7
  228. package/skills/weaver-e10-workflow/SKILL.md +32 -37
  229. package/skills/weaver-e10-workflow/references/source-manifest.json +40 -77
  230. package/skills/weaver-e10-workflow/references/workflow-batch.md +90 -29
  231. package/skills/weaver-e10-workflow/references/workflow-create.md +79 -109
  232. package/skills/weaver-e10-workflow/references/workflow-memory.md +19 -9
  233. package/skills/weaver-e10-workflow/references/workflow-operate.md +2 -2
  234. package/skills/weaver-e10-workflow/references/workflow-query.md +37 -20
  235. package/skills/weaver-e10-workflow/references/workflow-share.md +18 -2
  236. package/skills/weaver-e10-workflow/references/workflow-view.md +4 -4
  237. package/skills/weaver-e10-yepiaotong/SKILL.md +75 -39
  238. package/skills/weaver-e10-yepiaotong/references/invoice-detail.md +22 -1
  239. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +3 -1
  240. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +5 -5
  241. package/skills/weaver-e10-yepiaotong/references/invoice-output-view.md +135 -0
  242. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +15 -3
  243. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +1 -1
  244. package/skills/weaver-e10-yepiaotong/references/invoice-summarize.py +217 -0
  245. package/skills/weaver-e10-yepiaotong/references/invoice-upload-and-validate.md +88 -0
  246. package/skills/weaver-e10-yepiaotong/references/invoice-validation-preview.md +7 -2
  247. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +90 -10
  248. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +1 -0
  249. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +70 -49
  250. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +119 -0
  251. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +55 -36
  252. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +4 -0
  253. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +149 -0
  254. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +125 -5
  255. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +430 -63
  256. package/skills/weaver-e10-yimiaoban/SKILL.md +4 -12
  257. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
  258. package/skills/weaver-e10-ziguanjia/SKILL.md +2 -1
  259. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +3 -3
  260. package/tools/weaver-skill-detector-1.0.13/SKILL.md +9 -9
  261. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +3 -3
  262. package/tools/weaver-skill-detector-1.0.13/scripts/__pycache__/validate_skill.cpython-313.pyc +0 -0
  263. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +98 -91
  264. package/scripts/sync-connector-skills.mjs +0 -172
@@ -0,0 +1,7 @@
1
+ /** 写操作链路的统一状态名,与业票通保持一致。 */
2
+ export const CONTRACT_WORKFLOW = {
3
+ awaitingConfirmation: (operation) => ({ operation, status: 'AWAITING_CONFIRMATION' }),
4
+ executing: (operation) => ({ operation, status: 'EXECUTING' }),
5
+ verifying: (operation) => ({ operation, status: 'VERIFYING' }),
6
+ complete: (operation) => ({ operation, status: 'COMPLETE' }),
7
+ };
@@ -0,0 +1,52 @@
1
+ import { contractValidationError } from '../errors.js';
2
+ import { ContractReadOperation, projectWorkflowBase, projectWorkflowList } from './read.js';
3
+ import { queryFrom, requireId, requireNonEmptyString } from './shared.js';
4
+ import { contractWorkflowFormInfoOperation } from './form-info.js';
5
+ export const contractWorkflowQueryOperation = new ContractReadOperation('contract.workflow.query', {
6
+ method: 'POST',
7
+ path: '/api/workflow/pathdef/openapi/getBaseInfoByCondition',
8
+ userIdField: 'userid',
9
+ body: (input) => ({
10
+ workflowName: requireNonEmptyString(input.workflowName, 'workflowName'),
11
+ ...queryFrom(input, ['workflowType', 'status']),
12
+ }),
13
+ shape: projectWorkflowBase,
14
+ });
15
+ export const contractWorkflowListOperation = new ContractReadOperation('contract.workflow.list', {
16
+ method: 'POST',
17
+ path: '/api/workflow/list/paService/v1/getCreateWorkflowList',
18
+ userIdField: 'userid',
19
+ body: (input) => {
20
+ const wfpCode = Number(input.wfpCode);
21
+ if (wfpCode !== 0 && wfpCode !== 1) {
22
+ throw contractValidationError('wfp_code_invalid', 'wfpCode 只能是 0(个人审批)或 1(团队审批)');
23
+ }
24
+ return { wfpCode, ...queryFrom(input, ['filterHide']) };
25
+ },
26
+ shape: projectWorkflowList,
27
+ });
28
+ export const contractWorkflowFieldOptionsOperation = new ContractReadOperation('contract.workflow.field.options', {
29
+ method: 'POST',
30
+ path: '/api/workflow/core/paService/v1/getFieldOption',
31
+ userIdField: 'userid',
32
+ body: (input) => ({
33
+ fieldId: requireId(input.fieldId, 'fieldId'),
34
+ ...queryFrom(input, ['pageNo', 'pageSize']),
35
+ }),
36
+ shape: (value) => {
37
+ const data = (value.data ?? {});
38
+ const rows = Array.isArray(data.data) ? data.data : [];
39
+ return {
40
+ message: value.message,
41
+ total: data.total,
42
+ maxPage: data.maxPage,
43
+ options: rows.map((item) => ({ id: item?.id, name: item?.name })),
44
+ };
45
+ },
46
+ });
47
+ export const contractWorkflowOperations = [
48
+ contractWorkflowQueryOperation,
49
+ contractWorkflowListOperation,
50
+ contractWorkflowFormInfoOperation,
51
+ contractWorkflowFieldOptionsOperation,
52
+ ];
@@ -0,0 +1,131 @@
1
+ import { ContractCliError, contractConfirmationError, contractPartialError, contractValidationError } from '../errors.js';
2
+ import { objectFingerprint, sessionMatches } from './shared.js';
3
+ import { CONTRACT_WORKFLOW } from './types.js';
4
+ function baseNameOf(operation) {
5
+ return operation.replace(/\.(prepare|apply)$/u, '');
6
+ }
7
+ function defaultFingerprint(input) {
8
+ return objectFingerprint(input);
9
+ }
10
+ export class ContractWritePrepareOperation {
11
+ name;
12
+ applyName;
13
+ spec;
14
+ constructor(name, applyName, spec) {
15
+ this.name = name;
16
+ this.applyName = applyName;
17
+ this.spec = spec;
18
+ }
19
+ async execute(input, { host, continuation }) {
20
+ this.spec.validate?.(input);
21
+ const precheck = this.spec.precheck ? await this.spec.precheck(input, host) : undefined;
22
+ const session = await host.getSession();
23
+ const preview = this.spec.preview(input);
24
+ const baseName = baseNameOf(this.name);
25
+ const continuationToken = await continuation.issue({
26
+ operation: this.applyName,
27
+ path: this.spec.path,
28
+ baseUrl: session.baseUrl,
29
+ profile: session.profile,
30
+ userId: session.userId,
31
+ fingerprint: (this.spec.fingerprint ?? defaultFingerprint)(input),
32
+ preview,
33
+ });
34
+ return {
35
+ status: 'AWAITING_CONFIRMATION',
36
+ data: {
37
+ preview,
38
+ continuation: continuationToken,
39
+ expiresInSeconds: 600,
40
+ workflow: CONTRACT_WORKFLOW.awaitingConfirmation(baseName),
41
+ ...(precheck ? { precheck } : {}),
42
+ },
43
+ };
44
+ }
45
+ }
46
+ export class ContractWriteApplyOperation {
47
+ name;
48
+ spec;
49
+ constructor(name, spec) {
50
+ this.name = name;
51
+ this.spec = spec;
52
+ }
53
+ async execute(input, { host, continuation }) {
54
+ this.spec.validate?.(input);
55
+ if (input.confirm !== true) {
56
+ throw contractConfirmationError(this.name, { hint: '重新调用时传入 confirm=true 与 prepare 返回的 continuation' });
57
+ }
58
+ const lease = await continuation.verify(input.continuation, this.name);
59
+ if (typeof lease.fingerprint !== 'string') {
60
+ throw contractValidationError('continuation_invalid', 'continuation 缺少写入指纹,请重新 prepare');
61
+ }
62
+ const session = await host.getSession();
63
+ if (!sessionMatches(lease, session)) {
64
+ throw contractValidationError('context_mismatch', '确认上下文已变化,请重新 prepare');
65
+ }
66
+ if ((this.spec.fingerprint ?? defaultFingerprint)(input) !== lease.fingerprint) {
67
+ throw contractValidationError('target_changed', '待写入内容与确认时不一致,请重新 prepare');
68
+ }
69
+ const baseName = baseNameOf(this.name);
70
+ const body = {
71
+ ...(this.spec.defaults ?? {}),
72
+ ...this.spec.body(input),
73
+ };
74
+ let result;
75
+ try {
76
+ result = await host.contractRequest({
77
+ method: this.spec.method,
78
+ path: this.spec.path,
79
+ body,
80
+ ...(this.spec.userIdField ? { userIdField: this.spec.userIdField } : {}),
81
+ });
82
+ }
83
+ catch (error) {
84
+ if (error instanceof ContractCliError && this.spec.mapError) {
85
+ const mapped = this.spec.mapError(error);
86
+ if (mapped)
87
+ throw mapped;
88
+ }
89
+ if (error instanceof ContractCliError && error.type === 'network') {
90
+ throw contractPartialError('写请求发送后连接中断,结果不确定,禁止自动重试;先用查询接口确认是否已生效', {
91
+ preview: lease.preview,
92
+ workflow: CONTRACT_WORKFLOW.executing(baseName),
93
+ }, { stage: 'write' });
94
+ }
95
+ throw error;
96
+ }
97
+ if (!this.spec.readback) {
98
+ return {
99
+ status: 'COMPLETE',
100
+ data: {
101
+ result,
102
+ verified: false,
103
+ workflow: CONTRACT_WORKFLOW.complete(baseName),
104
+ },
105
+ };
106
+ }
107
+ let verified;
108
+ try {
109
+ verified = await this.spec.readback(result, input, host);
110
+ }
111
+ catch (error) {
112
+ if (error instanceof ContractCliError && error.type === 'partial')
113
+ throw error;
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ throw contractPartialError(`写入接口已返回成功,但回读校验失败,结果不确定:${message}`, {
116
+ preview: lease.preview,
117
+ result,
118
+ workflow: CONTRACT_WORKFLOW.verifying(baseName),
119
+ }, { stage: 'readback' });
120
+ }
121
+ return {
122
+ status: 'COMPLETE',
123
+ data: {
124
+ result,
125
+ verified: true,
126
+ workflow: CONTRACT_WORKFLOW.complete(baseName),
127
+ ...(verified ? { readback: verified } : {}),
128
+ },
129
+ };
130
+ }
131
+ }
@@ -0,0 +1,8 @@
1
+ import { getContractOperation } from './operations/registry.js';
2
+ import { asObject, assertNoForbiddenKeys } from './operations/shared.js';
3
+ export async function executeContractOperation(operation, rawInput, context) {
4
+ const handler = getContractOperation(operation);
5
+ const input = asObject(rawInput);
6
+ assertNoForbiddenKeys(input);
7
+ return handler.execute(input, context);
8
+ }
@@ -57,6 +57,51 @@ function parseJsonInput(raw, label) {
57
57
  throw hrmValidationError('input_invalid', `${label} 不是合法 JSON 对象`);
58
58
  }
59
59
  }
60
+ /** 解析 run-many 的 JSON 数组输入:[{ "op": "...", "input": {...} }, ...] */
61
+ export async function inputArrayFrom(options) {
62
+ const opts = asOptions(options);
63
+ if (opts.input && opts.inputJson) {
64
+ throw hrmValidationError('input_conflict', '不能同时传 --input 和 --input-json');
65
+ }
66
+ let raw;
67
+ if (opts.inputJson) {
68
+ raw = String(opts.inputJson);
69
+ }
70
+ else if (opts.input) {
71
+ try {
72
+ raw = stripBom(opts.input === '-' ? await readStdin() : await readFile(String(opts.input), 'utf8'));
73
+ }
74
+ catch {
75
+ throw hrmValidationError('input_unreadable', `无法读取 --input:${String(opts.input)}`);
76
+ }
77
+ }
78
+ else {
79
+ throw hrmValidationError('input_required', 'run-many 需要 --input 或 --input-json 提供 JSON 数组');
80
+ }
81
+ let parsed;
82
+ try {
83
+ parsed = JSON.parse(stripBom(raw));
84
+ }
85
+ catch {
86
+ throw hrmValidationError('input_invalid', 'run-many 输入不是合法 JSON');
87
+ }
88
+ if (!Array.isArray(parsed)) {
89
+ throw hrmValidationError('input_invalid', 'run-many 输入必须是数组:[{"op":"...","input":{...}}, ...]');
90
+ }
91
+ return parsed.map((item, index) => {
92
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
93
+ throw hrmValidationError('input_invalid', `run-many 第 ${index + 1} 项必须是对象`);
94
+ }
95
+ const obj = item;
96
+ if (typeof obj.op !== 'string' || !obj.op.trim()) {
97
+ throw hrmValidationError('input_invalid', `run-many 第 ${index + 1} 项缺少 op 字段`);
98
+ }
99
+ const input = obj.input && typeof obj.input === 'object' && !Array.isArray(obj.input)
100
+ ? obj.input
101
+ : {};
102
+ return { op: obj.op.trim(), input };
103
+ });
104
+ }
60
105
  export function successEnvelope(operation, result) {
61
106
  return {
62
107
  schemaVersion: 1,
@@ -96,8 +141,25 @@ export const hrmShortcut = {
96
141
  const hrm = program.command('hrm').description('泛微E10组织管理(员工查询/组织架构/岗位查询)');
97
142
  hrm.command('schema')
98
143
  .description('Print hrm operation manifest for agents')
99
- .action(action((ctx) => {
100
- ctx.output.write(hrmManifest, { json: true });
144
+ .option('--full', 'print full manifest with complete inputSchema')
145
+ .action(action((ctx, options) => {
146
+ if (asOptions(options).full === true) {
147
+ ctx.output.write(hrmManifest, { json: true });
148
+ return;
149
+ }
150
+ // 精简版:name + summary + 参数名 + 必填项,供 Agent 快速路由,避免全量 schema 撑爆上下文
151
+ const brief = {
152
+ schemaVersion: hrmManifest.schemaVersion,
153
+ name: hrmManifest.name,
154
+ version: hrmManifest.version,
155
+ operations: hrmManifest.operations.map((o) => ({
156
+ name: o.name,
157
+ summary: o.summary,
158
+ params: Object.keys(o.inputSchema?.properties ?? {}),
159
+ required: o.inputSchema?.required ?? [],
160
+ })),
161
+ };
162
+ ctx.output.write(brief, { json: true });
101
163
  }));
102
164
  hrm.command('run')
103
165
  .description('Run an hrm operation with JSON input')
@@ -107,5 +169,24 @@ export const hrmShortcut = {
107
169
  .action(action(async (ctx, operation, options) => {
108
170
  await executeAndPrint(ctx, String(operation), await inputFrom(options));
109
171
  }));
172
+ hrm.command('run-many')
173
+ .description('Run multiple hrm operations in a single process (shared session)')
174
+ .option('--input <path>', 'JSON array file, or - for stdin')
175
+ .option('--input-json <json>', 'inline JSON array')
176
+ .action(action(async (ctx, options) => {
177
+ const items = await inputArrayFrom(options);
178
+ const host = createHrmHost(ctx);
179
+ const results = [];
180
+ for (const item of items) {
181
+ try {
182
+ const result = await executeHrmOperation(item.op, item.input, { ctx, host });
183
+ results.push(successEnvelope(item.op, result));
184
+ }
185
+ catch (error) {
186
+ results.push({ operation: item.op, ...failureEnvelope(error) });
187
+ }
188
+ }
189
+ ctx.output.write({ schemaVersion: 1, ok: true, count: results.length, results }, { json: true });
190
+ }));
110
191
  },
111
192
  };
@@ -26,6 +26,125 @@ function formatMember(r, baseUrl) {
26
26
  deptLink: deptId ? orgLink(baseUrl, deptId) : '',
27
27
  };
28
28
  }
29
+ /**
30
+ * 人员卡片默认输出的核心字段白名单(顺序即输出顺序)。
31
+ * 完整卡片有 85 个顶层键、~5.3KB,其中大部分是登录/管理元数据(password、last_android、
32
+ * hideFieldList、setting、*_admin…),Agent 用不上;投影后 ~1.2KB。
33
+ * 需要完整卡片时传 `full: true`。
34
+ */
35
+ const DETAIL_CORE_FIELDS = [
36
+ 'id', 'name', 'username', 'job_num', 'sex', 'avatar',
37
+ 'personnel_status', 'status', 'type',
38
+ 'department', 'subcompany', 'organization', 'position', 'jobsetid', 'jobset_level',
39
+ 'superior', 'other_superior', 'only_superior', 'subordinate', 'assistant',
40
+ 'mobile', 'email', 'telephone', 'office', 'location', 'active_date',
41
+ ];
42
+ /** 实体数组字段(人员状态/部门/分部/岗位/职级/地点):只留 id + name。 */
43
+ const DETAIL_ENTITY_FIELDS = ['personnel_status', 'department', 'subcompany', 'position', 'jobsetid', 'jobset_level', 'location'];
44
+ /** 人员数组字段(上级/其他上级/助理):保留 id + name + statusCode + 瘦身头像。 */
45
+ const DETAIL_PERSON_FIELDS = ['superior', 'other_superior', 'assistant'];
46
+ /** 头像瘦身:原响应重复给出 7 个等价 URL(url/downloadUrl/appDownloadUrl/previewUrl/…)+ p1..p5,只留 1 个 url。 */
47
+ function slimAvatar(avatar) {
48
+ if (!avatar || typeof avatar !== 'object')
49
+ return avatar;
50
+ const a = avatar;
51
+ const out = {};
52
+ for (const key of ['url', 'name', 'userId']) {
53
+ if (a[key] !== undefined && a[key] !== '')
54
+ out[key] = a[key];
55
+ }
56
+ return out;
57
+ }
58
+ /** 实体数组瘦身:只留 id + name,丢掉 _entityType/icon/tenantKey/delete 等前端元数据。 */
59
+ function slimEntities(value) {
60
+ if (!Array.isArray(value))
61
+ return value;
62
+ return value.map((item) => {
63
+ if (!item || typeof item !== 'object')
64
+ return item;
65
+ const e = item;
66
+ return e.name === undefined ? { id: e.id } : { id: e.id, name: e.name };
67
+ });
68
+ }
69
+ /** 人员数组瘦身:id + name + statusCode + 瘦身头像。 */
70
+ function slimPersons(value) {
71
+ if (!Array.isArray(value))
72
+ return value;
73
+ return value.map((item) => {
74
+ if (!item || typeof item !== 'object')
75
+ return item;
76
+ const p = item;
77
+ const out = { id: p.id, name: p.name };
78
+ if (p.statusCode !== undefined)
79
+ out.statusCode = p.statusCode;
80
+ if (p.avatar !== undefined)
81
+ out.avatar = slimAvatar(p.avatar);
82
+ return out;
83
+ });
84
+ }
85
+ /** 默认精简投影:只保留核心业务字段(`full: true` 时跳过,直接回完整卡片)。 */
86
+ function pickDetailCore(raw) {
87
+ const out = {};
88
+ for (const key of DETAIL_CORE_FIELDS) {
89
+ const value = raw[key];
90
+ if (value === undefined || value === null)
91
+ continue;
92
+ if (DETAIL_PERSON_FIELDS.includes(key))
93
+ out[key] = slimPersons(value);
94
+ else if (DETAIL_ENTITY_FIELDS.includes(key))
95
+ out[key] = slimEntities(value);
96
+ else if (key === 'avatar')
97
+ out[key] = slimAvatar(value);
98
+ else
99
+ out[key] = value;
100
+ }
101
+ return out;
102
+ }
103
+ /**
104
+ * 「人员列表」精简行:只留 id + name(外加派生的 link,供 `[姓名](link)` 渲染)。
105
+ * 用于 `subordinate.mine` / `subordinate.by-id` / `dept.members` 的默认输出。
106
+ * 对比原始行:by-id 原始行单行 960B(其中上游的 superior 对象一项就占 485B)、
107
+ * `formatMember` 行 366B;精简后约 120B。需要部门/岗位/电话等完整行时传 `full: true`。
108
+ */
109
+ function formatPersonBrief(r, baseUrl) {
110
+ const id = r.id ?? r.employeeId;
111
+ return { id, name: r.name ?? r.username, link: personLink(baseUrl, id) };
112
+ }
113
+ /** 下属完整行(`full: true` 时用于 by-id):by-id 原始行的显式投影。 */
114
+ function formatSubordinateFull(r, baseUrl) {
115
+ const id = r.id ?? r.employeeId;
116
+ return {
117
+ id,
118
+ name: r.name ?? r.username,
119
+ pinyin: r.pinyin,
120
+ telephone: r.telephone,
121
+ sex: r.sex,
122
+ personnelStatus: r.personnelStatus,
123
+ department: r.department,
124
+ position: r.position,
125
+ superior: r.superior,
126
+ link: personLink(baseUrl, id),
127
+ };
128
+ }
129
+ /**
130
+ * `employee.query` 默认行:只回「人员信息」——人员 id / 姓名 / 部门 / 岗位 / 状态 + 卡片 link。
131
+ * 相对完整行(`formatMember`,368B)去掉的是:
132
+ * ① 另两套 id —— `userid`(登录信息 id)、`cid`(IM 联系人 id):查人场景用不到,且极易和人员 id 混用;
133
+ * ② `mobile`:本接口恒为空串(要脱敏手机号得用 `employee.batch`);
134
+ * ③ `departmentId` + `deptLink`:部门卡片链接,需要时用 `org.detail` 或传 `full: true`。
135
+ * 注意人员 id 是唯一应该传给 `employee.detail` / `employee.batch` / `subordinate.by-id` 的 id。
136
+ */
137
+ function formatQueryRowBrief(r, baseUrl) {
138
+ const id = r.id;
139
+ return {
140
+ id,
141
+ name: r.name,
142
+ department: r.department,
143
+ position: r.position,
144
+ statusCode: r.statusCode,
145
+ link: personLink(baseUrl, id),
146
+ };
147
+ }
29
148
  /** 批量详情行 → 精简人员行。 */
30
149
  function formatBatch(u, baseUrl) {
31
150
  const deptId = u.departmentId != null && u.departmentId !== '' ? String(u.departmentId) : '';
@@ -73,7 +192,7 @@ const employeeQuery = {
73
192
  resource: 'employee',
74
193
  method: 'employee.query',
75
194
  action: 'hrm.employee.query',
76
- summary: '按工号/姓名/手机/邮箱(keyword 自动推断或显式字段)查询员工;默认在职,status 传 ["6"] 查离职',
195
+ summary: '按工号/姓名/手机/邮箱(keyword 自动推断或显式字段)查询员工;默认在职,status 传 ["6"] 查离职;默认每行只回人员信息',
77
196
  risk: READ,
78
197
  inputSchema: schemaOf({
79
198
  keyword: { type: 'string', description: '查询关键词,自动推断:含 @→邮箱;11 位纯数字→手机;纯数字或字母开头含数字→工号;其余→姓名(模糊)' },
@@ -82,6 +201,7 @@ const employeeQuery = {
82
201
  mobile: { type: 'string', description: '手机号(模糊匹配)' },
83
202
  email: { type: 'string', description: '邮箱(模糊匹配)' },
84
203
  status: { type: 'array', items: { type: 'string', enum: [...EMPLOYEE_STATUS] }, description: '默认 ["normal"] 在职;["6"] 离职。不能单独作为收敛条件' },
204
+ full: { type: 'boolean', description: '默认 false:每行只回人员信息(id/name/department/position/statusCode/link);true:回完整行(另含 userid/cid/mobile/departmentId/deptLink)' },
85
205
  ...SCHEMA_PAGE,
86
206
  }),
87
207
  handler: async (args, { host }) => {
@@ -117,11 +237,13 @@ const employeeQuery = {
117
237
  body.pageSize = pageSize;
118
238
  const session = await host.getSession();
119
239
  const baseUrl = session.baseUrl.replace(/\/+$/, '');
120
- const countResp = await host.request({ method: 'POST', path: '/api/hrm/common/browser/count/resource', body });
240
+ // 不请求 count 接口(省一次 HTTP 往返);data 接口的 count 字段不可靠(恒 0),
241
+ // 用返回行数作为 total。收敛条件查询结果通常 < pageSize,行数即总数。
121
242
  const dataResp = await host.request({ method: 'POST', path: '/api/hrm/common/browser/data/resource', body });
122
- const total = Number(countResp?.data?.total ?? 0);
123
243
  const rawRows = dataResp?.data?.data ?? [];
124
- const rows = rawRows.map((r) => formatMember(r, baseUrl));
244
+ const total = rawRows.length;
245
+ // 默认只回人员信息(id/name/department/position/statusCode/link);完整行传 full: true。
246
+ const rows = rawRows.map((r) => (args.full === true ? formatMember(r, baseUrl) : formatQueryRowBrief(r, baseUrl)));
125
247
  const lookingNormal = statusText.includes('normal');
126
248
  const hint = lookingNormal && rows.length === 0
127
249
  ? `在职员工未查到「${value}」;若为离职人员,请用 status=["6"] 再查`
@@ -170,18 +292,30 @@ const employeeDetail = {
170
292
  resource: 'employee',
171
293
  method: 'employee.detail',
172
294
  action: 'hrm.employee.detail',
173
- summary: '按人员 id 取资料名片(头像/是否管理员/管理者类型/在线状态);不含姓名/工号/部门/手机',
295
+ summary: '按人员 id 取人员卡片(默认只回核心业务字段;full=true 回完整卡片 85 个顶层键)',
174
296
  risk: READ,
175
- inputSchema: schemaOf({ id: SCHEMA_ID }, { required: ['id'] }),
297
+ inputSchema: schemaOf({
298
+ id: SCHEMA_ID,
299
+ full: { type: 'boolean', description: '默认 false:只回核心业务字段(姓名/工号/组织/岗位/上级/助理/联系方式/办公地点等,体积约为完整卡片的 1/5);true:回完整人员卡片(含 password、last_android、hideFieldList、setting、*_admin 等登录/管理元数据)' },
300
+ }, { required: ['id'] }),
176
301
  handler: async (args, { host }) => {
177
302
  const id = String(args.id).trim();
178
303
  if (!id)
179
304
  throw hrmValidationError('id_required', 'id 必填');
180
- const resp = await host.request({ method: 'GET', path: '/api/hrm/profile/get', query: { id } });
181
- const data = resp?.data ?? {};
182
305
  const session = await host.getSession();
183
306
  const baseUrl = session.baseUrl.replace(/\/+$/, '');
184
- return hrmSuccess('hrm.employee.detail', { ...data, link: personLink(baseUrl, id) });
307
+ // readOnly=true 必需:非只读且非本人/非基础信息管理员时服务端会拒绝(checkCardHasRight)。
308
+ const resp = await host.request({
309
+ method: 'GET',
310
+ path: `/api/hrm/card/get/${id}`,
311
+ query: { id, readOnly: true },
312
+ headers: { Referer: `${baseUrl}/sp/hrm/profileInfo/${id}` },
313
+ });
314
+ // 第一层:只提取 data.data 的业务字段,砍掉 groups/layout/items 等前端表单元数据(几千行)。
315
+ const raw = resp?.data?.data ?? {};
316
+ // 第二层:默认再投影成核心字段(85 键/~5.3KB → 26 键/~1.2KB);full=true 时保留完整卡片。
317
+ const payload = args.full === true ? raw : pickDetailCore(raw);
318
+ return hrmSuccess('hrm.employee.detail', { ...payload, id, link: personLink(baseUrl, id) });
185
319
  },
186
320
  };
187
321
  const subordinateMine = {
@@ -189,10 +323,11 @@ const subordinateMine = {
189
323
  resource: 'employee',
190
324
  method: 'subordinate.mine',
191
325
  action: 'hrm.subordinate.mine',
192
- summary: '查当前登录人的下属(all 所有/direct 直接/indirect 间接),基于当前会话身份',
326
+ summary: '查当前登录人的下属(all 所有/direct 直接/indirect 间接),基于当前会话身份;默认每行只回 id+name',
193
327
  risk: READ,
194
328
  inputSchema: schemaOf({
195
329
  scope: { type: 'string', enum: [...SUBORDINATE_SCOPE], description: '默认 all;direct 直接 / indirect 间接 / all 所有' },
330
+ full: { type: 'boolean', description: '默认 false:每行只回 id/name/link;true:回完整行(部门/岗位/手机/状态等)' },
196
331
  ...SCHEMA_PAGE,
197
332
  }),
198
333
  handler: async (args, { host }) => {
@@ -217,11 +352,13 @@ const subordinateMine = {
217
352
  body.pageSize = pageSize;
218
353
  const session = await host.getSession();
219
354
  const baseUrl = session.baseUrl.replace(/\/+$/, '');
220
- const countResp = await host.request({ method: 'POST', path: '/api/hrm/common/browser/count/resource', body });
355
+ // 不请求 count 接口(省一次 HTTP 往返);data 接口的 count 字段不可靠(恒 0),
356
+ // 用返回行数作为 total。收敛条件查询结果通常 < pageSize,行数即总数。
221
357
  const dataResp = await host.request({ method: 'POST', path: '/api/hrm/common/browser/data/resource', body });
222
- const total = Number(countResp?.data?.total ?? 0);
223
358
  const rawRows = dataResp?.data?.data ?? [];
224
- const rows = rawRows.map((r) => formatMember(r, baseUrl));
359
+ const total = rawRows.length;
360
+ // 默认只回 id + name(+ link);需要部门/岗位/手机等完整行时传 full: true。
361
+ const rows = rawRows.map((r) => (args.full === true ? formatMember(r, baseUrl) : formatPersonBrief(r, baseUrl)));
225
362
  return hrmSuccess('hrm.subordinate.mine', { rows, total, scope }, { current: current ?? 1, pageSize: pageSize ?? 200 });
226
363
  },
227
364
  };
@@ -230,13 +367,14 @@ const subordinateById = {
230
367
  resource: 'employee',
231
368
  method: 'subordinate.by-id',
232
369
  action: 'hrm.subordinate.by-id',
233
- summary: '按目标人员 id 查其直接/间接下属(无需切换会话);所有下属 = direct + indirect 两次并集',
370
+ summary: '按目标人员 id 查其直接/间接下属(无需切换会话);默认每行只回 id+name;所有下属 = direct + indirect 两次并集',
234
371
  risk: READ,
235
372
  inputSchema: schemaOf({
236
373
  id: SCHEMA_ID,
237
374
  scope: { type: 'string', enum: [...SUBORDINATE_BYID_SCOPE], description: '默认 direct;direct 直接 / indirect 间接' },
238
- pageNo: SCHEMA_ID,
239
- pageSize: SCHEMA_ID,
375
+ full: { type: 'boolean', description: '默认 false:每行只回 id/name/link;true:回完整行(部门/岗位/上级/电话等)' },
376
+ pageNo: { ...SCHEMA_ID, description: '页码,从 1 开始。⚠️ 本操作用 pageNo(不是 current)' },
377
+ pageSize: { ...SCHEMA_ID, description: '每页条数,默认 10' },
240
378
  }, { required: ['id'] }),
241
379
  handler: async (args, { host }) => {
242
380
  const id = String(args.id).trim();
@@ -258,18 +396,8 @@ const subordinateById = {
258
396
  });
259
397
  const page = resp?.data?.page ?? {};
260
398
  const rawRows = Array.isArray(page.result) ? page.result : [];
261
- const rows = rawRows.map((r) => ({
262
- id: r.id ?? r.employeeId,
263
- name: r.name ?? r.username,
264
- pinyin: r.pinyin,
265
- telephone: r.telephone,
266
- sex: r.sex,
267
- personnelStatus: r.personnelStatus,
268
- department: r.department,
269
- position: r.position,
270
- superior: r.superior,
271
- link: personLink(baseUrl, r.id ?? r.employeeId),
272
- }));
399
+ // 默认只回 id + name(+ link);需要部门/岗位/上级/电话等完整行时传 full: true。
400
+ const rows = rawRows.map((r) => (args.full === true ? formatSubordinateFull(r, baseUrl) : formatPersonBrief(r, baseUrl)));
273
401
  return hrmSuccess('hrm.subordinate.by-id', {
274
402
  rows,
275
403
  total: Number(page.totalCount ?? rows.length),
@@ -288,10 +416,11 @@ const deptMembers = {
288
416
  resource: 'employee',
289
417
  method: 'dept.members',
290
418
  action: 'hrm.dept.members',
291
- summary: '查某部门/分部下的直接成员(部门名或 id;部门名需唯一,重名改用 id)',
419
+ summary: '查某部门/分部下的直接成员(部门名或 id;部门名需唯一,重名改用 id);默认每行只回 id+name',
292
420
  risk: READ,
293
421
  inputSchema: schemaOf({
294
422
  dept: { type: 'string', description: '部门/分部名称(需唯一)或组织节点 id' },
423
+ full: { type: 'boolean', description: '默认 false:每行只回 id/name/link;true:回完整行(部门/岗位/手机/状态/部门卡片链接等)' },
295
424
  ...SCHEMA_PAGE,
296
425
  }, { required: ['dept'] }),
297
426
  handler: async (args, { host }) => {
@@ -324,7 +453,8 @@ const deptMembers = {
324
453
  const rawRows = resp?.data?.data ?? [];
325
454
  const session = await host.getSession();
326
455
  const baseUrl = session.baseUrl.replace(/\/+$/, '');
327
- const rows = rawRows.map((r) => formatMember(r, baseUrl));
456
+ // 默认只回 id + name(+ link);需要部门/岗位/手机/状态等完整行时传 full: true。
457
+ const rows = rawRows.map((r) => (args.full === true ? formatMember(r, baseUrl) : formatPersonBrief(r, baseUrl)));
328
458
  return hrmSuccess('hrm.dept.members', { dept: { id: deptId }, rows, total: rows.length }, { current: current ?? 1, pageSize: pageSize ?? 200 });
329
459
  },
330
460
  };
@@ -1,6 +1,6 @@
1
1
  import { hrmValidationError } from '../errors.js';
2
2
  import { READ, SCHEMA_ID, SCHEMA_PAGE, hrmSuccess, schemaOf, } from './types.js';
3
- import { browserBaseBody, orgLink, toNumber, toPositiveInt, toStringIdList, } from './shared.js';
3
+ import { browserBaseBody, orgLink, pickDefined, toNumber, toPositiveInt, toStringIdList, } from './shared.js';
4
4
  const ORG_TYPES = ['subcompany', 'department'];
5
5
  /**
6
6
  * 组织浏览接口(分部 subcompany / 部门 department)统一入口。
@@ -100,8 +100,22 @@ const orgDetail = {
100
100
  query: { id, readOnly: true, includeSub: true, quote: true, isMobile: false },
101
101
  headers: { Referer: `${baseUrl}/sp/hrm/orgCard/${id}` },
102
102
  });
103
- const data = resp?.data ?? {};
104
- return hrmSuccess('hrm.org.detail', { ...data, link: orgLink(baseUrl, id) });
103
+ // 只提取 data.data 的业务字段,砍掉 groups/layout/items 等前端表单元数据(几千行)。
104
+ const raw = resp?.data?.data ?? {};
105
+ const parentName = Array.isArray(raw.parent) && raw.parent[0]?.name ? String(raw.parent[0].name) : '';
106
+ const subcompanyName = Array.isArray(raw.subcompany) && raw.subcompany[0]?.name ? String(raw.subcompany[0].name) : '';
107
+ return hrmSuccess('hrm.org.detail', {
108
+ id,
109
+ name: raw.name,
110
+ type: raw.type,
111
+ code: raw.code,
112
+ fullPath: raw.fullPath,
113
+ status: raw.status,
114
+ ...(parentName ? { parentName } : {}),
115
+ ...(subcompanyName ? { subcompanyName } : {}),
116
+ ...pickDefined(raw, ['description', 'remark']),
117
+ link: orgLink(baseUrl, id),
118
+ });
105
119
  },
106
120
  };
107
121
  export const hrmOrgOperations = [
@@ -100,8 +100,24 @@ const positionDetail = {
100
100
  query: { id, type: 'info' },
101
101
  headers: { Referer: `${baseUrl}/sp/hrm/positionCard/${id}` },
102
102
  });
103
- const data = resp?.data ?? {};
104
- return hrmSuccess('hrm.position.detail', { ...data, link: positionLink(baseUrl, id) });
103
+ // 只提取 data.data 的业务字段,砍掉 groups/layout/items 等前端表单元数据。
104
+ const raw = resp?.data?.data ?? {};
105
+ const parentName = Array.isArray(raw.parent) && raw.parent[0]?.name ? String(raw.parent[0].name) : '';
106
+ const jobsetName = Array.isArray(raw.jobsetid) && raw.jobsetid[0]?.name ? String(raw.jobsetid[0].name) : '';
107
+ const jobsetTypeName = Array.isArray(raw.jobset_type_id) && raw.jobset_type_id[0]?.name ? String(raw.jobset_type_id[0].name) : '';
108
+ const grade = Array.isArray(raw.grade)
109
+ ? raw.grade.map((g) => (g && typeof g === 'object' ? (g.name ?? g) : g))
110
+ : raw.grade;
111
+ return hrmSuccess('hrm.position.detail', {
112
+ id,
113
+ name: raw.name,
114
+ code: raw.code,
115
+ ...(parentName ? { parentName } : {}),
116
+ ...(jobsetName ? { jobsetName } : {}),
117
+ ...(jobsetTypeName ? { jobsetTypeName } : {}),
118
+ ...(grade !== undefined ? { grade } : {}),
119
+ link: positionLink(baseUrl, id),
120
+ });
105
121
  },
106
122
  };
107
123
  const positionList = {