weaver-work-cli 0.1.1 → 0.1.4

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 (351) hide show
  1. package/README.md +187 -76
  2. package/dist/cmd/setup/index.js +5 -5
  3. package/dist/cmd/skills/index.js +1 -1
  4. package/dist/core/hooks.js +7 -0
  5. package/dist/index.js +0 -0
  6. package/dist/internal/e10/auth/commands.js +160 -55
  7. package/dist/internal/e10/auth/crypto.js +7 -22
  8. package/dist/internal/e10/auth/session.js +1 -1
  9. package/dist/internal/e10/auth/xiaoe.js +268 -0
  10. package/dist/internal/e10/context.js +16 -2
  11. package/dist/internal/e10/index.js +1 -0
  12. package/dist/internal/e10/request-runtime.js +344 -0
  13. package/dist/internal/skills/install.js +48 -6
  14. package/dist/internal/skills/reader.js +27 -0
  15. package/dist/shortcuts/archive/continuation.js +70 -0
  16. package/dist/shortcuts/archive/errors.js +53 -0
  17. package/dist/shortcuts/archive/host.js +197 -0
  18. package/dist/shortcuts/archive/index.js +363 -0
  19. package/dist/shortcuts/archive/manifest.js +45 -0
  20. package/dist/shortcuts/archive/operations/archive-info.js +55 -0
  21. package/dist/shortcuts/archive/operations/borrow-car.js +146 -0
  22. package/dist/shortcuts/archive/operations/borrow-list.js +200 -0
  23. package/dist/shortcuts/archive/operations/confirm.js +46 -0
  24. package/dist/shortcuts/archive/operations/download.js +238 -0
  25. package/dist/shortcuts/archive/operations/flow-url.js +240 -0
  26. package/dist/shortcuts/archive/operations/fonds.js +95 -0
  27. package/dist/shortcuts/archive/operations/getPageUrl.js +137 -0
  28. package/dist/shortcuts/archive/operations/registry.js +43 -0
  29. package/dist/shortcuts/archive/operations/search.js +88 -0
  30. package/dist/shortcuts/archive/operations/shared.js +120 -0
  31. package/dist/shortcuts/archive/operations/types.js +23 -0
  32. package/dist/shortcuts/archive/operations/upload.js +182 -0
  33. package/dist/shortcuts/archive/operations.js +13 -0
  34. package/dist/shortcuts/archive/render/search-format.js +276 -0
  35. package/dist/shortcuts/archive/render/search-view.js +125 -0
  36. package/dist/shortcuts/asset/continuation.js +70 -0
  37. package/dist/shortcuts/asset/errors.js +67 -0
  38. package/dist/shortcuts/asset/host.js +438 -0
  39. package/dist/shortcuts/asset/index.js +255 -0
  40. package/dist/shortcuts/asset/manifest.js +471 -0
  41. package/dist/shortcuts/asset/operations/query.js +249 -0
  42. package/dist/shortcuts/asset/operations/registry.js +85 -0
  43. package/dist/shortcuts/asset/operations/shared.js +100 -0
  44. package/dist/shortcuts/asset/operations/type-cache.js +49 -0
  45. package/dist/shortcuts/asset/operations/types.js +1 -0
  46. package/dist/shortcuts/asset/operations/viewlink.js +117 -0
  47. package/dist/shortcuts/asset/operations/write.js +435 -0
  48. package/dist/shortcuts/asset/operations.js +9 -0
  49. package/dist/shortcuts/calendar/continuation.js +70 -0
  50. package/dist/shortcuts/calendar/errors.js +53 -0
  51. package/dist/shortcuts/calendar/host.js +148 -0
  52. package/dist/shortcuts/calendar/index.js +125 -0
  53. package/dist/shortcuts/calendar/manifest.js +26 -0
  54. package/dist/shortcuts/calendar/operations/read.js +259 -0
  55. package/dist/shortcuts/calendar/operations/registry.js +31 -0
  56. package/dist/shortcuts/calendar/operations/shared.js +132 -0
  57. package/dist/shortcuts/calendar/operations/types.js +25 -0
  58. package/dist/shortcuts/calendar/operations/write.js +439 -0
  59. package/dist/shortcuts/calendar/operations.js +8 -0
  60. package/dist/shortcuts/ehr/continuation.js +70 -0
  61. package/dist/shortcuts/ehr/errors.js +53 -0
  62. package/dist/shortcuts/ehr/host.js +213 -0
  63. package/dist/shortcuts/ehr/index.js +205 -0
  64. package/dist/shortcuts/ehr/manifest.js +47 -0
  65. package/dist/shortcuts/ehr/operations/attend-read.js +373 -0
  66. package/dist/shortcuts/ehr/operations/attend-write.js +514 -0
  67. package/dist/shortcuts/ehr/operations/hr-contact.js +363 -0
  68. package/dist/shortcuts/ehr/operations/hr-ds.js +298 -0
  69. package/dist/shortcuts/ehr/operations/hr-report.js +386 -0
  70. package/dist/shortcuts/ehr/operations/recruit-write.js +1629 -0
  71. package/dist/shortcuts/ehr/operations/recruit.js +803 -0
  72. package/dist/shortcuts/ehr/operations/registry.js +43 -0
  73. package/dist/shortcuts/ehr/operations/salary.js +319 -0
  74. package/dist/shortcuts/ehr/operations/shared.js +246 -0
  75. package/dist/shortcuts/ehr/operations/types.js +25 -0
  76. package/dist/shortcuts/ehr/operations.js +25 -0
  77. package/dist/shortcuts/esb/continuation.js +70 -0
  78. package/dist/shortcuts/esb/errors.js +65 -0
  79. package/dist/shortcuts/esb/host.js +187 -0
  80. package/dist/shortcuts/esb/index.js +112 -0
  81. package/dist/shortcuts/esb/manifest.js +91 -0
  82. package/dist/shortcuts/esb/operations/input-format.js +31 -0
  83. package/dist/shortcuts/esb/operations/registry.js +48 -0
  84. package/dist/shortcuts/esb/operations/shared.js +152 -0
  85. package/dist/shortcuts/esb/operations/trigger.js +76 -0
  86. package/dist/shortcuts/esb/operations/types.js +21 -0
  87. package/dist/shortcuts/esb/operations.js +15 -0
  88. package/dist/shortcuts/fna/continuation.js +70 -0
  89. package/dist/shortcuts/fna/errors.js +59 -0
  90. package/dist/shortcuts/fna/host.js +146 -0
  91. package/dist/shortcuts/fna/index.js +111 -0
  92. package/dist/shortcuts/fna/manifest.js +41 -0
  93. package/dist/shortcuts/fna/operations/budget.js +124 -0
  94. package/dist/shortcuts/fna/operations/expense-center.js +148 -0
  95. package/dist/shortcuts/fna/operations/invoice-writeoff.js +212 -0
  96. package/dist/shortcuts/fna/operations/loan.js +195 -0
  97. package/dist/shortcuts/fna/operations/registry.js +35 -0
  98. package/dist/shortcuts/fna/operations/shared.js +210 -0
  99. package/dist/shortcuts/fna/operations/types.js +25 -0
  100. package/dist/shortcuts/fna/operations.js +19 -0
  101. package/dist/shortcuts/hrm/errors.js +38 -0
  102. package/dist/shortcuts/hrm/host.js +156 -0
  103. package/dist/shortcuts/hrm/index.js +111 -0
  104. package/dist/shortcuts/hrm/manifest.js +32 -0
  105. package/dist/shortcuts/hrm/operations/employee.js +338 -0
  106. package/dist/shortcuts/hrm/operations/org.js +110 -0
  107. package/dist/shortcuts/hrm/operations/position.js +176 -0
  108. package/dist/shortcuts/hrm/operations/registry.js +33 -0
  109. package/dist/shortcuts/hrm/operations/shared.js +143 -0
  110. package/dist/shortcuts/hrm/operations/types.js +23 -0
  111. package/dist/shortcuts/hrm/operations.js +19 -0
  112. package/dist/shortcuts/index.js +20 -0
  113. package/dist/shortcuts/invoice/host.js +99 -94
  114. package/dist/shortcuts/invoice/index.js +65 -61
  115. package/dist/shortcuts/invoice/manifest.js +222 -14
  116. package/dist/shortcuts/invoice/operations/browse-field.js +17 -0
  117. package/dist/shortcuts/invoice/operations/issuing.js +198 -0
  118. package/dist/shortcuts/invoice/operations/ocr-preview.js +6 -3
  119. package/dist/shortcuts/invoice/operations/registry.js +17 -0
  120. package/dist/shortcuts/invoice/operations/reim.js +401 -0
  121. package/dist/shortcuts/invoice/operations/shared.js +271 -6
  122. package/dist/shortcuts/invoice/operations/validate-preview.js +1 -1
  123. package/dist/shortcuts/jiuchuanhui/cache.js +441 -0
  124. package/dist/shortcuts/jiuchuanhui/continuation.js +70 -0
  125. package/dist/shortcuts/jiuchuanhui/errors.js +59 -0
  126. package/dist/shortcuts/jiuchuanhui/host.js +159 -0
  127. package/dist/shortcuts/jiuchuanhui/index.js +109 -0
  128. package/dist/shortcuts/jiuchuanhui/manifest.js +469 -0
  129. package/dist/shortcuts/jiuchuanhui/operations/datajson.js +214 -0
  130. package/dist/shortcuts/jiuchuanhui/operations/esb.js +157 -0
  131. package/dist/shortcuts/jiuchuanhui/operations/generic.js +146 -0
  132. package/dist/shortcuts/jiuchuanhui/operations/registry.js +133 -0
  133. package/dist/shortcuts/jiuchuanhui/operations/shared.js +158 -0
  134. package/dist/shortcuts/jiuchuanhui/operations/types.js +1 -0
  135. package/dist/shortcuts/jiuchuanhui/operations.js +8 -0
  136. package/dist/shortcuts/mail/continuation.js +70 -0
  137. package/dist/shortcuts/mail/errors.js +53 -0
  138. package/dist/shortcuts/mail/host.js +182 -0
  139. package/dist/shortcuts/mail/index.js +519 -0
  140. package/dist/shortcuts/mail/manifest.js +38 -0
  141. package/dist/shortcuts/mail/operations/mail-blacklist.js +79 -0
  142. package/dist/shortcuts/mail/operations/mail-contacts.js +195 -0
  143. package/dist/shortcuts/mail/operations/mail-manage.js +335 -0
  144. package/dist/shortcuts/mail/operations/mail-read.js +508 -0
  145. package/dist/shortcuts/mail/operations/mail-send.js +474 -0
  146. package/dist/shortcuts/mail/operations/mail-sign.js +203 -0
  147. package/dist/shortcuts/mail/operations/mail-template.js +170 -0
  148. package/dist/shortcuts/mail/operations/registry.js +41 -0
  149. package/dist/shortcuts/mail/operations/shared.js +64 -0
  150. package/dist/shortcuts/mail/operations/types.js +27 -0
  151. package/dist/shortcuts/mail/operations.js +19 -0
  152. package/dist/shortcuts/plan/continuation.js +70 -0
  153. package/dist/shortcuts/plan/errors.js +70 -0
  154. package/dist/shortcuts/plan/host.js +180 -0
  155. package/dist/shortcuts/plan/index.js +150 -0
  156. package/dist/shortcuts/plan/manifest.js +53 -0
  157. package/dist/shortcuts/plan/operations/link.js +256 -0
  158. package/dist/shortcuts/plan/operations/read.js +259 -0
  159. package/dist/shortcuts/plan/operations/registry.js +33 -0
  160. package/dist/shortcuts/plan/operations/shared.js +246 -0
  161. package/dist/shortcuts/plan/operations/types.js +49 -0
  162. package/dist/shortcuts/plan/operations/version.js +49 -0
  163. package/dist/shortcuts/plan/operations/write.js +710 -0
  164. package/dist/shortcuts/plan/operations.js +8 -0
  165. package/docs/SKILL.md +11 -7
  166. package/docs/_catalog.md +23 -5
  167. package/docs/agent-invoice.md +31 -10
  168. package/docs/agent-skill-install.md +211 -0
  169. package/docs/calendar.md +87 -0
  170. package/docs/e10-auth.md +28 -9
  171. package/docs/ehr.md +186 -0
  172. package/docs/esb.md +64 -0
  173. package/docs/fna.md +72 -0
  174. package/docs/hrm.md +89 -0
  175. package/docs/invoice.md +66 -20
  176. package/docs/jiuchuanhui.md +131 -0
  177. package/docs/operation-manual.md +443 -0
  178. package/docs/plan.md +82 -0
  179. package/docs/wenshuding.md +129 -0
  180. package/docs/ziguanjia.md +89 -0
  181. package/package.json +4 -4
  182. package/scripts/package-skill.mjs +127 -34
  183. package/scripts/postbuild.mjs +13 -0
  184. package/skill-template/business-info.json +13 -2
  185. package/skill-template/domains/calendar.md +15 -0
  186. package/skill-template/domains/esb.md +13 -0
  187. package/skill-template/domains/hrm.md +16 -0
  188. package/skill-template/domains/jiuchuanhui.md +23 -0
  189. package/skill-template/domains/jucailin.md +22 -0
  190. package/skill-template/domains/mail.md +21 -0
  191. package/skill-template/domains/plan.md +17 -0
  192. package/skill-template/domains/qiyecheng.md +17 -0
  193. package/skill-template/domains/shared.md +9 -3
  194. package/skill-template/domains/skill-maker.md +10 -0
  195. package/skill-template/domains/wenshuding.md +22 -0
  196. package/skill-template/domains/{invoice.md → yepiaotong.md} +4 -0
  197. package/skill-template/domains/ziguanjia.md +21 -0
  198. package/skill-template/master-skill-template.md +12 -6
  199. package/skill-template/skill-template.md +40 -9
  200. package/skills/weaver-e10-calendar/SKILL.md +101 -0
  201. package/skills/weaver-e10-calendar/product.json +8 -0
  202. package/skills/weaver-e10-calendar/references/calendar-app-check.md +65 -0
  203. package/skills/weaver-e10-calendar/references/calendar-dict.md +55 -0
  204. package/skills/weaver-e10-calendar/references/calendar-query.md +70 -0
  205. package/skills/weaver-e10-calendar/references/calendar-write.md +97 -0
  206. package/skills/weaver-e10-calendar/references/source-manifest.json +54 -0
  207. package/skills/weaver-e10-esb/SKILL.md +102 -0
  208. package/skills/weaver-e10-esb/product.json +8 -0
  209. package/skills/weaver-e10-esb/references/input-format.md +52 -0
  210. package/skills/weaver-e10-esb/references/source-manifest.json +242 -0
  211. package/skills/weaver-e10-esb/references/trigger.md +99 -0
  212. package/skills/weaver-e10-hrm/SKILL.md +87 -0
  213. package/skills/weaver-e10-hrm/product.json +8 -0
  214. package/skills/weaver-e10-hrm/references/employee.md +109 -0
  215. package/skills/weaver-e10-hrm/references/org.md +95 -0
  216. package/skills/weaver-e10-hrm/references/position.md +95 -0
  217. package/skills/weaver-e10-hrm/references/source-manifest.json +121 -0
  218. package/skills/weaver-e10-jiuchuanhui/SKILL.md +174 -0
  219. package/skills/weaver-e10-jiuchuanhui/product.json +8 -0
  220. package/skills/weaver-e10-jiuchuanhui/references/cache.md +110 -0
  221. package/skills/weaver-e10-jiuchuanhui/references/clue.md +61 -0
  222. package/skills/weaver-e10-jiuchuanhui/references/contact-plan.md +56 -0
  223. package/skills/weaver-e10-jiuchuanhui/references/contact-records.md +48 -0
  224. package/skills/weaver-e10-jiuchuanhui/references/contact.md +56 -0
  225. package/skills/weaver-e10-jiuchuanhui/references/customer.md +69 -0
  226. package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +24 -0
  227. package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +60 -0
  228. package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +59 -0
  229. package/skills/weaver-e10-jiuchuanhui/references/jiuchuanhui-entry.md +60 -0
  230. package/skills/weaver-e10-jiuchuanhui/references/sale.md +72 -0
  231. package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +2632 -0
  232. package/skills/weaver-e10-jucailin/SKILL.md +101 -0
  233. package/skills/weaver-e10-jucailin/product.json +8 -0
  234. package/skills/weaver-e10-jucailin/references/attend-read.md +73 -0
  235. package/skills/weaver-e10-jucailin/references/attend-write.md +82 -0
  236. package/skills/weaver-e10-jucailin/references/hr-contact.md +92 -0
  237. package/skills/weaver-e10-jucailin/references/hr-datasource.md +104 -0
  238. package/skills/weaver-e10-jucailin/references/hr-report.md +76 -0
  239. package/skills/weaver-e10-jucailin/references/recruit-read.md +76 -0
  240. package/skills/weaver-e10-jucailin/references/recruit-write.md +70 -0
  241. package/skills/weaver-e10-jucailin/references/safety-boundaries.md +35 -0
  242. package/skills/weaver-e10-jucailin/references/salary.md +110 -0
  243. package/skills/weaver-e10-jucailin/references/source-manifest.json +152 -0
  244. package/skills/weaver-e10-mail/SKILL.md +151 -0
  245. package/skills/weaver-e10-mail/product.json +8 -0
  246. package/skills/weaver-e10-mail/references/mail-agent-entry.md +47 -0
  247. package/skills/weaver-e10-mail/references/mail-attachment.md +30 -0
  248. package/skills/weaver-e10-mail/references/mail-blacklist.md +27 -0
  249. package/skills/weaver-e10-mail/references/mail-contacts.md +33 -0
  250. package/skills/weaver-e10-mail/references/mail-manage.md +44 -0
  251. package/skills/weaver-e10-mail/references/mail-read.md +76 -0
  252. package/skills/weaver-e10-mail/references/mail-send.md +43 -0
  253. package/skills/weaver-e10-mail/references/mail-sign.md +33 -0
  254. package/skills/weaver-e10-mail/references/mail-template.md +31 -0
  255. package/skills/weaver-e10-mail/references/safety-boundaries.md +32 -0
  256. package/skills/weaver-e10-plan/SKILL.md +139 -0
  257. package/skills/weaver-e10-plan/product.json +8 -0
  258. package/skills/weaver-e10-plan/references/plan-collect-and-compose.md +123 -0
  259. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +138 -0
  260. package/skills/weaver-e10-plan/references/plan-report-query.md +154 -0
  261. package/skills/weaver-e10-plan/references/plan-report-remind.md +104 -0
  262. package/skills/weaver-e10-plan/references/plan-report-write.md +162 -0
  263. package/skills/weaver-e10-plan/references/source-manifest.json +858 -0
  264. package/skills/weaver-e10-qiyecheng/SKILL.md +118 -0
  265. package/skills/weaver-e10-qiyecheng/product.json +8 -0
  266. package/skills/weaver-e10-qiyecheng/references/budget.md +65 -0
  267. package/skills/weaver-e10-qiyecheng/references/catalog.md +56 -0
  268. package/skills/weaver-e10-qiyecheng/references/expense-center.md +51 -0
  269. package/skills/weaver-e10-qiyecheng/references/invoice-writeoff.md +57 -0
  270. package/skills/weaver-e10-qiyecheng/references/loan-management.md +59 -0
  271. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +698 -0
  272. package/skills/weaver-e10-shared/SKILL.md +86 -0
  273. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +127 -0
  274. package/skills/weaver-e10-shared/references/json-output-contract.md +120 -0
  275. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +110 -0
  276. package/skills/weaver-e10-skill-maker/SKILL.md +77 -0
  277. package/skills/weaver-e10-skill-maker/product.json +8 -0
  278. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +124 -0
  279. package/skills/weaver-e10-skill-maker/references/detector-validation.md +101 -0
  280. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +230 -0
  281. package/skills/weaver-e10-skill-maker/references/post-generation-install.md +105 -0
  282. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +261 -0
  283. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +141 -0
  284. package/skills/weaver-e10-wenshuding/SKILL.md +157 -0
  285. package/skills/weaver-e10-wenshuding/product.json +8 -0
  286. package/skills/weaver-e10-wenshuding/references/archive-agent-entry.md +63 -0
  287. package/skills/weaver-e10-wenshuding/references/archive-borrow-car.md +52 -0
  288. package/skills/weaver-e10-wenshuding/references/archive-borrow-list.md +52 -0
  289. package/skills/weaver-e10-wenshuding/references/archive-disabled-capabilities.md +22 -0
  290. package/skills/weaver-e10-wenshuding/references/archive-download.md +48 -0
  291. package/skills/weaver-e10-wenshuding/references/archive-flow.md +46 -0
  292. package/skills/weaver-e10-wenshuding/references/archive-fonds.md +34 -0
  293. package/skills/weaver-e10-wenshuding/references/archive-info.md +33 -0
  294. package/skills/weaver-e10-wenshuding/references/archive-search.md +52 -0
  295. package/skills/weaver-e10-wenshuding/references/archive-upload.md +41 -0
  296. package/skills/weaver-e10-wenshuding/references/source-manifest.json +517 -0
  297. package/skills/weaver-e10-yepiaotong/SKILL.md +184 -0
  298. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-add.md +1 -1
  299. package/skills/weaver-e10-yepiaotong/references/invoice-agent-entry.md +47 -0
  300. package/skills/weaver-e10-yepiaotong/references/invoice-browse-field-data.md +204 -0
  301. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-delete.md +1 -1
  302. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-detail.md +13 -5
  303. package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +28 -0
  304. package/skills/weaver-e10-yepiaotong/references/invoice-download.md +31 -0
  305. package/skills/weaver-e10-yepiaotong/references/invoice-enterprise-list.md +45 -0
  306. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-file-upload.md +15 -4
  307. package/skills/weaver-e10-yepiaotong/references/invoice-import.md +45 -0
  308. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +120 -0
  309. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +161 -0
  310. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +99 -0
  311. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-ocr-preview.md +14 -4
  312. package/skills/weaver-e10-yepiaotong/references/invoice-personal-list.md +58 -0
  313. package/skills/weaver-e10-yepiaotong/references/invoice-red.md +261 -0
  314. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +97 -0
  315. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-update.md +1 -1
  316. package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/references/invoice-validation-preview.md +10 -3
  317. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +1340 -0
  318. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +845 -0
  319. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +94 -0
  320. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +449 -0
  321. package/skills/weaver-e10-yepiaotong/references/reim-matching-rules.md +266 -0
  322. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +494 -0
  323. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1103 -0
  324. package/skills/weaver-e10-ziguanjia/SKILL.md +162 -0
  325. package/skills/weaver-e10-ziguanjia/product.json +8 -0
  326. package/skills/weaver-e10-ziguanjia/references/source-manifest.json +147 -0
  327. package/skills/weaver-e10-ziguanjia/references/ziguanjia-agent-entry.md +65 -0
  328. package/skills/weaver-e10-ziguanjia/references/ziguanjia-create.md +45 -0
  329. package/skills/weaver-e10-ziguanjia/references/ziguanjia-depre.md +48 -0
  330. package/skills/weaver-e10-ziguanjia/references/ziguanjia-disabled-capabilities.md +22 -0
  331. package/skills/weaver-e10-ziguanjia/references/ziguanjia-purch.md +57 -0
  332. package/skills/weaver-e10-ziguanjia/references/ziguanjia-query.md +62 -0
  333. package/skills/weaver-e10-ziguanjia/references/ziguanjia-repair.md +83 -0
  334. package/skills/weaver-e10-ziguanjia/references/ziguanjia-resolve.md +49 -0
  335. package/skills/weaver-e10-ziguanjia/references/ziguanjia-return.md +70 -0
  336. package/skills/weaver-e10-ziguanjia/references/ziguanjia-update.md +45 -0
  337. package/skills/weaver-e10-ziguanjia/references/ziguanjia-use.md +60 -0
  338. package/skills/weaver-e10-ziguanjia/references/ziguanjia-viewlink.md +81 -0
  339. package/skills/weaver-work-cli-invoice/SKILL.md +0 -103
  340. package/skills/weaver-work-cli-invoice/references/invoice-agent-entry.md +0 -38
  341. package/skills/weaver-work-cli-invoice/references/invoice-disabled-capabilities.md +0 -26
  342. package/skills/weaver-work-cli-invoice/references/invoice-download.md +0 -22
  343. package/skills/weaver-work-cli-invoice/references/invoice-enterprise-list.md +0 -35
  344. package/skills/weaver-work-cli-invoice/references/invoice-import.md +0 -28
  345. package/skills/weaver-work-cli-invoice/references/invoice-personal-list.md +0 -47
  346. package/skills/weaver-work-cli-shared/SKILL.md +0 -53
  347. package/skills/weaver-work-cli-shared/references/e10-auth-and-session.md +0 -83
  348. package/skills/weaver-work-cli-shared/references/json-output-contract.md +0 -61
  349. package/skills/weaver-work-cli-shared/references/weaver-work-cli-installation.md +0 -61
  350. /package/skills/{weaver-work-cli-shared → weaver-e10-shared}/references/high-risk-write.md +0 -0
  351. /package/skills/{weaver-work-cli-invoice → weaver-e10-yepiaotong}/product.json +0 -0
