weaver-work-cli 0.1.40 → 0.1.42

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 (185) hide show
  1. package/dist/chunks/{archive-JHTONRST.js → archive-J4I6MCKF.js} +2 -2
  2. package/dist/chunks/{archive-oa-FCHLCPHC.js → archive-oa-BRZKKTO4.js} +2 -2
  3. package/dist/chunks/{asset-RLIHC7DB.js → asset-L7D4FAOZ.js} +2 -2
  4. package/dist/chunks/{asset-idle-KVOFTLCP.js → asset-idle-DADLLV75.js} +2 -2
  5. package/dist/chunks/{attachment-content-RZYDB3MB.js → attachment-content-K4C7VLWQ.js} +3 -3
  6. package/dist/chunks/{blog-42OZTJNP.js → blog-BVGQ7NZL.js} +2 -2
  7. package/dist/chunks/{caizhilian-KRK547K5.js → caizhilian-SAADNFD6.js} +2 -2
  8. package/dist/chunks/{calendar-RTWAMKHD.js → calendar-Q7NQY5Z2.js} +61 -23
  9. package/dist/chunks/{chunk-LFMSDBZN.js → chunk-3KTFS4IA.js} +308 -153
  10. package/dist/chunks/{chunk-G2JSIUWF.js → chunk-4PD5NWAX.js} +89 -0
  11. package/dist/chunks/{chunk-T6EGVBKU.js → chunk-4RAE6JXP.js} +1 -1
  12. package/dist/chunks/{chunk-7BRU4EFH.js → chunk-72CKAUAG.js} +29 -2
  13. package/dist/chunks/{chunk-NETCCARD.js → chunk-APVFJUQH.js} +2 -2
  14. package/dist/chunks/{chunk-CAQNBBQA.js → chunk-BBKDXQEC.js} +2 -2
  15. package/dist/chunks/{chunk-57IECMWX.js → chunk-BIPYLLVS.js} +26 -12
  16. package/dist/chunks/{chunk-LN447PY2.js → chunk-EKWAQSLU.js} +3 -2
  17. package/dist/chunks/{chunk-4TEDNCON.js → chunk-FOAA27U5.js} +338 -52
  18. package/dist/chunks/{chunk-7U577Q7U.js → chunk-G4UR6NXN.js} +2 -2
  19. package/dist/chunks/{chunk-NJKREDA3.js → chunk-GEWYMQME.js} +1 -1
  20. package/dist/chunks/{chunk-I566ZYNF.js → chunk-GHXCDPTM.js} +1 -1
  21. package/dist/chunks/{chunk-4CTESSKI.js → chunk-IZ7S7QUS.js} +4 -4
  22. package/dist/chunks/{chunk-VNBU55SL.js → chunk-JPRO6ER2.js} +57 -2432
  23. package/dist/chunks/{chunk-IZSSVYAG.js → chunk-LADPYOWC.js} +2 -2
  24. package/dist/chunks/chunk-LDZQPUIA.js +1238 -0
  25. package/dist/chunks/chunk-NKZRQXST.js +2546 -0
  26. package/dist/chunks/{chunk-JWRZM2H2.js → chunk-QKNKR4RH.js} +3 -3
  27. package/dist/chunks/{chunk-FLS4DPGP.js → chunk-R4CHUSKC.js} +28 -15
  28. package/dist/chunks/{chunk-WFFNM65B.js → chunk-SA6VBIHY.js} +73 -19
  29. package/dist/chunks/{chunk-SPRHHSVC.js → chunk-TTPJJJC4.js} +2 -2
  30. package/dist/chunks/{chunk-7E6MUOEK.js → chunk-W7BEHOSR.js} +2 -2
  31. package/dist/chunks/{chunk-CXRQWOZ5.js → chunk-YRSZQUD2.js} +2 -2
  32. package/dist/chunks/{conflict-VR55AOOO.js → conflict-F2OMZRTO.js} +2 -2
  33. package/dist/chunks/{contract-HI4FNPYE.js → contract-5BETHRSG.js} +2 -2
  34. package/dist/chunks/{cowork-HJLI353D.js → cowork-3MN6EQQO.js} +22 -12
  35. package/dist/chunks/create-flow-5FQNNMVO.js +55 -0
  36. package/dist/chunks/{ebuilder-form-HTGI63UU.js → ebuilder-form-SBHOYBUD.js} +3 -3
  37. package/dist/chunks/{ehr-GYXIYH3A.js → ehr-KMOJULKG.js} +432 -1234
  38. package/dist/chunks/{env-DW3G4HUO.js → env-LB7NBL2O.js} +2 -2
  39. package/dist/chunks/{esb-I5INCEFE.js → esb-W2PJCFCX.js} +4 -4
  40. package/dist/chunks/{factory-TKXGXS6L.js → factory-6ZFQXQ46.js} +1 -1
  41. package/dist/chunks/{fna-K2MHTQ24.js → fna-ZQPCUV4R.js} +2 -2
  42. package/dist/chunks/{group-chat-VYBNMJLG.js → group-chat-2HHAJN46.js} +5 -3
  43. package/dist/chunks/{host-ZUQTJYYK.js → host-4CBHGOHT.js} +3 -3
  44. package/dist/chunks/{host-D5SERMAJ.js → host-ATKVNZAT.js} +3 -3
  45. package/dist/chunks/{host-JM4YVHFM.js → host-BQH22NTQ.js} +3 -3
  46. package/dist/chunks/{host-L3VLITMT.js → host-X2CSLFM6.js} +3 -3
  47. package/dist/chunks/{host-256GVCAH.js → host-YTODKMSL.js} +3 -3
  48. package/dist/chunks/{hrm-VLRBKTZB.js → hrm-FFM4UJFS.js} +4 -4
  49. package/dist/chunks/{im-QK3VTLXK.js → im-VEHKDQPB.js} +7 -7
  50. package/dist/chunks/{invoice-T5JTA32H.js → invoice-LVIKSIIF.js} +3 -3
  51. package/dist/chunks/{jiuchuanhui-P2FNEQVZ.js → jiuchuanhui-X6DINO5P.js} +2 -2
  52. package/dist/chunks/{lazy-dispatch-XRRI46M6.js → lazy-dispatch-KSWTN5L6.js} +4 -4
  53. package/dist/chunks/{mail-3Y7MJ6PS.js → mail-VDKRGUD3.js} +2 -2
  54. package/dist/chunks/{meeting-M7LTXGJE.js → meeting-CLC6HP3Z.js} +12 -11
  55. package/dist/chunks/{okr-OBC6S67L.js → okr-CHG4OO3U.js} +2 -2
  56. package/dist/chunks/{plan-5H6I53SK.js → plan-XWR75667.js} +2 -2
  57. package/dist/chunks/{project-L75QZV4S.js → project-5BXOOKMG.js} +40 -23
  58. package/dist/chunks/recruit-stage-DGMFNNDE.js +4507 -0
  59. package/dist/chunks/{registry-RXPN6BBT.js → registry-7FFOXIAW.js} +4 -4
  60. package/dist/chunks/{registry-KMHCPAAD.js → registry-BKBZPBAJ.js} +10 -9
  61. package/dist/chunks/{registry-QGHFNUWS.js → registry-OZSMDVPP.js} +2 -2
  62. package/dist/chunks/{registry-UGRBCBHO.js → registry-RHMW6ANW.js} +2 -2
  63. package/dist/chunks/{registry-NYPYMZXG.js → registry-Z43434VG.js} +1 -1
  64. package/dist/chunks/{room-recommend-A3UDC3CA.js → room-recommend-62H7ZQQ6.js} +4 -4
  65. package/dist/chunks/{schedule-5GCACCK7.js → schedule-FNXKH3DQ.js} +4 -4
  66. package/dist/chunks/{workflow-M5ZJ7DWF.js → workflow-ADHKCAJK.js} +6 -6
  67. package/dist/index.js +33 -28
  68. package/dist/internal/e10/auth/commands.js +1 -0
  69. package/dist/internal/e10/auth/messages.js +6 -0
  70. package/dist/internal/e10/auth/session.js +17 -0
  71. package/dist/internal/e10/auth/xiaoe.js +286 -44
  72. package/dist/internal/e10/auth-recovery.js +36 -5
  73. package/dist/internal/e10/context.js +56 -12
  74. package/dist/internal/e10/index.js +1 -1
  75. package/dist/internal/e10/network-error.js +93 -0
  76. package/dist/internal/e10/request-runtime.js +6 -0
  77. package/dist/shortcuts/calendar/operations/read.js +31 -22
  78. package/dist/shortcuts/calendar/operations/write.js +45 -1
  79. package/dist/shortcuts/cowork/operations/discuss-write.js +5 -1
  80. package/dist/shortcuts/cowork/operations/item-write.js +1 -7
  81. package/dist/shortcuts/cowork/operations/member-write.js +3 -1
  82. package/dist/shortcuts/cowork/operations/permission-write.js +5 -1
  83. package/dist/shortcuts/cowork/operations/shared.js +7 -0
  84. package/dist/shortcuts/ehr/operations/attend-write.js +43 -1
  85. package/dist/shortcuts/ehr/operations/hr-contact.js +7 -4
  86. package/dist/shortcuts/ehr/operations/hr-report.js +13 -7
  87. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +134 -59
  88. package/dist/shortcuts/ehr/operations/recruit-write.js +262 -17
  89. package/dist/shortcuts/ehr/operations/recruit.js +10 -4
  90. package/dist/shortcuts/ehr/operations/shared.js +47 -0
  91. package/dist/shortcuts/hrm/operations/org.js +118 -0
  92. package/dist/shortcuts/index.js +5 -0
  93. package/dist/shortcuts/meeting/operations/eb/catalog.js +4 -1
  94. package/dist/shortcuts/meeting/operations/eb/create-flow.js +85 -28
  95. package/dist/shortcuts/meeting/operations/eb/factory.js +33 -0
  96. package/dist/shortcuts/meeting/operations/eb/group-chat.js +25 -3
  97. package/dist/shortcuts/meeting/operations/eb/person-cache.js +96 -0
  98. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +33 -11
  99. package/dist/shortcuts/project/operations/read.js +6 -25
  100. package/dist/shortcuts/project/operations/shared.js +22 -0
  101. package/dist/shortcuts/project/operations/write.js +28 -2
  102. package/dist/shortcuts/recruit-stage/context.js +119 -0
  103. package/dist/shortcuts/recruit-stage/continuation.js +73 -0
  104. package/dist/shortcuts/recruit-stage/errors.js +65 -0
  105. package/dist/shortcuts/recruit-stage/host.js +109 -0
  106. package/dist/shortcuts/recruit-stage/index.js +115 -0
  107. package/dist/shortcuts/recruit-stage/manifest.js +29 -0
  108. package/dist/shortcuts/recruit-stage/operations/interview-arrange.js +722 -0
  109. package/dist/shortcuts/recruit-stage/operations/interview-feedback.js +269 -0
  110. package/dist/shortcuts/recruit-stage/operations/jd-create.js +630 -0
  111. package/dist/shortcuts/recruit-stage/operations/list.js +431 -0
  112. package/dist/shortcuts/recruit-stage/operations/registry.js +38 -0
  113. package/dist/shortcuts/recruit-stage/operations/resume-import.js +1198 -0
  114. package/dist/shortcuts/recruit-stage/operations/search.js +158 -0
  115. package/dist/shortcuts/recruit-stage/operations/types.js +25 -0
  116. package/dist/shortcuts/recruit-stage/school-tiers.json +318 -0
  117. package/dist/shortcuts/recruit-stage/shared.js +608 -0
  118. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +4 -2
  119. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +13 -6
  120. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +17 -24
  121. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +14 -9
  122. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +50 -8
  123. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +2 -1
  124. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +17 -11
  125. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +7 -39
  126. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +3 -2
  127. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +51 -15
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +16 -1
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +151 -63
  130. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.subGroup.js +56 -0
  131. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +39 -14
  132. package/docs/e10-auth.md +7 -1
  133. package/docs/operation-manual.md +8 -0
  134. package/package.json +1 -1
  135. package/scripts/install-skill-to-xiaoe.mjs +105 -0
  136. package/scripts/package-skill.mjs +4 -2
  137. package/skill-template/business-info.json +24 -0
  138. package/skill-template/domains/recruit-stage.md +29 -0
  139. package/skills/weaver-e10-calendar/SKILL.md +2 -1
  140. package/skills/weaver-e10-calendar/references/calendar-write.md +54 -0
  141. package/skills/weaver-e10-cowork/SKILL.md +1 -1
  142. package/skills/weaver-e10-cowork/references/cowork-write.md +3 -1
  143. package/skills/weaver-e10-jucailin/SKILL.md +1 -1
  144. package/skills/weaver-e10-jucailin/references/attend-write.md +8 -0
  145. package/skills/weaver-e10-jucailin/references/hr-contact.md +8 -0
  146. package/skills/weaver-e10-jucailin/references/hr-report.md +8 -0
  147. package/skills/weaver-e10-jucailin/references/recruit-create.md +2 -2
  148. package/skills/weaver-e10-jucailin/references/recruit-read.md +1 -1
  149. package/skills/weaver-e10-jucailin/references/recruit-write.md +4 -3
  150. package/skills/weaver-e10-mail/SKILL.md +1 -1
  151. package/skills/weaver-e10-meeting/SKILL.md +32 -2
  152. package/skills/weaver-e10-meeting.zip +0 -0
  153. package/skills/weaver-e10-recruit-stage/SKILL.md +226 -0
  154. package/skills/weaver-e10-recruit-stage/agents/openai.yaml +119 -0
  155. package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +188 -0
  156. package/skills/weaver-e10-recruit-stage/references/recruit-arrange-interview.md +322 -0
  157. package/skills/weaver-e10-recruit-stage/references/recruit-capability-check.md +127 -0
  158. package/skills/weaver-e10-recruit-stage/references/recruit-check-install.md +65 -0
  159. package/skills/weaver-e10-recruit-stage/references/recruit-detail-process.md +63 -0
  160. package/skills/weaver-e10-recruit-stage/references/recruit-edit-interview-feedback.md +142 -0
  161. package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +73 -0
  162. package/skills/weaver-e10-recruit-stage/references/recruit-jd-template.md +56 -0
  163. package/skills/weaver-e10-recruit-stage/references/recruit-list-candidate.md +101 -0
  164. package/skills/weaver-e10-recruit-stage/references/recruit-list-interview.md +148 -0
  165. package/skills/weaver-e10-recruit-stage/references/recruit-match-candidates.md +77 -0
  166. package/skills/weaver-e10-recruit-stage/references/recruit-output-links.md +59 -0
  167. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-creator.md +69 -0
  168. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-metadata.md +241 -0
  169. package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +438 -0
  170. package/skills/weaver-e10-recruit-stage/references/recruit-resume-parse.md +128 -0
  171. package/skills/weaver-e10-recruit-stage/references/recruit-search-candidate.md +146 -0
  172. package/skills/weaver-e10-recruit-stage/references/recruit-urge-feedback.md +69 -0
  173. package/skills/weaver-e10-shared/SKILL.md +1 -1
  174. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -2
  175. package/skills/weaver-e10-shijingran/SKILL.md +3 -2
  176. package/skills/weaver-e10-shijingran/references/operations.md +3 -1
  177. package/skills/weaver-e10-shijingran/references/source-manifest.json +41 -40
  178. package/skills/weaver-e10-shijingran/references/write-operations.md +2 -0
  179. package/skills/weaver-e10-workflow/SKILL.md +4 -2
  180. package/skills/weaver-e10-workflow/aigroup/default.json +11 -9
  181. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +23 -13
  182. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +62 -20
  183. package/skills/weaver-e10-yepiaotong/SKILL.md +9 -10
  184. package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +6 -3
  185. package/dist/chunks/{chunk-E7OK5WD5.js → chunk-2LV74IWP.js} +5 -5
