weaver-work-cli 0.1.39 → 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 (197) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/{archive-ENZYBBME.js → archive-J4I6MCKF.js} +2 -2
  3. package/dist/chunks/{archive-oa-YWLEC5V4.js → archive-oa-BRZKKTO4.js} +2 -2
  4. package/dist/chunks/{asset-OOV6DWPA.js → asset-L7D4FAOZ.js} +2 -2
  5. package/dist/chunks/{asset-idle-RE632MY5.js → asset-idle-DADLLV75.js} +2 -2
  6. package/dist/chunks/{attachment-content-RWN2FCLL.js → attachment-content-K4C7VLWQ.js} +3 -3
  7. package/dist/chunks/{blog-PEPOJAL7.js → blog-BVGQ7NZL.js} +2 -2
  8. package/dist/chunks/{caizhilian-WYIQDVPJ.js → caizhilian-SAADNFD6.js} +2 -2
  9. package/dist/chunks/{calendar-CIARBAOX.js → calendar-Q7NQY5Z2.js} +61 -23
  10. package/dist/chunks/{chunk-F2EQXUEE.js → chunk-3KTFS4IA.js} +397 -159
  11. package/dist/chunks/{chunk-G2JSIUWF.js → chunk-4PD5NWAX.js} +89 -0
  12. package/dist/chunks/{chunk-HJYVHXUQ.js → chunk-4RAE6JXP.js} +1 -1
  13. package/dist/chunks/{chunk-CQHGNCK2.js → chunk-72CKAUAG.js} +41 -8
  14. package/dist/chunks/{chunk-K3RUEEOL.js → chunk-APVFJUQH.js} +2 -2
  15. package/dist/chunks/{chunk-BJGXMFGD.js → chunk-BBKDXQEC.js} +2 -2
  16. package/dist/chunks/chunk-BIPYLLVS.js +501 -0
  17. package/dist/chunks/{chunk-GVPJZXJ2.js → chunk-EKWAQSLU.js} +92 -50
  18. package/dist/chunks/{chunk-XXJOSZTQ.js → chunk-FOAA27U5.js} +381 -57
  19. package/dist/chunks/{chunk-I2HB737U.js → chunk-G4UR6NXN.js} +2 -2
  20. package/dist/chunks/{chunk-YS5DPN4T.js → chunk-GEWYMQME.js} +1 -1
  21. package/dist/chunks/{chunk-P2DAISYW.js → chunk-GHXCDPTM.js} +1 -1
  22. package/dist/chunks/{chunk-L4N7B7ZD.js → chunk-IZ7S7QUS.js} +4 -4
  23. package/dist/chunks/{chunk-NLK2RZKG.js → chunk-JPRO6ER2.js} +122 -2214
  24. package/dist/chunks/{chunk-36DIFZAH.js → chunk-LADPYOWC.js} +2 -2
  25. package/dist/chunks/chunk-LDZQPUIA.js +1238 -0
  26. package/dist/chunks/chunk-NKZRQXST.js +2546 -0
  27. package/dist/chunks/{chunk-EINRJHQ3.js → chunk-QKNKR4RH.js} +3 -3
  28. package/dist/chunks/{chunk-T7JDJJ7H.js → chunk-R4CHUSKC.js} +126 -19
  29. package/dist/chunks/{chunk-EZGFHJFW.js → chunk-SA6VBIHY.js} +88 -15
  30. package/dist/chunks/{chunk-ZXRNHZTE.js → chunk-TTPJJJC4.js} +2 -2
  31. package/dist/chunks/{chunk-KTCB5R5H.js → chunk-W7BEHOSR.js} +2 -2
  32. package/dist/chunks/{chunk-MUQUHKPG.js → chunk-YRSZQUD2.js} +2 -2
  33. package/dist/chunks/{conflict-DRMDOL56.js → conflict-F2OMZRTO.js} +2 -2
  34. package/dist/chunks/{contract-73MERZWN.js → contract-5BETHRSG.js} +2 -2
  35. package/dist/chunks/{cowork-HNGBPD7A.js → cowork-3MN6EQQO.js} +27 -10
  36. package/dist/chunks/create-flow-5FQNNMVO.js +55 -0
  37. package/dist/chunks/{ebuilder-form-OY6DVNRK.js → ebuilder-form-SBHOYBUD.js} +3 -3
  38. package/dist/chunks/{ehr-RAQNEQO7.js → ehr-KMOJULKG.js} +432 -1234
  39. package/dist/chunks/env-LB7NBL2O.js +19 -0
  40. package/dist/chunks/{esb-6XV7C7YV.js → esb-W2PJCFCX.js} +4 -4
  41. package/dist/chunks/factory-6ZFQXQ46.js +30 -0
  42. package/dist/chunks/{fna-XTSRB7G7.js → fna-ZQPCUV4R.js} +2 -2
  43. package/dist/chunks/group-chat-2HHAJN46.js +20 -0
  44. package/dist/chunks/{host-YMBXWFWX.js → host-4CBHGOHT.js} +3 -3
  45. package/dist/chunks/{host-A7J6GDUP.js → host-ATKVNZAT.js} +3 -3
  46. package/dist/chunks/{host-3F5UM5GN.js → host-BQH22NTQ.js} +3 -3
  47. package/dist/chunks/{host-YAQAPYNP.js → host-X2CSLFM6.js} +3 -3
  48. package/dist/chunks/{host-VGPVP7YF.js → host-YTODKMSL.js} +3 -3
  49. package/dist/chunks/{hrm-TLHKC545.js → hrm-FFM4UJFS.js} +4 -4
  50. package/dist/chunks/{im-YOUCFMUK.js → im-VEHKDQPB.js} +8 -8
  51. package/dist/chunks/{invoice-WNBS2B4C.js → invoice-LVIKSIIF.js} +3 -3
  52. package/dist/chunks/{jiuchuanhui-XABWS5RN.js → jiuchuanhui-X6DINO5P.js} +2 -2
  53. package/dist/chunks/{lazy-dispatch-NLZ5UQQI.js → lazy-dispatch-KSWTN5L6.js} +4 -4
  54. package/dist/chunks/{mail-LRYMBFNL.js → mail-VDKRGUD3.js} +9 -4
  55. package/dist/chunks/{meeting-ESXL6MFI.js → meeting-CLC6HP3Z.js} +12 -10
  56. package/dist/chunks/{okr-EOV65SSF.js → okr-CHG4OO3U.js} +2 -2
  57. package/dist/chunks/{plan-YFPVKW6Y.js → plan-XWR75667.js} +2 -2
  58. package/dist/chunks/{project-MDVZUYQW.js → project-5BXOOKMG.js} +40 -23
  59. package/dist/chunks/recruit-stage-DGMFNNDE.js +4507 -0
  60. package/dist/chunks/{registry-VT5Y2HLA.js → registry-7FFOXIAW.js} +4 -4
  61. package/dist/chunks/{registry-QCLZEDAP.js → registry-BKBZPBAJ.js} +10 -8
  62. package/dist/chunks/{registry-YX4CSEMD.js → registry-RHMW6ANW.js} +2 -2
  63. package/dist/chunks/{registry-NYPYMZXG.js → registry-Z43434VG.js} +1 -1
  64. package/dist/chunks/{room-recommend-L3MWH4JW.js → room-recommend-62H7ZQQ6.js} +4 -4
  65. package/dist/chunks/{schedule-UASAVDVD.js → schedule-FNXKH3DQ.js} +4 -4
  66. package/dist/chunks/{workflow-KDGIUCNW.js → workflow-ADHKCAJK.js} +4 -4
  67. package/dist/index.js +33 -28
  68. package/dist/internal/e10/auth/commands.js +110 -56
  69. package/dist/internal/e10/auth/messages.js +6 -0
  70. package/dist/internal/e10/auth/session.js +28 -1
  71. package/dist/internal/e10/auth/xiaoe.js +315 -48
  72. package/dist/internal/e10/auth-recovery.js +36 -5
  73. package/dist/internal/e10/client.js +13 -0
  74. package/dist/internal/e10/context.js +61 -9
  75. package/dist/internal/e10/index.js +1 -1
  76. package/dist/internal/e10/network-error.js +93 -0
  77. package/dist/internal/e10/request-runtime.js +15 -0
  78. package/dist/shortcuts/calendar/operations/read.js +31 -22
  79. package/dist/shortcuts/calendar/operations/write.js +45 -1
  80. package/dist/shortcuts/cowork/operations/discuss-write.js +5 -1
  81. package/dist/shortcuts/cowork/operations/item-write.js +5 -3
  82. package/dist/shortcuts/cowork/operations/member-write.js +3 -1
  83. package/dist/shortcuts/cowork/operations/permission-write.js +5 -1
  84. package/dist/shortcuts/cowork/operations/shared.js +7 -0
  85. package/dist/shortcuts/ehr/operations/attend-write.js +43 -1
  86. package/dist/shortcuts/ehr/operations/hr-contact.js +7 -4
  87. package/dist/shortcuts/ehr/operations/hr-report.js +13 -7
  88. package/dist/shortcuts/ehr/operations/recruit-candidate-import.js +134 -59
  89. package/dist/shortcuts/ehr/operations/recruit-write.js +262 -17
  90. package/dist/shortcuts/ehr/operations/recruit.js +10 -4
  91. package/dist/shortcuts/ehr/operations/shared.js +47 -0
  92. package/dist/shortcuts/hrm/operations/org.js +118 -0
  93. package/dist/shortcuts/index.js +5 -0
  94. package/dist/shortcuts/mail/operations/mail-send.js +8 -2
  95. package/dist/shortcuts/meeting/operations/eb/catalog.js +9 -2
  96. package/dist/shortcuts/meeting/operations/eb/create-flow.js +461 -127
  97. package/dist/shortcuts/meeting/operations/eb/crm-search.js +87 -5
  98. package/dist/shortcuts/meeting/operations/eb/factory.js +33 -0
  99. package/dist/shortcuts/meeting/operations/eb/flow-memory.js +49 -0
  100. package/dist/shortcuts/meeting/operations/eb/group-chat.js +605 -0
  101. package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
  102. package/dist/shortcuts/meeting/operations/eb/person-cache.js +96 -0
  103. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +44 -6
  104. package/dist/shortcuts/meeting/operations/eb/room-summary.js +2 -0
  105. package/dist/shortcuts/meeting/operations/eb/usage-cache.js +99 -0
  106. package/dist/shortcuts/meeting/operations/from-group-chat.js +35 -12
  107. package/dist/shortcuts/project/operations/read.js +6 -25
  108. package/dist/shortcuts/project/operations/shared.js +22 -0
  109. package/dist/shortcuts/project/operations/write.js +28 -2
  110. package/dist/shortcuts/recruit-stage/context.js +119 -0
  111. package/dist/shortcuts/recruit-stage/continuation.js +73 -0
  112. package/dist/shortcuts/recruit-stage/errors.js +65 -0
  113. package/dist/shortcuts/recruit-stage/host.js +109 -0
  114. package/dist/shortcuts/recruit-stage/index.js +115 -0
  115. package/dist/shortcuts/recruit-stage/manifest.js +29 -0
  116. package/dist/shortcuts/recruit-stage/operations/interview-arrange.js +722 -0
  117. package/dist/shortcuts/recruit-stage/operations/interview-feedback.js +269 -0
  118. package/dist/shortcuts/recruit-stage/operations/jd-create.js +630 -0
  119. package/dist/shortcuts/recruit-stage/operations/list.js +431 -0
  120. package/dist/shortcuts/recruit-stage/operations/registry.js +38 -0
  121. package/dist/shortcuts/recruit-stage/operations/resume-import.js +1198 -0
  122. package/dist/shortcuts/recruit-stage/operations/search.js +158 -0
  123. package/dist/shortcuts/recruit-stage/operations/types.js +25 -0
  124. package/dist/shortcuts/recruit-stage/school-tiers.json +318 -0
  125. package/dist/shortcuts/recruit-stage/shared.js +608 -0
  126. package/dist/shortcuts/workflow/operations/form.js +21 -2
  127. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +52 -1
  128. package/dist/shortcuts/workflow/operations/todoSmartConvert/detailFieldRules.js +4 -2
  129. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +19 -11
  130. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailCache.js +17 -24
  131. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoDetailFetcher.js +14 -9
  132. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +78 -10
  133. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +3 -1
  134. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.detail.js +17 -11
  135. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +16 -40
  136. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.groupSummary.js +3 -2
  137. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +52 -15
  138. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.pipeline.js +16 -1
  139. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.smartGroup.js +151 -63
  140. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.subGroup.js +56 -0
  141. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +53 -16
  142. package/docs/e10-auth.md +22 -1
  143. package/docs/operation-manual.md +8 -0
  144. package/package.json +1 -1
  145. package/scripts/install-skill-to-xiaoe.mjs +105 -0
  146. package/scripts/mcp-meeting.mjs +234 -0
  147. package/scripts/package-skill.mjs +4 -2
  148. package/skill-template/business-info.json +24 -0
  149. package/skill-template/domains/recruit-stage.md +29 -0
  150. package/skills/weaver-e10-calendar/SKILL.md +2 -1
  151. package/skills/weaver-e10-calendar/references/calendar-write.md +54 -0
  152. package/skills/weaver-e10-cowork/SKILL.md +1 -1
  153. package/skills/weaver-e10-cowork/references/cowork-write.md +3 -1
  154. package/skills/weaver-e10-jucailin/SKILL.md +1 -1
  155. package/skills/weaver-e10-jucailin/references/attend-write.md +8 -0
  156. package/skills/weaver-e10-jucailin/references/hr-contact.md +8 -0
  157. package/skills/weaver-e10-jucailin/references/hr-report.md +8 -0
  158. package/skills/weaver-e10-jucailin/references/recruit-create.md +2 -2
  159. package/skills/weaver-e10-jucailin/references/recruit-read.md +1 -1
  160. package/skills/weaver-e10-jucailin/references/recruit-write.md +4 -3
  161. package/skills/weaver-e10-mail/SKILL.md +1 -1
  162. package/skills/weaver-e10-mail/references/mail-send.md +2 -0
  163. package/skills/weaver-e10-meeting/SKILL.md +119 -26
  164. package/skills/weaver-e10-meeting/references/meeting-minute-file.md +25 -11
  165. package/skills/weaver-e10-meeting.zip +0 -0
  166. package/skills/weaver-e10-recruit-stage/SKILL.md +226 -0
  167. package/skills/weaver-e10-recruit-stage/agents/openai.yaml +119 -0
  168. package/skills/weaver-e10-recruit-stage/lib/recruit-xiaoe-file.mjs +188 -0
  169. package/skills/weaver-e10-recruit-stage/references/recruit-arrange-interview.md +322 -0
  170. package/skills/weaver-e10-recruit-stage/references/recruit-capability-check.md +127 -0
  171. package/skills/weaver-e10-recruit-stage/references/recruit-check-install.md +65 -0
  172. package/skills/weaver-e10-recruit-stage/references/recruit-detail-process.md +63 -0
  173. package/skills/weaver-e10-recruit-stage/references/recruit-edit-interview-feedback.md +142 -0
  174. package/skills/weaver-e10-recruit-stage/references/recruit-jd-create.md +73 -0
  175. package/skills/weaver-e10-recruit-stage/references/recruit-jd-template.md +56 -0
  176. package/skills/weaver-e10-recruit-stage/references/recruit-list-candidate.md +101 -0
  177. package/skills/weaver-e10-recruit-stage/references/recruit-list-interview.md +148 -0
  178. package/skills/weaver-e10-recruit-stage/references/recruit-match-candidates.md +77 -0
  179. package/skills/weaver-e10-recruit-stage/references/recruit-output-links.md +59 -0
  180. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-creator.md +69 -0
  181. package/skills/weaver-e10-recruit-stage/references/recruit-resolve-metadata.md +241 -0
  182. package/skills/weaver-e10-recruit-stage/references/recruit-resume-import.md +438 -0
  183. package/skills/weaver-e10-recruit-stage/references/recruit-resume-parse.md +128 -0
  184. package/skills/weaver-e10-recruit-stage/references/recruit-search-candidate.md +146 -0
  185. package/skills/weaver-e10-recruit-stage/references/recruit-urge-feedback.md +69 -0
  186. package/skills/weaver-e10-shared/SKILL.md +2 -2
  187. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +4 -2
  188. package/skills/weaver-e10-shijingran/SKILL.md +3 -2
  189. package/skills/weaver-e10-shijingran/references/operations.md +3 -1
  190. package/skills/weaver-e10-shijingran/references/source-manifest.json +41 -40
  191. package/skills/weaver-e10-shijingran/references/write-operations.md +2 -0
  192. package/skills/weaver-e10-workflow/SKILL.md +7 -1
  193. package/skills/weaver-e10-workflow/aigroup/default.json +23 -9
  194. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +37 -21
  195. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +135 -43
  196. package/skills/weaver-e10-yepiaotong/SKILL.md +9 -10
  197. package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +6 -3