@@ -0,0 +1,120 @@
1
+ import { archiveValidationError } from '../errors.js';
2
+ /** 原型污染防护:禁止出现在输入对象中的键。 */
3
+ const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
4
+ export function asObject(value) {
5
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
6
+ throw archiveValidationError('input_not_object', '输入必须是 JSON 对象');
7
+ }
8
+ return value;
9
+ }
10
+ export function assertNoForbiddenKeys(input) {
11
+ for (const key of Object.keys(input)) {
12
+ if (FORBIDDEN_KEYS.has(key)) {
13
+ throw archiveValidationError('forbidden_key', `输入包含非法字段:${key}`);
14
+ }
15
+ }
16
+ }
17
+ /** 移除值为 undefined 的键,避免把空值写进请求体。 */
18
+ export function compact(body) {
19
+ for (const key of Object.keys(body)) {
20
+ if (body[key] === undefined)
21
+ delete body[key];
22
+ }
23
+ return body;
24
+ }
25
+ export function pickDefined(value, keys) {
26
+ const result = {};
27
+ for (const key of keys) {
28
+ if (value[key] !== undefined)
29
+ result[key] = value[key];
30
+ }
31
+ return result;
32
+ }
33
+ /** 取出响应中的业务数据。 */
34
+ export function unwrapData(response) {
35
+ if (!response || typeof response !== 'object')
36
+ return response;
37
+ return response.data;
38
+ }
39
+ /**
40
+ * 取出 ebuilder 自定义接口(getFormDataList / getFormDataByPk)的 datajson 业务体。
41
+ * 这类接口把业务状态放在 `datajson.status`,数据放在 `datajson.datas`。
42
+ */
43
+ export function unwrapDatajson(response) {
44
+ if (!response || typeof response !== 'object')
45
+ return undefined;
46
+ const value = response;
47
+ if (value.datajson && typeof value.datajson === 'object')
48
+ return value.datajson;
49
+ const data = value.data;
50
+ if (data && typeof data === 'object' && data.datajson) {
51
+ return data.datajson;
52
+ }
53
+ return undefined;
54
+ }
55
+ /** 正整数入参(分页页码/条数)。 */
56
+ export function positiveInt(value, field, fallback) {
57
+ if (value === undefined || value === null || value === '')
58
+ return fallback;
59
+ const parsed = Number(value);
60
+ if (!Number.isInteger(parsed) || parsed <= 0) {
61
+ throw archiveValidationError('number_invalid', `${field} 必须是正整数`);
62
+ }
63
+ return parsed;
64
+ }
65
+ /** 非空字符串,供必填提示。 */
66
+ export function requireText(value, field) {
67
+ if (typeof value !== 'string' || !value.trim()) {
68
+ throw archiveValidationError('field_required', `${field} 必填`);
69
+ }
70
+ return value.trim();
71
+ }
72
+ /** 字符串 ID(允许纯数字字符串)。 */
73
+ export function toStringId(value, field) {
74
+ if (value === undefined || value === null || value === '')
75
+ return undefined;
76
+ const text = typeof value === 'string' ? value.trim() : String(value);
77
+ if (!/^\d+$/.test(text))
78
+ throw archiveValidationError('id_invalid', `${field} 必须是数字 ID`);
79
+ return text;
80
+ }
81
+ /** 数组内每个元素必须是数字 ID,返回字符串列表(部分接口要求字符串 id)。 */
82
+ export function toStringIdList(value, field) {
83
+ if (value === undefined || value === null)
84
+ return undefined;
85
+ if (!Array.isArray(value))
86
+ throw archiveValidationError('id_list_invalid', `${field} 必须是 ID 数组`);
87
+ return value.map((item, index) => {
88
+ const text = toStringId(item, `${field}[${index}]`);
89
+ if (text === undefined)
90
+ throw archiveValidationError('id_list_invalid', `${field}[${index}] 不能为空`);
91
+ return text;
92
+ });
93
+ }
94
+ /** 非空字符串数组。 */
95
+ export function requireStringList(value, field) {
96
+ if (!Array.isArray(value) || value.length === 0) {
97
+ throw archiveValidationError('list_required', `${field} 必填且至少一项`);
98
+ }
99
+ return value.map((item, index) => {
100
+ if (typeof item !== 'string' || !item.trim()) {
101
+ throw archiveValidationError('list_invalid', `${field}[${index}] 必须是字符串`);
102
+ }
103
+ return item.trim();
104
+ });
105
+ }
106
+ /** 校验数组内每个元素都在枚举内。 */
107
+ export function assertEnum(value, allowed, field) {
108
+ const text = typeof value === 'string' ? value : String(value);
109
+ if (!allowed.includes(text)) {
110
+ throw archiveValidationError('enum_invalid', `${field} 取值必须是 ${allowed.join(' / ')} 之一`);
111
+ }
112
+ return text;
113
+ }
114
+ /** 安全布尔。 */
115
+ export function requireBoolean(value, field) {
116
+ if (typeof value !== 'boolean') {
117
+ throw archiveValidationError('boolean_invalid', `${field} 必须是布尔值`);
118
+ }
119
+ return value;
120
+ }
@@ -0,0 +1,23 @@
1
+ export const READ = 'read';
2
+ export const WRITE = 'write';
3
+ export function archiveSuccess(action, data, meta) {
4
+ const payload = { ok: true, action, data };
5
+ if (meta && Object.keys(meta).length > 0)
6
+ payload.meta = meta;
7
+ return payload;
8
+ }
9
+ /** 构造「对象类型 + 严格禁止额外字段」的 JSON Schema。 */
10
+ export function schemaOf(properties, options = {}) {
11
+ return {
12
+ type: 'object',
13
+ additionalProperties: false,
14
+ properties,
15
+ ...(options.required?.length ? { required: options.required } : {}),
16
+ ...(options.description ? { description: options.description } : {}),
17
+ };
18
+ }
19
+ export const SCHEMA_INTEGER_POSITIVE = { type: 'integer', minimum: 1 };
20
+ export const SCHEMA_DATE = { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' };
21
+ export const SCHEMA_ID = { oneOf: [{ type: 'integer' }, { type: 'string', pattern: '^\\d+$' }] };
22
+ export const SCHEMA_ID_LIST = { type: 'array', items: SCHEMA_ID };
23
+ export const SCHEMA_STRING_LIST = { type: 'array', items: { type: 'string' } };
@@ -0,0 +1,182 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { stat } from 'node:fs/promises';
3
+ import { basename, resolve } from 'node:path';
4
+ import { readFile } from 'node:fs/promises';
5
+ import { archiveValidationError } from '../errors.js';
6
+ import { READ, WRITE, archiveSuccess, schemaOf, } from './types.js';
7
+ import { unwrapData } from './shared.js';
8
+ import { assertLeaseTarget, issueLease, verifyLease } from './confirm.js';
9
+ const UPLOAD_APPLY = 'archive.upload.prelib.apply';
10
+ const MAX_UPLOAD_BYTES = 200 * 1024 * 1024;
11
+ function isEfileLibNode(item) {
12
+ const id = typeof item?.id === 'string' ? item.id : '';
13
+ if (!id || !id.includes('_'))
14
+ return false;
15
+ const [left, right] = id.split('_');
16
+ return Boolean(left && left === right);
17
+ }
18
+ /** treeId 未提供时,从预归档树中取唯一的电子文件库节点。 */
19
+ async function resolveDefaultTreeId(host) {
20
+ const response = await host.request({
21
+ method: 'POST',
22
+ path: '/api/archive/core/archivetree/getTree',
23
+ body: { treeType: '4', menuSign: 'preLib', logFlag: 'preLib', needControl: true, needSysn: true },
24
+ });
25
+ const data = unwrapData(response);
26
+ if (!Array.isArray(data)) {
27
+ throw archiveValidationError('tree_invalid', 'getTree 返回异常:data 不是数组');
28
+ }
29
+ const nodes = data.filter(isEfileLibNode);
30
+ if (nodes.length === 0) {
31
+ throw archiveValidationError('tree_id_required', '未找到电子文件库节点,请显式传入 treeId');
32
+ }
33
+ if (nodes.length > 1) {
34
+ throw archiveValidationError('tree_id_ambiguous', `存在多个电子文件库节点(${nodes.map((item) => item.id).join('、')}),请显式传入 treeId`);
35
+ }
36
+ return String(nodes[0].id);
37
+ }
38
+ const uploadPrepare = {
39
+ name: 'archive.upload.prelib.prepare',
40
+ resource: 'upload',
41
+ method: 'prelib',
42
+ action: 'archive.upload.prelib.prepare',
43
+ summary: '预归档上传预检:校验本地文件与目标全宗/电子文件库节点,并生成确认票据(不上传)',
44
+ capability: 'archive',
45
+ risk: READ,
46
+ inputSchema: schemaOf({
47
+ file: { type: 'string', description: '本地文件路径' },
48
+ fondsId: { type: 'string', pattern: '^\\d+$', description: '目标全宗 ID(archive.fonds.list 返回的 id)' },
49
+ treeId: { type: 'string', description: '可选,电子文件库节点 id;不传时取唯一的电子文件库节点' },
50
+ }, { required: ['file', 'fondsId'], description: '预归档上传预检' }),
51
+ handler: async (args, env) => {
52
+ const filePath = typeof args.file === 'string' && args.file.trim() ? resolve(args.file.trim()) : '';
53
+ if (!filePath)
54
+ throw archiveValidationError('file_required', 'file 必填(本地文件路径)');
55
+ const fondsId = String(args.fondsId ?? '').trim();
56
+ if (!/^\d+$/.test(fondsId))
57
+ throw archiveValidationError('fonds_id_invalid', 'fondsId 必填且为数字 ID');
58
+ let info;
59
+ try {
60
+ info = await stat(filePath);
61
+ }
62
+ catch {
63
+ throw archiveValidationError('file_unreadable', `无法读取本地文件:${filePath}`);
64
+ }
65
+ if (!info.isFile())
66
+ throw archiveValidationError('file_invalid', `不是文件:${filePath}`);
67
+ if (info.size === 0)
68
+ throw archiveValidationError('file_empty', `文件为空:${filePath}`);
69
+ if (info.size > MAX_UPLOAD_BYTES) {
70
+ throw archiveValidationError('file_too_large', `文件超过 ${MAX_UPLOAD_BYTES} 字节上限:${filePath}`);
71
+ }
72
+ const treeId = args.treeId === undefined || args.treeId === null || args.treeId === ''
73
+ ? await resolveDefaultTreeId(env.host)
74
+ : String(args.treeId);
75
+ const { continuation, expiresInMs } = await issueLease(env, UPLOAD_APPLY, {
76
+ file: filePath,
77
+ fileName: basename(filePath),
78
+ size: info.size,
79
+ fondsId,
80
+ treeId,
81
+ });
82
+ return archiveSuccess('archive.upload.prelib.prepare', {
83
+ preview: {
84
+ file: filePath,
85
+ fileName: basename(filePath),
86
+ size: info.size,
87
+ fondsId,
88
+ treeId,
89
+ },
90
+ hint: '确认后调用 archive.upload.prelib.apply(CLI:archive upload apply --continuation <token> --yes)',
91
+ continuation,
92
+ }, { requiresConfirmation: true, expiresInMs });
93
+ },
94
+ };
95
+ const uploadApply = {
96
+ name: UPLOAD_APPLY,
97
+ resource: 'upload',
98
+ method: 'prelib',
99
+ action: UPLOAD_APPLY,
100
+ summary: '预归档上传(写操作):preUploadCheck → module/upload → saveFile,把本地文件存入预归档电子文件库',
101
+ capability: 'archive',
102
+ risk: WRITE,
103
+ requiresConfirmation: true,
104
+ inputSchema: schemaOf({
105
+ continuation: { type: 'string', description: 'prepare 返回的确认票据' },
106
+ confirm: { type: 'boolean', description: '必须为 true(CLI --yes)' },
107
+ fondsId: { type: 'string', description: '可选回传,用于与 prepare 快照比对' },
108
+ }, { required: ['continuation', 'confirm'], description: '预归档上传' }),
109
+ handler: async (args, env) => {
110
+ const lease = await verifyLease(args, UPLOAD_APPLY, env);
111
+ const filePath = String(lease.file ?? '');
112
+ const fileName = String(lease.fileName ?? basename(filePath));
113
+ const fondsId = String(lease.fondsId ?? '');
114
+ const treeId = String(lease.treeId ?? '');
115
+ if (!filePath || !fondsId || !treeId) {
116
+ throw archiveValidationError('continuation_payload_invalid', '确认票据缺少 file / fondsId / treeId');
117
+ }
118
+ assertLeaseTarget(lease, args, ['fondsId'], UPLOAD_APPLY);
119
+ const content = await readFile(filePath);
120
+ if (content.length === 0)
121
+ throw archiveValidationError('file_empty', `文件为空:${filePath}`);
122
+ const md5 = createHash('md5').update(content).digest('hex');
123
+ const lastModified = Date.now();
124
+ const checkForm = new FormData();
125
+ checkForm.append('module', 'archive');
126
+ checkForm.append('name', fileName);
127
+ checkForm.append('size', String(content.length));
128
+ checkForm.append('MD5', md5);
129
+ checkForm.append('lastModified', String(lastModified));
130
+ checkForm.append('lastModifiedDate', String(lastModified));
131
+ checkForm.append('compressImg', 'false');
132
+ const check = await env.host.request({
133
+ method: 'POST',
134
+ path: '/api/file/preUploadCheck',
135
+ multipart: checkForm,
136
+ });
137
+ const checkData = (unwrapData(check) ?? {});
138
+ if (checkData.permissionType?.upload === false) {
139
+ throw archiveValidationError('upload_forbidden', '当前账号没有上传权限');
140
+ }
141
+ const uploadForm = new FormData();
142
+ uploadForm.append('module', 'archive');
143
+ uploadForm.append('name', fileName);
144
+ uploadForm.append('size', String(content.length));
145
+ uploadForm.append('MD5', md5);
146
+ uploadForm.append('lastModified', String(lastModified));
147
+ uploadForm.append('lastModifiedDate', String(lastModified));
148
+ uploadForm.append('compressImg', 'false');
149
+ uploadForm.append('file', new Blob([content], { type: 'application/octet-stream' }), fileName);
150
+ const uploaded = await env.host.request({
151
+ method: 'POST',
152
+ path: '/api/file/module/upload',
153
+ multipart: uploadForm,
154
+ });
155
+ const uploadData = (unwrapData(uploaded) ?? {});
156
+ const fileId = String(uploadData.fileid ?? '');
157
+ if (!fileId)
158
+ throw archiveValidationError('upload_failed', '文件上传失败,未取到 fileid');
159
+ await env.host.request({
160
+ method: 'POST',
161
+ path: '/api/archive/core/intelligent/saveFile',
162
+ body: {
163
+ docIds: fileId,
164
+ docNames: String(uploadData.name ?? fileName),
165
+ treeId,
166
+ fonds_code: fondsId,
167
+ logFlag: 'preLib',
168
+ },
169
+ });
170
+ return archiveSuccess(UPLOAD_APPLY, {
171
+ saved: true,
172
+ fileId,
173
+ docId: String(uploadData.docId ?? ''),
174
+ fileName: String(uploadData.name ?? fileName),
175
+ size: Number(uploadData.size ?? content.length),
176
+ treeId,
177
+ fondsId,
178
+ md5,
179
+ }, { wrote: true });
180
+ },
181
+ };
182
+ export const uploadOperations = [uploadPrepare, uploadApply];
@@ -0,0 +1,13 @@
1
+ import { getArchiveOperation } from './operations/registry.js';
2
+ import { asObject, assertNoForbiddenKeys } from './operations/shared.js';
3
+ /**
4
+ * 执行一个档案操作。操作定义(含入参 schema、风险等级、是否必须确认)
5
+ * 全部来自 operations/registry,CLI 侧只负责校验输入形状。
6
+ */
7
+ export async function executeArchiveOperation(operation, rawInput, env) {
8
+ const definition = getArchiveOperation(operation);
9
+ const input = asObject(rawInput);
10
+ assertNoForbiddenKeys(input);
11
+ return definition.handler(input, { ctx: env.ctx, host: env.host, continuation: env.continuation });
12
+ }
13
+ export { archiveOperationDefinitions, getArchiveOperation, hasArchiveOperation } from './operations/registry.js';
@@ -0,0 +1,276 @@
1
+ import { toSearchRowView, withBaseUrl } from './search-view.js';
2
+ function escapeHtml(value) {
3
+ return String(value)
4
+ .replace(/&/g, '&amp;')
5
+ .replace(/</g, '&lt;')
6
+ .replace(/>/g, '&gt;')
7
+ .replace(/"/g, '&quot;');
8
+ }
9
+ /** 行字段是否齐全(能拼出可打开链接)。 */
10
+ function hasLink(view) {
11
+ return Boolean(view.id && view.formId);
12
+ }
13
+ function describeDate(view) {
14
+ return view.dateLabel && view.dateValue ? `${view.dateLabel}:${view.dateValue}` : '';
15
+ }
16
+ function textMetaItems(view) {
17
+ const parts = [];
18
+ parts.push(`全宗:${view.fonds || '-'}`);
19
+ parts.push(`分类:${view.category || '-'}`);
20
+ const datePart = describeDate(view);
21
+ if (datePart)
22
+ parts.push(datePart);
23
+ return parts;
24
+ }
25
+ /** 单行文本:`1. 【原文】 题名` */
26
+ function textTitleLine(index, view) {
27
+ return `${index}. 【${view.level || '未分类'}】 ${view.title || '(无题名)'}`;
28
+ }
29
+ /** 单行摘要:`全宗:x 分类:y 文件形成时间:z` */
30
+ function textMetaLine(view) {
31
+ return textMetaItems(view).join(' ');
32
+ }
33
+ function pageLabel(meta) {
34
+ if (!meta)
35
+ return '';
36
+ const pageNo = Number(meta.pageNo);
37
+ const totalPage = Number(meta.totalPage);
38
+ const bits = [];
39
+ if (meta.total !== undefined)
40
+ bits.push(`命中 ${meta.total} 条`);
41
+ if (!Number.isNaN(pageNo))
42
+ bits.push(`第 ${pageNo + 1} 页`);
43
+ if (!Number.isNaN(totalPage) && totalPage > 0)
44
+ bits.push(`共 ${totalPage} 页`);
45
+ return bits.join(' · ');
46
+ }
47
+ /** 取原行命中高亮原文(含 <em>),无则回退已剥标签的展示摘要。 */
48
+ function highlightSourceOf(raw, view) {
49
+ const direct = String(raw.highLightContent ?? raw.content ?? '').trim();
50
+ return direct || view.highlight;
51
+ }
52
+ const EMPH_OPEN = '\u0001';
53
+ const EMPH_CLOSE = '\u0002';
54
+ function segmentsOfHighlight(value) {
55
+ // 先合并被拆开的高亮字:<em>会</em><em>计</em> -> <em>会计</em>,避免输出 **会****计**
56
+ const source = String(value ?? '')
57
+ .replace(/<\/em>\s*<em>/gi, '')
58
+ .replace(/<em>([\s\S]*?)<\/em>/gi, (_all, inner) => `${EMPH_OPEN}${inner}${EMPH_CLOSE}`)
59
+ .replace(/<[^>]+>/g, ' ');
60
+ const segments = [];
61
+ let buffer = '';
62
+ let inEm = false;
63
+ const flush = () => {
64
+ const text = buffer
65
+ .replace(/&nbsp;/g, ' ')
66
+ .replace(/&amp;/g, '&')
67
+ .replace(/&lt;/g, '<')
68
+ .replace(/&gt;/g, '>')
69
+ .replace(/&quot;/g, '"')
70
+ .replace(/&#0*39;/g, "'")
71
+ .replace(/\s+/g, ' ')
72
+ .trim();
73
+ if (text)
74
+ segments.push({ em: inEm, text });
75
+ buffer = '';
76
+ };
77
+ for (const char of source) {
78
+ if (char === EMPH_OPEN) {
79
+ flush();
80
+ inEm = true;
81
+ continue;
82
+ }
83
+ if (char === EMPH_CLOSE) {
84
+ flush();
85
+ inEm = false;
86
+ continue;
87
+ }
88
+ buffer += char;
89
+ }
90
+ flush();
91
+ return segments;
92
+ }
93
+ /** Markdown 高亮:`<em>词</em>` -> `**词**`,同时压行到 max 且不切断强调标记。 */
94
+ function markdownHighlight(value, max = 200) {
95
+ const segments = segmentsOfHighlight(value);
96
+ let out = '';
97
+ let remaining = max;
98
+ let truncated = false;
99
+ const isWordChar = (char) => Boolean(char && /[\w\u3400-\u9fff]/.test(char));
100
+ for (let i = 0; i < segments.length; i += 1) {
101
+ const segment = segments[i];
102
+ if (remaining <= 0) {
103
+ truncated = true;
104
+ break;
105
+ }
106
+ if (segment.em) {
107
+ const overhead = '**__**'.length - 2;
108
+ const bodyMax = Math.max(1, remaining - overhead);
109
+ const body = segment.text.slice(0, bodyMax);
110
+ if (body.length < segment.text.length)
111
+ truncated = true;
112
+ // Markdown 强调两侧需与字面词隔开才能被识别,补一个空格保护
113
+ const leadPad = isWordChar(out.slice(-1)) ? ' ' : '';
114
+ const tailPad = isWordChar(segments[i + 1]?.text[0]) ? ' ' : '';
115
+ out += `${leadPad}**${body}**${tailPad}`;
116
+ remaining -= overhead + body.length + leadPad.length + tailPad.length;
117
+ }
118
+ else {
119
+ const take = Math.min(segment.text.length, remaining);
120
+ out += segment.text.slice(0, take);
121
+ remaining -= take;
122
+ if (take < segment.text.length) {
123
+ truncated = true;
124
+ break;
125
+ }
126
+ }
127
+ }
128
+ return (truncated ? `${out.replace(/…$/, '')}…` : out).trim();
129
+ }
130
+ /** HTML 高亮:`<em>词</em>` -> `<mark>词</mark>`,其余内容转义。 */
131
+ function htmlHighlight(value, max = 300) {
132
+ const segments = segmentsOfHighlight(value);
133
+ let out = '';
134
+ let remaining = max;
135
+ let truncated = false;
136
+ for (const segment of segments) {
137
+ if (remaining <= 0) {
138
+ truncated = true;
139
+ break;
140
+ }
141
+ if (segment.em) {
142
+ const overhead = '<mark>__</mark>'.length - 2;
143
+ const bodyMax = Math.max(1, remaining - overhead);
144
+ const body = segment.text.slice(0, bodyMax);
145
+ if (body.length < segment.text.length)
146
+ truncated = true;
147
+ out += `<mark>${escapeHtml(body)}</mark>`;
148
+ remaining -= overhead + body.length;
149
+ }
150
+ else {
151
+ const take = Math.min(segment.text.length, remaining);
152
+ out += escapeHtml(segment.text.slice(0, take));
153
+ remaining -= take;
154
+ if (take < segment.text.length) {
155
+ truncated = true;
156
+ break;
157
+ }
158
+ }
159
+ }
160
+ return truncated ? `${out.replace(/…$/, '')}…` : out;
161
+ }
162
+ /* ------------------------------------------------------------------ *
163
+ * 三段式纯文本(终端 / 精简场景)
164
+ * ------------------------------------------------------------------ */
165
+ export function formatSearchText(rows, meta = undefined) {
166
+ const views = rows.map((row) => toSearchRowView(row));
167
+ const keyword = String(meta?.keyword ?? '').trim();
168
+ const headerBits = [];
169
+ if (keyword)
170
+ headerBits.push(`「${keyword}」检索`);
171
+ const pages = pageLabel(meta);
172
+ if (pages)
173
+ headerBits.push(pages);
174
+ const lines = [];
175
+ if (headerBits.length)
176
+ lines.push(headerBits.join(' | '));
177
+ if (lines.length)
178
+ lines.push('='.repeat(60));
179
+ views.forEach((view, index) => {
180
+ lines.push(textTitleLine(index + 1, view));
181
+ if (view.highlight)
182
+ lines.push(` ${view.highlight}`);
183
+ lines.push(` ${textMetaLine(view)}`);
184
+ if (index < views.length - 1)
185
+ lines.push('');
186
+ });
187
+ return lines.join('\n');
188
+ }
189
+ /* ------------------------------------------------------------------ *
190
+ * 对话内 Markdown 卡片:标题可点击、命中词加粗高亮,直接粘贴进回复。
191
+ * 版式(每条固定三段,段落之间用空行分隔,保证任意 Markdown 渲染下都分列显示):
192
+ * 1. **【原文】** [题名](http://host/sp/custom/archive/archiveView?...) ← 第一段
193
+ *
194
+ * 命中正文摘要(**高亮词**,压行 ~200 字) ← 第二段
195
+ *
196
+ * 全宗:xxx 分类:xxx 文件形成时间:2026-01-23 ← 第三段
197
+ * 注意:三段间必须空行,单换行在 Markdown 是软换行会被折叠成空格。
198
+ * ------------------------------------------------------------------ */
199
+ export function formatSearchCard(rows, options = {}) {
200
+ const baseUrl = String(options.baseUrl ?? '').trim().replace(/\/+$/, '');
201
+ const items = rows.map((row) => {
202
+ const raw = row;
203
+ const view = toSearchRowView(raw);
204
+ return { raw, view: view.viewPath && baseUrl ? withBaseUrl(view, baseUrl) : view };
205
+ });
206
+ const blocks = [];
207
+ items.forEach(({ raw, view }, index) => {
208
+ const url = view.viewUrl || view.viewPath || '';
209
+ const titleLink = url
210
+ ? `[${view.title || '(无题名)'}](${url})`
211
+ : view.title || '(无题名)';
212
+ const parts = [];
213
+ parts.push(`${index + 1}. **【${view.level || '未分类'}】** ${titleLink}`);
214
+ const highlight = markdownHighlight(highlightSourceOf(raw, view));
215
+ if (highlight) {
216
+ parts.push('');
217
+ parts.push(highlight);
218
+ }
219
+ parts.push('');
220
+ parts.push(textMetaItems(view).join(' '));
221
+ blocks.push(parts.join('\n'));
222
+ });
223
+ return blocks.join('\n\n');
224
+ }
225
+ /* ------------------------------------------------------------------ *
226
+ * HTML 卡片:标题可点击、命中词 <mark> 高亮。
227
+ * ------------------------------------------------------------------ */
228
+ export function formatSearchHtml(rows, options = {}) {
229
+ const baseUrl = String(options.baseUrl ?? '').trim().replace(/\/+$/, '');
230
+ const items = rows.map((row) => {
231
+ const raw = row;
232
+ const view = toSearchRowView(raw);
233
+ return { raw, view: hasLink(view) && baseUrl ? withBaseUrl(view, baseUrl) : view };
234
+ });
235
+ const keyword = String(options.keyword ?? '').trim();
236
+ const heading = options.heading ?? (keyword ? `「${keyword}」档案检索结果` : '档案检索结果');
237
+ const cards = items
238
+ .map(({ raw, view }, index) => {
239
+ const linkHref = view.viewUrl || view.viewPath;
240
+ const titleEl = linkHref
241
+ ? `<a class="tlink" href="${escapeHtml(linkHref)}" target="_blank" title="打开档案视图">${escapeHtml(view.title || '(无题名)')}</a>`
242
+ : `<span class="tlink plain">${escapeHtml(view.title || '(无题名)')}</span>`;
243
+ const highlight = htmlHighlight(highlightSourceOf(raw, view));
244
+ return `<article class="card">
245
+ <div class="trow"><span class="idx">${index + 1}.</span><span class="lvl">${escapeHtml(view.level || '未分类')}</span>${titleEl}</div>
246
+ ${highlight ? `<p class="hl">${highlight}</p>` : ''}
247
+ <div class="meta"><span>全宗:${escapeHtml(view.fonds || '-')}</span><span>分类:${escapeHtml(view.category || '-')}</span>${view.dateLabel && view.dateValue ? `<span>${escapeHtml(describeDate(view))}</span>` : ''}</div>
248
+ </article>`;
249
+ })
250
+ .join('\n');
251
+ const pages = pageLabel(options);
252
+ return `<!DOCTYPE html>
253
+ <html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
254
+ <title>${escapeHtml(heading)}</title><style>
255
+ *{box-sizing:border-box;margin:0;padding:0}
256
+ body{font:14px/1.7 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif;background:#f0f2f5;color:#1f2d3d;padding:28px}
257
+ .wrap{max-width:980px;margin:0 auto}
258
+ h1{font-size:20px;margin-bottom:4px}
259
+ .sub{color:#64748b;font-size:13px;margin-bottom:18px}
260
+ .card{background:#fff;border:1px solid #e2e8f0;border-radius:10px;padding:14px 18px;margin-bottom:12px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
261
+ .trow{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
262
+ .idx{color:#94a3b8;font-weight:600}
263
+ .lvl{flex:none;background:#eef4ff;color:#2563eb;border:1px solid #bfdbfe;border-radius:4px;padding:0 8px;font-size:12px}
264
+ .tlink{font-size:15px;font-weight:600;color:#0f2c5c;text-decoration:none;word-break:break-all}
265
+ .tlink:hover{color:#1d6ae5;text-decoration:underline}
266
+ .tlink.plain{cursor:default}
267
+ .hl{margin:8px 0 6px;color:#475569;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;font-size:13px}
268
+ .hl mark{background:#ffe58f;color:#7c2d12;padding:0 2px;border-radius:3px;font-weight:600}
269
+ .meta{display:flex;gap:22px;color:#334155;font-size:13px;border-top:1px dashed #e2e8f0;padding-top:8px;flex-wrap:wrap}
270
+ .meta span:first-child{color:#0f766e}
271
+ </style></head><body><div class="wrap">
272
+ <h1>${escapeHtml(heading)}</h1>
273
+ ${pages ? `<div class="sub">${escapeHtml(pages)} · 点击题名打开档案视图</div>` : ''}
274
+ ${cards}
275
+ </div></body></html>`;
276
+ }