@@ -0,0 +1,431 @@
1
+ import { rsSuccess, schemaOf } from './types.js';
2
+ import { rsFail } from '../errors.js';
3
+ import { resolveCurrentUser, randHex32 } from '../shared.js';
4
+ const TYPES = {
5
+ demand: { listKey: 'demand', formType: 'demand_manage', title: '招聘需求', menuNames: ['招聘需求', '需求'] },
6
+ position: { listKey: 'position', formType: 'position_manage', title: '招聘职位', menuNames: ['招聘职位', '职位列表', '职位管理'] },
7
+ entry: { listKey: 'entry', formType: 'entry_manage', title: '入职管理', menuNames: ['入职管理'] },
8
+ offer: { listKey: 'offer', formType: 'offer', title: '录用管理', menuNames: ['录用管理', 'offer管理', 'offer'] },
9
+ interview: { listKey: 'interview', formType: 'interview_plan', title: '面试管理', menuNames: ['面试管理', '面试安排列表', '面试安排'] },
10
+ candidate: { listKey: 'candidate', formType: 'candidate_manage', title: '应聘者', menuNames: ['应聘者', '应聘管理', '候选人', '应聘批次'] },
11
+ };
12
+ // 跨基线列名归一:只在主字段缺失时用别名补展示值,不覆盖已有值
13
+ const DISPLAY_ALIASES = {
14
+ candidate_name: ['talent_id', 'name'],
15
+ position_name: ['position_manage_id', 'position'],
16
+ interview_type_key: ['interview_type'],
17
+ union_status_key: ['union_status'],
18
+ stage_name: ['stage_type_key', 'current_stage'],
19
+ };
20
+ const FILTER_ARGS = {
21
+ positionId: { dataKey: 'position_id', label: '职位' },
22
+ talentId: { dataKey: 'talent_id', label: '人才' },
23
+ candidateManageId: { dataKey: 'candidate_manage_id', label: '应聘批次' },
24
+ };
25
+ const INTERNAL_KEYS = new Set([
26
+ 'id', 'form_data_id', 'form_table_id', 'rowKey', 'ebrecord_rowkey',
27
+ 'isFollow', 'is_flow', 'data_status', 'data_status_value', 'update_time',
28
+ 'formDataId', 'isFollowSearch', 'isShareToMe',
29
+ ]);
30
+ const DISPLAY_UI_KEYS = new Set([...INTERNAL_KEYS, 'operate', 'isToDo', 'todoPropSpan', 'flow_status']);
31
+ // 短枚举状态字段(值=0/1/2…):走 dict/options 或 ds/options 映射中文
32
+ const ENUM_SHORT_FIELDS = new Set([
33
+ 'entry_status', 'transact_status', 'demand_status', 'position_status',
34
+ 'offer_status', 'talent_status', 'channel_status',
35
+ ]);
36
+ async function runList(env, args, feedbackTodoMode) {
37
+ const host = env.host;
38
+ const session = await host.getSession();
39
+ const baseUrl = session.baseUrl.replace(/\/+$/, '');
40
+ const cache = await env.recruitContext();
41
+ const appId = cache.appId;
42
+ const type = String(args.type || (feedbackTodoMode ? 'interview' : ''));
43
+ if (!type || !TYPES[type]) {
44
+ throw rsFail('type 必须是 demand|position|entry|offer|interview|candidate 之一');
45
+ }
46
+ const t = TYPES[type];
47
+ const pageSize = Number(args.pageSize || (feedbackTodoMode ? 200 : 10));
48
+ const current = Number(args.current || 1);
49
+ if (feedbackTodoMode) {
50
+ args.all = true;
51
+ }
52
+ // 列表参数:优先环境缓存 lists[key];缺失回退按菜单别名解析——跨基线不写死
53
+ const listParam = cache.lists[t.listKey]
54
+ || (cache.lists?.all_search || []).find((m) => m.listId && t.menuNames.some((n) => (m.menuName || '').trim() === n))
55
+ || (cache.lists?.all_search || []).find((m) => m.listId && t.menuNames.some((n) => (m.menuName || '').includes(n)));
56
+ if (!listParam?.listId) {
57
+ throw rsFail('当前环境未启用「' + t.title + '」功能(未找到对应菜单/列表参数,按能力探测拦截)');
58
+ }
59
+ const listId = listParam.listId;
60
+ const cusMenuId = listParam.cusMenuId;
61
+ const menuName = listParam.menuName || t.title;
62
+ const formObjId = cache.forms[t.formType]?.objId;
63
+ const fieldMap = (formObjId && cache.fields[formObjId]) || {};
64
+ // 精确筛选:参数 → dataKey → field_<fieldId>(mark 运行时解析)
65
+ const commonSearchFields = {};
66
+ const appliedFilters = [];
67
+ for (const [argKey, def] of Object.entries(FILTER_ARGS)) {
68
+ const val = args[argKey];
69
+ if (!val)
70
+ continue;
71
+ const fieldId = fieldMap[def.dataKey];
72
+ if (!fieldId)
73
+ continue;
74
+ const mark = 'field_' + fieldId;
75
+ commonSearchFields[mark] = [{ [mark]: String(val), [mark + '_condition_type']: 'like' }];
76
+ appliedFilters.push(def.label);
77
+ }
78
+ const baseBody = {
79
+ current, pageSize, tableuid: randHex32(), listId,
80
+ searchParamData: { searchType: '3', type: 'and', ...(appliedFilters.length ? { commonSearchFields } : {}) },
81
+ terminal: 'PC', browser: 'Chrome',
82
+ requiredMatch: 1, mapResult: 1, recheckQuery: 1,
83
+ isFollow: 0, isShareToMe: 0, filterCondition: {},
84
+ cusMenuId, urlPageTitle: t.title, menuName,
85
+ };
86
+ const extra = { ebBusinessId: listId };
87
+ const defaultPages = feedbackTodoMode ? 5 : 20;
88
+ const wantPages = args.all ? Math.max(1, Number(args.maxPages || defaultPages)) : 1;
89
+ const rawRows = [];
90
+ const dispRows = [];
91
+ let hasDisplay = false;
92
+ let cntTotal = null;
93
+ let lastGot = 0;
94
+ for (let i = 0; i < wantPages; i++) {
95
+ const body = { ...baseBody, current: current + i };
96
+ const cntBody = { ...body };
97
+ delete cntBody.current;
98
+ delete cntBody.pageSize;
99
+ const [resp, c] = await Promise.all([
100
+ host.request({
101
+ path: '/api/ebuilder/form/list/getListDatas?passid=' + formObjId + '&cid=' + formObjId,
102
+ body, headers: extra,
103
+ }),
104
+ i === 0
105
+ ? host.request({
106
+ path: '/api/ebuilder/form/list/getListDataCount?passid=' + formObjId + '&cid=' + formObjId,
107
+ body: cntBody, headers: extra,
108
+ }).catch(() => null)
109
+ : Promise.resolve(null),
110
+ ]);
111
+ if (c && typeof c?.data?.total === 'number')
112
+ cntTotal = c.data.total;
113
+ const rRaw = resp?.data?.data || [];
114
+ const rDisp = resp?.data?.displayData;
115
+ if (Array.isArray(rDisp) && rDisp.length) {
116
+ hasDisplay = true;
117
+ dispRows.push(...rDisp);
118
+ }
119
+ rawRows.push(...rRaw);
120
+ const got = (Array.isArray(rDisp) && rDisp.length) ? rDisp.length : rRaw.length;
121
+ lastGot = got;
122
+ if (got < pageSize)
123
+ break;
124
+ if (cntTotal !== null && (current + i) * pageSize >= cntTotal)
125
+ break;
126
+ }
127
+ const rows = hasDisplay ? dispRows : rawRows;
128
+ // 取回后本地过滤/排序(服务端时间筛选在部分列表不稳定,统一本地处理)
129
+ let workRows = rows;
130
+ const clientFilters = [];
131
+ for (const [argKey, def] of Object.entries(FILTER_ARGS)) {
132
+ if (args[argKey])
133
+ clientFilters.push([def.dataKey, String(args[argKey])]);
134
+ }
135
+ if (clientFilters.length) {
136
+ // 客户端精确筛选:服务端 commonSearchFields 对部分字段不生效,按 dataKey 本地等值筛选;配合 all 保证扫全量
137
+ workRows = workRows.filter((r) => clientFilters.every(([dk, val]) => String(r[dk] ?? '') === val));
138
+ }
139
+ if (args.recent) {
140
+ const days = Number(args.recent);
141
+ const cutoff = Date.now() - days * 86400000;
142
+ workRows = workRows.filter((r) => {
143
+ const raw = String(r.start_time || r.interview_time || '');
144
+ const ts = Date.parse(raw.replace(/-/g, '/'));
145
+ return !Number.isNaN(ts) && ts >= cutoff;
146
+ });
147
+ }
148
+ if (args.sort) {
149
+ const k = String(args.sort);
150
+ workRows = [...workRows].sort((a, b) => {
151
+ const av = String(a[k] ?? ''), bv = String(b[k] ?? '');
152
+ return av < bv ? 1 : av > bv ? -1 : 0; // 默认倒序(时间类:最新在前)
153
+ });
154
+ }
155
+ // 总数三态:① 计数接口精确 ② 到末页按已取回行数 ③ 不可知置 null(绝不把失败当 0)
156
+ const reachedEnd = lastGot < pageSize;
157
+ let total;
158
+ let totalExact;
159
+ let totalNote;
160
+ if (cntTotal !== null) {
161
+ total = cntTotal;
162
+ totalExact = true;
163
+ }
164
+ else if (reachedEnd) {
165
+ total = rows.length;
166
+ totalExact = true;
167
+ totalNote = '计数接口不可用,已到末页,总数按已取回行数得出';
168
+ }
169
+ else {
170
+ total = null;
171
+ totalExact = false;
172
+ totalNote = '计数接口不可用且本页已满,总数不可知(可加大 pageSize 或开 all 取回全部行数)';
173
+ }
174
+ if (args.all && rows.length >= wantPages * pageSize && (cntTotal === null || cntTotal > rows.length)) {
175
+ totalNote = `已取回 ${rows.length} 行(达 maxPages 上限 ${wantPages} 页),可能仍有更多;可增大 maxPages 或加精确筛选`;
176
+ }
177
+ // 原始层兜底翻译(displayData 存在时跳过全部翻译,零本地翻译、零额外请求)
178
+ const dictNameMap = {};
179
+ const optionsMap = {};
180
+ const dsOptionsCache = {};
181
+ for (const item of cache.dict?.raw || []) {
182
+ if (item.option_id && item.selection_name)
183
+ dictNameMap[item.option_id] = item.selection_name;
184
+ }
185
+ for (const [fieldId, opts] of Object.entries(cache.options || {})) {
186
+ if (opts && typeof opts === 'object')
187
+ Object.assign(optionsMap, opts);
188
+ }
189
+ const dsOptionsFor = async (fieldId) => {
190
+ if (fieldId && dsOptionsCache[fieldId] === undefined) {
191
+ try {
192
+ const o = await host.request({
193
+ path: '/api/ebuilder/common/ds/options?fieldName=' + fieldId + '&objId=' + formObjId + '&sourceType=FORM&groupId=' + appId,
194
+ method: 'GET',
195
+ });
196
+ const map = {};
197
+ for (const it of o?.data || [])
198
+ if (it?.optionId || it?.id)
199
+ map[String(it.optionId || it.id)] = it.name || it.text || it.optionName;
200
+ dsOptionsCache[fieldId] = map;
201
+ }
202
+ catch {
203
+ dsOptionsCache[fieldId] = {};
204
+ }
205
+ }
206
+ return dsOptionsCache[fieldId] || {};
207
+ };
208
+ const mapEnum = async (optionId, fieldKey) => {
209
+ const s = String(optionId);
210
+ if (dictNameMap[s])
211
+ return dictNameMap[s];
212
+ if (optionsMap[s])
213
+ return optionsMap[s];
214
+ let cn = (await dsOptionsFor(fieldMap[fieldKey]))[s] || null;
215
+ if (!cn && String(fieldKey).endsWith('_key')) {
216
+ cn = (await dsOptionsFor(fieldMap[String(fieldKey).slice(0, -4)]))[s] || null;
217
+ }
218
+ return cn;
219
+ };
220
+ // ds/options 并行预热(仅原始层需要)
221
+ if (!hasDisplay) {
222
+ const needFieldIds = new Set();
223
+ for (const r of workRows) {
224
+ for (const [k, v] of Object.entries(r)) {
225
+ if (INTERNAL_KEYS.has(k) || DISPLAY_UI_KEYS.has(k))
226
+ continue;
227
+ const s = String(v ?? '');
228
+ if (!s)
229
+ continue;
230
+ if (!ENUM_SHORT_FIELDS.has(k) && !/^\d{16,}$/.test(s))
231
+ continue;
232
+ if (dictNameMap[s] || optionsMap[s])
233
+ continue;
234
+ const fieldId = fieldMap[k] || (k.endsWith('_key') ? fieldMap[k.slice(0, -4)] : null);
235
+ if (fieldId && dsOptionsCache[fieldId] === undefined)
236
+ needFieldIds.add(fieldId);
237
+ }
238
+ }
239
+ await Promise.all([...needFieldIds].map((fieldId) => dsOptionsFor(fieldId)));
240
+ }
241
+ const out = {
242
+ env: new URL(baseUrl).hostname, type, total, totalExact,
243
+ ...(totalNote ? { totalNote } : {}),
244
+ ...(appliedFilters.length ? { filters: appliedFilters } : {}),
245
+ ...(args.recent ? { recentDays: Number(args.recent) } : {}),
246
+ page: current, pageSize, scanned: rows.length, returned: workRows.length, rows: [],
247
+ };
248
+ const outRows = out.rows;
249
+ for (const r of workRows) {
250
+ const row = { form_data_id: r.form_data_id || r.id };
251
+ for (const [k, v] of Object.entries(r)) {
252
+ if (hasDisplay) {
253
+ if (DISPLAY_UI_KEYS.has(k) || k === 'form_data_id')
254
+ continue;
255
+ if (k.endsWith('Span') || k.endsWith('Action'))
256
+ continue;
257
+ const s = String(v ?? '');
258
+ if (!s)
259
+ continue;
260
+ // displayData 偶尔把枚举回退成 optionId → 运行时映射回中文;映射不到保留原值(业务 id 需保留)
261
+ if (/^\d{16,}$/.test(s)) {
262
+ const cn = await mapEnum(s, k);
263
+ if (cn) {
264
+ row[k] = cn;
265
+ continue;
266
+ }
267
+ }
268
+ if (typeof v === 'object' || (typeof v === 'string' && s.trimStart().startsWith('['))) {
269
+ let arr;
270
+ if (typeof v === 'object')
271
+ arr = Array.isArray(v) ? v : [v];
272
+ else {
273
+ try {
274
+ arr = JSON.parse(s);
275
+ }
276
+ catch {
277
+ arr = null;
278
+ }
279
+ }
280
+ if (Array.isArray(arr)) {
281
+ const names = arr.map((x) => x?.name || x?.username || x?.text || x?.title || (typeof x === 'string' ? x : null)).filter(Boolean);
282
+ if (names.length) {
283
+ row[k] = names.join('、');
284
+ continue;
285
+ }
286
+ }
287
+ row[k] = s;
288
+ continue;
289
+ }
290
+ row[k] = s;
291
+ continue;
292
+ }
293
+ // 原始层兜底
294
+ if (INTERNAL_KEYS.has(k))
295
+ continue;
296
+ const s = String(v ?? '');
297
+ if (!s)
298
+ continue;
299
+ if (ENUM_SHORT_FIELDS.has(k)) {
300
+ const cn = await mapEnum(s, k);
301
+ row[k] = cn || s;
302
+ continue;
303
+ }
304
+ if (/^\d{16,}$/.test(s)) {
305
+ const cn = await mapEnum(s, k);
306
+ if (cn)
307
+ row[k] = cn;
308
+ continue;
309
+ }
310
+ if (typeof v === 'object' || (typeof v === 'string' && s.trimStart().startsWith('['))) {
311
+ let arr;
312
+ if (typeof v === 'object')
313
+ arr = Array.isArray(v) ? v : [v];
314
+ else {
315
+ try {
316
+ arr = JSON.parse(s);
317
+ }
318
+ catch {
319
+ arr = null;
320
+ }
321
+ }
322
+ if (Array.isArray(arr)) {
323
+ const names = arr.map((x) => x?.name || x?.text || x?.title || (typeof x === 'string' ? x : null)).filter(Boolean);
324
+ if (names.length) {
325
+ row[k] = names.join('、');
326
+ continue;
327
+ }
328
+ }
329
+ row[k] = JSON.stringify(v);
330
+ continue;
331
+ }
332
+ row[k] = s;
333
+ }
334
+ // 跨基线列名归一(跳过"看起来是裸 id"的值,避免把 16 位 id 当人名展示)
335
+ for (const [canonical, aliases] of Object.entries(DISPLAY_ALIASES)) {
336
+ const cur = row[canonical];
337
+ if (cur !== undefined && cur !== '')
338
+ continue;
339
+ for (const a of aliases) {
340
+ const v = row[a];
341
+ if (v === undefined || v === '')
342
+ continue;
343
+ if (/^\d{16,}$/.test(String(v)))
344
+ continue;
345
+ row[canonical] = v;
346
+ break;
347
+ }
348
+ }
349
+ outRows.push(row);
350
+ }
351
+ // feedbackTodo:从当前结果集派生两个待办视图
352
+ if (feedbackTodoMode) {
353
+ const cu = await resolveCurrentUser(host);
354
+ const me = cu ? cu.name : '';
355
+ const st = (r) => String(r.union_status_key || r.union_status || '');
356
+ const tp = (r) => String(r.interview_type_key || r.interview_type || '');
357
+ const iv = (r) => String(r.interviewer || '');
358
+ const slim = (r) => ({
359
+ form_data_id: r.form_data_id,
360
+ candidate_name: r.candidate_name,
361
+ position_name: r.position_name,
362
+ start_time: r.start_time,
363
+ interview_type_key: tp(r),
364
+ interview_session: r.interview_session,
365
+ interviewer: iv(r),
366
+ union_status_key: st(r),
367
+ subject: r.subject,
368
+ mobile: r.mobile,
369
+ talent_id: r.talent_id,
370
+ position_manage_id: r.position_manage_id,
371
+ });
372
+ const pending = outRows.filter((r) => /待反馈/.test(st(r)));
373
+ const mineAll = me ? pending.filter((r) => iv(r).includes(me)) : [];
374
+ const myAi = mineAll.filter((r) => /AI\s*面试/i.test(tp(r)));
375
+ const notes = [];
376
+ if (!me)
377
+ notes.push('未能解析当前登录用户,无法按面试官过滤(结果为空)');
378
+ if (out.total !== null && outRows.length < out.total) {
379
+ notes.push('已扫描 ' + outRows.length + '/' + out.total + ' 条(面试列表默认顺序),未覆盖全部;如怀疑遗漏可加大 maxPages');
380
+ }
381
+ out.feedbackTodo = {
382
+ currentUser: me || null,
383
+ scanned: outRows.length,
384
+ myAiPendingFeedback: { count: myAi.length, rows: myAi.map(slim) },
385
+ myOtherPendingFeedback: {
386
+ count: mineAll.length - myAi.length,
387
+ rows: mineAll.filter((r) => !/AI\s*面试/i.test(tp(r))).map(slim),
388
+ },
389
+ ...(notes.length ? { note: notes.join(';') } : {}),
390
+ };
391
+ }
392
+ return out;
393
+ }
394
+ const listInput = {
395
+ type: { type: 'string', enum: ['demand', 'position', 'entry', 'offer', 'interview', 'candidate'], description: '列表类型' },
396
+ pageSize: { type: 'integer', minimum: 1, maximum: 200, description: '每页行数(默认 10;feedbackTodo 默认 200)' },
397
+ current: { type: 'integer', minimum: 1, description: '页码(默认 1)' },
398
+ positionId: { type: 'string', description: '按职位 id 客户端精确筛选(服务端筛选部分列表不可靠)' },
399
+ talentId: { type: 'string', description: '按人才 id 客户端精确筛选' },
400
+ candidateManageId: { type: 'string', description: '按应聘批次 id 客户端精确筛选' },
401
+ recent: { type: 'integer', minimum: 1, description: '近 N 天(按 start_time 本地过滤)' },
402
+ sort: { type: 'string', description: '按 dataKey 倒序排序(如 start_time)' },
403
+ all: { type: 'boolean', description: '翻页取全(末页判据=页内行数 < pageSize)' },
404
+ maxPages: { type: 'integer', minimum: 1, maximum: 50, description: 'all 模式最大页数(默认 20;feedbackTodo 默认 5)' },
405
+ };
406
+ export const listOperations = [
407
+ {
408
+ name: 'recruit-stage.list',
409
+ resource: 'recruit.list',
410
+ method: 'query',
411
+ action: 'list',
412
+ summary: '招聘 6 类列表查询(需求/职位/入职/录用/面试/应聘者):displayData 显示层优先、客户端筛选/时间过滤/排序、总数三态不造假',
413
+ capability: 'recruit.list',
414
+ risk: 'read',
415
+ inputSchema: schemaOf(listInput, { required: ['type'] }),
416
+ handler: async (args, env) => rsSuccess('recruit-stage.list', await runList(env, args, false)),
417
+ },
418
+ {
419
+ name: 'recruit-stage.interview.feedbackTodo',
420
+ resource: 'recruit.interview',
421
+ method: 'query',
422
+ action: 'interview.feedbackTodo',
423
+ summary: '面试待办:只返回我作为面试官的待反馈清单(后端 TO_FEEDBACK 已含「已完成+有录像+未填意见」语义);myAiPendingFeedback 主清单 + myOtherPendingFeedback 附带',
424
+ capability: 'recruit.interview.feedback',
425
+ risk: 'read',
426
+ inputSchema: schemaOf({
427
+ pageSize: listInput.pageSize, maxPages: listInput.maxPages,
428
+ }, {}),
429
+ handler: async (args, env) => rsSuccess('recruit-stage.interview.feedbackTodo', await runList(env, { ...args, type: 'interview' }, true)),
430
+ },
431
+ ];
@@ -0,0 +1,38 @@
1
+ import { RecruitStageCliError } from '../errors.js';
2
+ import { jdCreateOperations } from './jd-create.js';
3
+ import { resumeImportOperations } from './resume-import.js';
4
+ import { interviewArrangeOperations } from './interview-arrange.js';
5
+ import { interviewFeedbackOperations } from './interview-feedback.js';
6
+ import { listOperations } from './list.js';
7
+ import { searchOperations } from './search.js';
8
+ const definitions = [
9
+ ...searchOperations,
10
+ ...listOperations,
11
+ ...jdCreateOperations,
12
+ ...resumeImportOperations,
13
+ ...interviewArrangeOperations,
14
+ ...interviewFeedbackOperations,
15
+ ];
16
+ const registry = new Map();
17
+ for (const definition of definitions) {
18
+ if (registry.has(definition.name)) {
19
+ throw new Error(`duplicate recruit-stage operation: ${definition.name}`);
20
+ }
21
+ registry.set(definition.name, definition);
22
+ }
23
+ export function recruitStageOperationDefinitions() {
24
+ return [...registry.values()];
25
+ }
26
+ export function getRecruitStageOperation(operation) {
27
+ const definition = registry.get(operation);
28
+ if (!definition) {
29
+ throw new RecruitStageCliError('validation', 'operation_unknown', `招聘助手不支持操作:${operation}`, {
30
+ exitCode: 1,
31
+ });
32
+ }
33
+ return definition;
34
+ }
35
+ export async function executeRecruitStageOperation(operation, input, env) {
36
+ const definition = getRecruitStageOperation(operation);
37
+ return definition.handler(input, env);
38
+ }