@@ -74,14 +74,51 @@ function unwrapRow(value) {
74
74
  const main = asRecord(record.mainTable);
75
75
  return main ?? record;
76
76
  }
77
+ /**
78
+ * 「列式」输出转行:动作流把 `mainTable` 按字段铺成平行数组时
79
+ * (2026-11-02 实测:`mc` 10 个客户名、`sjID` 10 个 id、`lxr` 4 条扁平联系人),
80
+ * 整体会被当成"一行",于是 `mc` 是数组、取不到名称 → 客户端判定"没匹配到客户"。
81
+ * 这里按索引把 id / 名称配成多行,至少能把这批客户列出来给用户选。
82
+ *
83
+ * ⚠️ 只配 id + 名称:`lxr` 是**跨客户的扁平汇总**,里面没有客户归属字段,
84
+ * 硬按索引分会把 A 家联系人挂到 B 家名下 —— 宁可不带,也不臆造。
85
+ */
86
+ function columnarCustomerRows(record) {
87
+ const idKey = ID_KEYS.find((key) => Array.isArray(record[key]));
88
+ const nameKey = NAME_KEYS.find((key) => Array.isArray(record[key]));
89
+ if (!idKey && !nameKey)
90
+ return [];
91
+ const idValues = (idKey ? record[idKey] : []);
92
+ const nameValues = (nameKey ? record[nameKey] : []);
93
+ const size = Math.max(idValues.length, nameValues.length);
94
+ if (size <= 1)
95
+ return [];
96
+ const rows = [];
97
+ for (let index = 0; index < size; index += 1) {
98
+ const row = {};
99
+ if (idKey)
100
+ row[idKey] = idValues[index];
101
+ if (nameKey)
102
+ row[nameKey] = nameValues[index];
103
+ rows.push(row);
104
+ }
105
+ return rows;
106
+ }
77
107
  /** 从 customData 里挖出所有可能承载客户行的候选数组。 */
78
108
  function collectCandidateLists(customData) {
79
109
  const found = [];
80
110
  const main = asRecord(customData.mainTable);
81
111
  if (main) {
82
- // ① 单条:mainTable 自己就是一行。即使 mc/sjID 都是空串也放进来,
83
- // 让 normalizeCustomers 丢掉后落到「没有匹配到客户」,而不是误报「结构不认识」
84
- found.push({ source: 'customData.mainTable', rows: [main] });
112
+ const columnar = columnarCustomerRows(main);
113
+ if (columnar.length > 1) {
114
+ // 列式:一行一个客户,但没有联系人(见 columnarCustomerRows 的说明)
115
+ found.push({ source: 'customData.mainTable.columns', rows: columnar });
116
+ }
117
+ else {
118
+ // ① 单条:mainTable 自己就是一行。即使 mc/sjID 都是空串也放进来,
119
+ // 让 normalizeCustomers 丢掉后落到「没有匹配到客户」,而不是误报「结构不认识」
120
+ found.push({ source: 'customData.mainTable', rows: [main] });
121
+ }
85
122
  // ② mainTable 里再套一层列表
86
123
  for (const key of LIST_KEYS) {
87
124
  const value = main[key];
@@ -95,6 +132,27 @@ function collectCandidateLists(customData) {
95
132
  if (Array.isArray(value))
96
133
  found.push({ source: `customData.${key}`, rows: value });
97
134
  }
135
+ /**
136
+ * ④ customData 下**任意**数组字段:动作流经常用表单字段 id 当 key
137
+ * (2026-11-02 实测:`customData["1173221220888059964"] = [客户1, 客户2, …]`)。
138
+ * 以前只认 LIST_KEYS 白名单,这类 key 直接被忽略 —— 于是「模糊查一个词命中 10 个客户」
139
+ * 只会返回第 1 个,用户看到的候选永远只有一个。
140
+ * 只收「元素看起来像客户行」(能取到 id 或名称)的数组,避免把别的明细当客户。
141
+ */
142
+ for (const [key, value] of Object.entries(customData)) {
143
+ if (LIST_KEYS.includes(key))
144
+ continue;
145
+ if (!Array.isArray(value) || value.length === 0)
146
+ continue;
147
+ const looksLikeCustomers = value.some((raw) => {
148
+ const row = unwrapRow(raw);
149
+ if (!row)
150
+ return false;
151
+ return pickFirst(row, ID_KEYS) !== '' || pickFirst(row, NAME_KEYS) !== '';
152
+ });
153
+ if (looksLikeCustomers)
154
+ found.push({ source: `customData.${key}`, rows: value });
155
+ }
98
156
  return found;
99
157
  }
100
158
  /** 客户行里「联系人明细 / 地址明细」的候选字段名(ESB 动作流输出)。 */
@@ -202,6 +260,30 @@ export function parseCrmAddresses(raw) {
202
260
  return out;
203
261
  }
204
262
  /** 把任意行数组转成客户数组(去重、丢空行)。 */
263
+ /**
264
+ * 取客户行里的「联系人明细」数组。
265
+ *
266
+ * 先按已知 key(`lxr` 等)找;找不到再按**内容特征**扫描 —— 动作流用表单字段 id 当 key 时
267
+ * (`1173221220888060716`),靠 key 名永远匹配不上,但里面每行都带姓名/手机号字段,
268
+ * 用 CONTACT_NAME_KEYS / CONTACT_MOBILE_KEYS 一认就知道是不是联系人子表。
269
+ */
270
+ function pickContactArray(row) {
271
+ const direct = pickRawList(row, CONTACT_LIST_KEYS);
272
+ if (direct !== undefined)
273
+ return direct;
274
+ for (const value of Object.values(row)) {
275
+ if (!Array.isArray(value) || value.length === 0)
276
+ continue;
277
+ const first = asRecord(value[0]);
278
+ if (!first)
279
+ continue;
280
+ const hasName = CONTACT_NAME_KEYS.some((key) => String(first[key] ?? '').trim() !== '');
281
+ const hasMobile = CONTACT_MOBILE_KEYS.some((key) => String(first[key] ?? '').trim() !== '');
282
+ if (hasName || hasMobile)
283
+ return value;
284
+ }
285
+ return undefined;
286
+ }
205
287
  export function normalizeCustomers(rows, limit) {
206
288
  const seen = new Map();
207
289
  for (const raw of rows) {
@@ -218,7 +300,7 @@ export function normalizeCustomers(rows, limit) {
218
300
  if (seen.has(key))
219
301
  continue;
220
302
  // 联系人与地址是客户行里的明细数组(lxr / khdz),不是客户列表本身 —— 别被 collectCandidateLists 当行用
221
- const contacts = parseCrmContacts(pickRawList(row, CONTACT_LIST_KEYS));
303
+ const contacts = parseCrmContacts(pickContactArray(row));
222
304
  const addresses = parseCrmAddresses(pickRawList(row, ADDRESS_LIST_KEYS));
223
305
  seen.set(key, {
224
306
  customerId,
@@ -412,7 +494,7 @@ const ebCrmSearch = {
412
494
  minLength: 1,
413
495
  description: '客户名称,走动作流入参 custom_name(包含匹配):给全称最稳,片段只回首个匹配',
414
496
  },
415
- limit: { type: 'number', description: '返回条数上限,默认 10(当前动作流一次只回 1 条,仅作兼容)' },
497
+ limit: { type: 'number', description: '返回条数上限,默认 10。动作流是模糊查,可能命中多个近名客户;列式输出(mc/sjID 平行数组)会按索引拆成多行返回' },
416
498
  flowId: { type: 'string', description: '覆盖 ESB 动作流 id(默认 904205116264026112)' },
417
499
  employeeId: { type: 'string', description: '覆盖触发人员 id(默认当前登录用户)' },
418
500
  includeRaw: { type: 'boolean', description: 'true=附带 ESB 原始返回,便于排查输出结构' },
@@ -212,6 +212,18 @@ function buildReadInputSchema(endpoint) {
212
212
  for (const param of endpoint.queryParams ?? []) {
213
213
  properties[param] = { type: 'string', description: `EB 筛选参数(URL 占位符名 ${param})` };
214
214
  }
215
+ // 便捷日期参数(2026-11-04):模型最常传的就是 date,别让它因为"没这个参数"去拿全量再下错结论。
216
+ if (endpoint.dateFilter) {
217
+ properties.date = {
218
+ type: 'string',
219
+ pattern: '^\\d{4}-\\d{2}-\\d{2}$',
220
+ description: '按日期过滤(yyyy-MM-dd):等价于 begindatetime=当天 00:00 + enddatetime=当天 23:59。'
221
+ + '⛔ 不传 date 且不传起止时间 = 返回**全量历史**(不是接口不支持按日期过滤)',
222
+ };
223
+ }
224
+ for (const [alias, target] of Object.entries(endpoint.paramAliases ?? {})) {
225
+ properties[alias] = { type: 'string', description: `入参别名,等价于 ${target}` };
226
+ }
215
227
  for (const param of endpoint.mainTableParams ?? []) {
216
228
  properties[param] = {
217
229
  type: 'string',
@@ -297,6 +309,27 @@ function buildReadOperation(endpoint) {
297
309
  if (value !== undefined && value !== null && value !== '')
298
310
  query[param] = value;
299
311
  }
312
+ // 别名落地(roomId→address 之类);只在目标参数没传时生效,不覆盖显式值
313
+ for (const [alias, target] of Object.entries(endpoint.paramAliases ?? {})) {
314
+ const value = args[alias];
315
+ if (value === undefined || value === null || value === '')
316
+ continue;
317
+ if (query[target] === undefined) {
318
+ if (target === 'date')
319
+ continue; // date 不是 query 参数,下面单独展开
320
+ query[target] = value;
321
+ }
322
+ }
323
+ // 便捷日期:date(或别名 day)→ 当天的 begindatetime/enddatetime
324
+ if (endpoint.dateFilter) {
325
+ const day = String(args.date ?? args.day ?? '').trim().slice(0, 10);
326
+ if (/^\d{4}-\d{2}-\d{2}$/.test(day)) {
327
+ if (query.begindatetime === undefined)
328
+ query.begindatetime = `${day} 00:00`;
329
+ if (query.enddatetime === undefined)
330
+ query.enddatetime = `${day} 23:59`;
331
+ }
332
+ }
300
333
  if (endpoint.kind === 'get') {
301
334
  const response = await env.host.request({
302
335
  method: 'POST',
@@ -0,0 +1,49 @@
1
+ import { readCache, writeCache, cacheScopeSuffix } from './cache.js';
2
+ const FLOW_PICK_TTL_MS = 30 * 24 * 60 * 60 * 1000;
3
+ function flowPickKey(scope) {
4
+ return `flow-pick-${scope}`;
5
+ }
6
+ /** 读当前登录人上次成功用的建会流程(换账号/换租户自动失效)。 */
7
+ export async function readFlowPick(env) {
8
+ const scope = await cacheScopeSuffix(env);
9
+ const hit = await readCache(env, flowPickKey(scope), { ttlMs: FLOW_PICK_TTL_MS });
10
+ if (!hit?.data?.workflowId)
11
+ return null;
12
+ try {
13
+ const session = await env.host.getSession();
14
+ const userId = String(session?.userId ?? '');
15
+ if (hit.data.userId && userId && hit.data.userId !== userId)
16
+ return null;
17
+ }
18
+ catch {
19
+ // 会话取不到就不做账号二次校验(缓存层已按租户隔离)
20
+ }
21
+ return hit.data;
22
+ }
23
+ /** 记住这次成功提交用的流程(提交成功后调用;失败不记)。 */
24
+ export async function rememberFlowPick(env, workflowId, name) {
25
+ const id = String(workflowId ?? '').trim();
26
+ if (id === '')
27
+ return;
28
+ let userId = '';
29
+ try {
30
+ const session = await env.host.getSession();
31
+ userId = String(session?.userId ?? '');
32
+ }
33
+ catch {
34
+ // 取不到就空着(读侧只在有值时才比对)
35
+ }
36
+ const scope = await cacheScopeSuffix(env);
37
+ await writeCache(env, flowPickKey(scope), {
38
+ workflowId: id,
39
+ ...(name ? { name: String(name) } : {}),
40
+ ...(userId ? { userId } : {}),
41
+ savedAt: Date.now(),
42
+ });
43
+ }
44
+ /** 纯函数:从可发起列表里挑出记忆命中的那条(没有/不命中 → null)。 */
45
+ export function pickRememberedFlow(launchable, remembered) {
46
+ if (!remembered?.workflowId)
47
+ return null;
48
+ return launchable.find((probe) => probe.workflowId === remembered.workflowId) ?? null;
49
+ }