dsh-crwu-workbench 0.0.10

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 (479) hide show
  1. package/CHANGELOG.md +1516 -0
  2. package/LICENSE +26 -0
  3. package/README.en.md +204 -0
  4. package/README.md +521 -0
  5. package/SECURITY.md +39 -0
  6. package/bin/darwin-arm64/crwu +0 -0
  7. package/bin/darwin-arm64/dws +0 -0
  8. package/bin/darwin-arm64/ossutil +0 -0
  9. package/bin/manifest.json +82 -0
  10. package/bin/win32-x64/crwu.exe +0 -0
  11. package/bin/win32-x64/dws.exe +4 -0
  12. package/bin/win32-x64/ossutil.exe +0 -0
  13. package/common/skills/.synced.json +8 -0
  14. package/common/skills/crwu-dws/SKILL.md +208 -0
  15. package/common/skills/crwu-dws/references/00-/347/233/256/345/275/225/345/277/253/347/205/247schema.md +113 -0
  16. package/common/skills/crwu-dws/references/01-/345/256/241/346/240/270/344/270/213/350/275/275/344/270/216manifest/350/247/204/350/214/203.md +115 -0
  17. package/common/skills/crwu-dws/references/02-/347/274/223/345/255/230/344/270/216/345/205/234/345/272/225/346/237/245/346/211/276/350/247/204/350/214/203.md +119 -0
  18. package/common/skills/crwu-dws/scripts/test_dws_source_contract.py +493 -0
  19. package/common/skills/crwu-h3yun-login/SKILL.md +130 -0
  20. package/common/skills/crwu-h3yun-query/SKILL.md +192 -0
  21. package/config/crwu-workbench.yml +26 -0
  22. package/cordis.patch.yml +46 -0
  23. package/lib/client.js +10346 -0
  24. package/lib/index.js +11584 -0
  25. package/package.json +119 -0
  26. package/scripts/prepare.mjs +59 -0
  27. package/skills/README.md +26 -0
  28. package/skills/crwu/crwu-audit/SKILL.md +152 -0
  29. package/skills/crwu/crwu-audit/references/00-input-and-route-profile.md +232 -0
  30. package/skills/crwu/crwu-audit/references/01-report-id-resolution.md +13 -0
  31. package/skills/crwu/crwu-audit/references/02-scope-classification.md +25 -0
  32. package/skills/crwu/crwu-audit/references/03-asset-classification.md +27 -0
  33. package/skills/crwu/crwu-audit/references/04-business-classification.md +63 -0
  34. package/skills/crwu/crwu-audit/references/05-method-classification.md +66 -0
  35. package/skills/crwu/crwu-audit/references/06-overlay-classification.md +42 -0
  36. package/skills/crwu/crwu-audit/references/07-skill-registry.md +57 -0
  37. package/skills/crwu/crwu-audit/references/08-union-dispatch-rules.md +142 -0
  38. package/skills/crwu/crwu-audit/references/09-review-risk-classification.md +85 -0
  39. package/skills/crwu/crwu-audit/references/10-capability-gap-proposal.md +68 -0
  40. package/skills/crwu/crwu-audit/references/11-html-delivery-spec.md +881 -0
  41. package/skills/crwu/crwu-audit/references/12-leaf-common-contract.md +202 -0
  42. package/skills/crwu/crwu-audit/references/13-dingtalk-result-publish.md +88 -0
  43. package/skills/crwu/crwu-audit/references/14-orchestration-workflow.md +52 -0
  44. package/skills/crwu/crwu-audit/references/99-maintenance.md +62 -0
  45. package/skills/crwu/crwu-audit/scripts/README.md +157 -0
  46. package/skills/crwu/crwu-audit/scripts/audit_delivery.py +2736 -0
  47. package/skills/crwu/crwu-audit/scripts/audit_result.schema.json +1659 -0
  48. package/skills/crwu/crwu-audit/scripts/examples/audit-result.sample.json +739 -0
  49. package/skills/crwu/crwu-audit/scripts/fetch_review_records.py +85 -0
  50. package/skills/crwu/crwu-audit/scripts/media_extract.py +513 -0
  51. package/skills/crwu/crwu-audit/scripts/prepare_materials.py +1252 -0
  52. package/skills/crwu/crwu-audit/scripts/test_audit_delivery.py +1678 -0
  53. package/skills/crwu/crwu-audit/scripts/test_audit_multiaxis_router.py +1339 -0
  54. package/skills/crwu/crwu-audit/scripts/test_fetch_review_records.py +77 -0
  55. package/skills/crwu/crwu-audit/scripts/test_media_extract.py +332 -0
  56. package/skills/crwu/crwu-audit/scripts/test_prepare_materials.py +1081 -0
  57. package/skills/crwu/crwu-audit/template/audit-report.html +435 -0
  58. package/skills/crwu/crwu-audit-asset-asset-group-goodwill/SKILL.md +38 -0
  59. package/skills/crwu/crwu-audit-asset-asset-group-goodwill/references/00-applicability.md +28 -0
  60. package/skills/crwu/crwu-audit-asset-asset-group-goodwill/references/01-kb-assembly.md +31 -0
  61. package/skills/crwu/crwu-audit-asset-asset-group-goodwill/references/02-review-focus.md +21 -0
  62. package/skills/crwu/crwu-audit-asset-asset-group-goodwill/references/03-common-contract.md +202 -0
  63. package/skills/crwu/crwu-audit-asset-debt/SKILL.md +38 -0
  64. package/skills/crwu/crwu-audit-asset-debt/references/00-applicability.md +40 -0
  65. package/skills/crwu/crwu-audit-asset-debt/references/01-kb-assembly.md +41 -0
  66. package/skills/crwu/crwu-audit-asset-debt/references/02-review-focus.md +54 -0
  67. package/skills/crwu/crwu-audit-asset-debt/references/03-common-contract.md +202 -0
  68. package/skills/crwu/crwu-audit-asset-enterprise-value/SKILL.md +38 -0
  69. package/skills/crwu/crwu-audit-asset-enterprise-value/references/00-applicability.md +31 -0
  70. package/skills/crwu/crwu-audit-asset-enterprise-value/references/01-kb-assembly.md +35 -0
  71. package/skills/crwu/crwu-audit-asset-enterprise-value/references/02-review-focus.md +33 -0
  72. package/skills/crwu/crwu-audit-asset-enterprise-value/references/03-common-contract.md +202 -0
  73. package/skills/crwu/crwu-audit-asset-equipment/SKILL.md +38 -0
  74. package/skills/crwu/crwu-audit-asset-equipment/references/00-applicability.md +40 -0
  75. package/skills/crwu/crwu-audit-asset-equipment/references/01-kb-assembly.md +39 -0
  76. package/skills/crwu/crwu-audit-asset-equipment/references/02-review-focus.md +52 -0
  77. package/skills/crwu/crwu-audit-asset-equipment/references/03-common-contract.md +202 -0
  78. package/skills/crwu/crwu-audit-asset-intangible/SKILL.md +38 -0
  79. package/skills/crwu/crwu-audit-asset-intangible/references/00-applicability.md +36 -0
  80. package/skills/crwu/crwu-audit-asset-intangible/references/01-kb-assembly.md +40 -0
  81. package/skills/crwu/crwu-audit-asset-intangible/references/02-review-focus.md +68 -0
  82. package/skills/crwu/crwu-audit-asset-intangible/references/03-common-contract.md +202 -0
  83. package/skills/crwu/crwu-audit-asset-inventory/SKILL.md +38 -0
  84. package/skills/crwu/crwu-audit-asset-inventory/references/00-applicability.md +28 -0
  85. package/skills/crwu/crwu-audit-asset-inventory/references/01-kb-assembly.md +31 -0
  86. package/skills/crwu/crwu-audit-asset-inventory/references/02-review-focus.md +21 -0
  87. package/skills/crwu/crwu-audit-asset-inventory/references/03-common-contract.md +202 -0
  88. package/skills/crwu/crwu-audit-asset-mining-right/SKILL.md +38 -0
  89. package/skills/crwu/crwu-audit-asset-mining-right/references/00-applicability.md +31 -0
  90. package/skills/crwu/crwu-audit-asset-mining-right/references/01-kb-assembly.md +35 -0
  91. package/skills/crwu/crwu-audit-asset-mining-right/references/02-review-focus.md +33 -0
  92. package/skills/crwu/crwu-audit-asset-mining-right/references/03-common-contract.md +202 -0
  93. package/skills/crwu/crwu-audit-asset-other/SKILL.md +38 -0
  94. package/skills/crwu/crwu-audit-asset-other/references/00-applicability.md +28 -0
  95. package/skills/crwu/crwu-audit-asset-other/references/01-kb-assembly.md +31 -0
  96. package/skills/crwu/crwu-audit-asset-other/references/02-review-focus.md +21 -0
  97. package/skills/crwu/crwu-audit-asset-other/references/03-common-contract.md +202 -0
  98. package/skills/crwu/crwu-audit-asset-portfolio/SKILL.md +38 -0
  99. package/skills/crwu/crwu-audit-asset-portfolio/references/00-applicability.md +28 -0
  100. package/skills/crwu/crwu-audit-asset-portfolio/references/01-kb-assembly.md +31 -0
  101. package/skills/crwu/crwu-audit-asset-portfolio/references/02-review-focus.md +21 -0
  102. package/skills/crwu/crwu-audit-asset-portfolio/references/03-common-contract.md +202 -0
  103. package/skills/crwu/crwu-audit-asset-realestate/SKILL.md +38 -0
  104. package/skills/crwu/crwu-audit-asset-realestate/references/00-applicability.md +43 -0
  105. package/skills/crwu/crwu-audit-asset-realestate/references/01-kb-assembly.md +75 -0
  106. package/skills/crwu/crwu-audit-asset-realestate/references/02-review-focus.md +93 -0
  107. package/skills/crwu/crwu-audit-asset-realestate/references/03-common-contract.md +202 -0
  108. package/skills/crwu/crwu-audit-asset-scrap-materials/SKILL.md +38 -0
  109. package/skills/crwu/crwu-audit-asset-scrap-materials/references/00-applicability.md +28 -0
  110. package/skills/crwu/crwu-audit-asset-scrap-materials/references/01-kb-assembly.md +31 -0
  111. package/skills/crwu/crwu-audit-asset-scrap-materials/references/02-review-focus.md +21 -0
  112. package/skills/crwu/crwu-audit-asset-scrap-materials/references/03-common-contract.md +202 -0
  113. package/skills/crwu/crwu-audit-asset-transport-equipment/SKILL.md +38 -0
  114. package/skills/crwu/crwu-audit-asset-transport-equipment/references/00-applicability.md +31 -0
  115. package/skills/crwu/crwu-audit-asset-transport-equipment/references/01-kb-assembly.md +35 -0
  116. package/skills/crwu/crwu-audit-asset-transport-equipment/references/02-review-focus.md +33 -0
  117. package/skills/crwu/crwu-audit-asset-transport-equipment/references/03-common-contract.md +202 -0
  118. package/skills/crwu/crwu-audit-biz-asset-operation/SKILL.md +38 -0
  119. package/skills/crwu/crwu-audit-biz-asset-operation/references/00-applicability.md +30 -0
  120. package/skills/crwu/crwu-audit-biz-asset-operation/references/01-kb-assembly.md +32 -0
  121. package/skills/crwu/crwu-audit-biz-asset-operation/references/02-review-focus.md +42 -0
  122. package/skills/crwu/crwu-audit-biz-asset-operation/references/03-common-contract.md +202 -0
  123. package/skills/crwu/crwu-audit-biz-consulting-review/SKILL.md +38 -0
  124. package/skills/crwu/crwu-audit-biz-consulting-review/references/00-applicability.md +30 -0
  125. package/skills/crwu/crwu-audit-biz-consulting-review/references/01-kb-assembly.md +32 -0
  126. package/skills/crwu/crwu-audit-biz-consulting-review/references/02-review-focus.md +40 -0
  127. package/skills/crwu/crwu-audit-biz-consulting-review/references/03-common-contract.md +202 -0
  128. package/skills/crwu/crwu-audit-biz-financial-reporting/SKILL.md +38 -0
  129. package/skills/crwu/crwu-audit-biz-financial-reporting/references/00-applicability.md +41 -0
  130. package/skills/crwu/crwu-audit-biz-financial-reporting/references/01-kb-assembly.md +40 -0
  131. package/skills/crwu/crwu-audit-biz-financial-reporting/references/02-review-focus.md +64 -0
  132. package/skills/crwu/crwu-audit-biz-financial-reporting/references/03-common-contract.md +202 -0
  133. package/skills/crwu/crwu-audit-biz-financing-debt/SKILL.md +38 -0
  134. package/skills/crwu/crwu-audit-biz-financing-debt/references/00-applicability.md +31 -0
  135. package/skills/crwu/crwu-audit-biz-financing-debt/references/01-kb-assembly.md +34 -0
  136. package/skills/crwu/crwu-audit-biz-financing-debt/references/02-review-focus.md +47 -0
  137. package/skills/crwu/crwu-audit-biz-financing-debt/references/03-common-contract.md +202 -0
  138. package/skills/crwu/crwu-audit-biz-investment-capital/SKILL.md +38 -0
  139. package/skills/crwu/crwu-audit-biz-investment-capital/references/00-applicability.md +31 -0
  140. package/skills/crwu/crwu-audit-biz-investment-capital/references/01-kb-assembly.md +34 -0
  141. package/skills/crwu/crwu-audit-biz-investment-capital/references/02-review-focus.md +47 -0
  142. package/skills/crwu/crwu-audit-biz-investment-capital/references/03-common-contract.md +202 -0
  143. package/skills/crwu/crwu-audit-biz-judicial-liquidation-compensation/SKILL.md +38 -0
  144. package/skills/crwu/crwu-audit-biz-judicial-liquidation-compensation/references/00-applicability.md +30 -0
  145. package/skills/crwu/crwu-audit-biz-judicial-liquidation-compensation/references/01-kb-assembly.md +32 -0
  146. package/skills/crwu/crwu-audit-biz-judicial-liquidation-compensation/references/02-review-focus.md +40 -0
  147. package/skills/crwu/crwu-audit-biz-judicial-liquidation-compensation/references/03-common-contract.md +202 -0
  148. package/skills/crwu/crwu-audit-biz-tax-history/SKILL.md +38 -0
  149. package/skills/crwu/crwu-audit-biz-tax-history/references/00-applicability.md +29 -0
  150. package/skills/crwu/crwu-audit-biz-tax-history/references/01-kb-assembly.md +30 -0
  151. package/skills/crwu/crwu-audit-biz-tax-history/references/02-review-focus.md +33 -0
  152. package/skills/crwu/crwu-audit-biz-tax-history/references/03-common-contract.md +202 -0
  153. package/skills/crwu/crwu-audit-biz-transaction-disposal/SKILL.md +38 -0
  154. package/skills/crwu/crwu-audit-biz-transaction-disposal/references/00-applicability.md +31 -0
  155. package/skills/crwu/crwu-audit-biz-transaction-disposal/references/01-kb-assembly.md +34 -0
  156. package/skills/crwu/crwu-audit-biz-transaction-disposal/references/02-review-focus.md +47 -0
  157. package/skills/crwu/crwu-audit-biz-transaction-disposal/references/03-common-contract.md +202 -0
  158. package/skills/crwu/crwu-audit-datacheck/SKILL.md +59 -0
  159. package/skills/crwu/crwu-audit-datacheck/references/00-KB/350/243/205/351/205/215/350/241/250.md +25 -0
  160. package/skills/crwu/crwu-audit-datacheck/references/01-runtime-and-check-catalog.md +139 -0
  161. package/skills/crwu/crwu-audit-datacheck/scripts/recalc_check.py +521 -0
  162. package/skills/crwu/crwu-audit-datacheck/scripts/test_recalc_check.py +419 -0
  163. package/skills/crwu/crwu-audit-datacheck/scripts/test_skill_self_containment.py +44 -0
  164. package/skills/crwu/crwu-audit-external-data/SKILL.md +73 -0
  165. package/skills/crwu/crwu-audit-external-data/references/00-KB/350/243/205/351/205/215/350/241/250.md +38 -0
  166. package/skills/crwu/crwu-audit-external-data/references/01-connector-access.md +116 -0
  167. package/skills/crwu/crwu-audit-external-data/references/02-verification-workflow.md +78 -0
  168. package/skills/crwu/crwu-audit-external-data/scripts/connector_probe.py +374 -0
  169. package/skills/crwu/crwu-audit-external-data/scripts/test_connector_probe.py +264 -0
  170. package/skills/crwu/crwu-audit-output-filter/SKILL.md +34 -0
  171. package/skills/crwu/crwu-audit-output-filter/references/00-applicability.md +7 -0
  172. package/skills/crwu/crwu-audit-output-filter/references/01-kb-assembly.md +11 -0
  173. package/skills/crwu/crwu-audit-output-filter/references/02-review-focus.md +25 -0
  174. package/skills/crwu/crwu-audit-public-general-standards/SKILL.md +46 -0
  175. package/skills/crwu/crwu-audit-public-general-standards/references/00-applicability.md +41 -0
  176. package/skills/crwu/crwu-audit-public-general-standards/references/01-kb-assembly.md +66 -0
  177. package/skills/crwu/crwu-audit-public-general-standards/references/02-review-focus.md +111 -0
  178. package/skills/crwu/crwu-audit-public-general-standards/references/03-execution-contract.md +89 -0
  179. package/skills/crwu/crwu-dev-audit-optimize/SKILL.md +98 -0
  180. package/skills/crwu/crwu-dev-audit-optimize/references/00-/344/274/230/345/214/226/350/247/204/350/214/203/344/270/216/346/226/207/344/273/266/350/220/275/347/202/271.md +132 -0
  181. package/skills/crwu/crwu-dev-audit-optimize/references/01-/345/217/215/351/246/210/345/256/232/344/275/215/344/270/216/347/224/273/345/203/217/346/265/201/347/250/213.md +108 -0
  182. package/skills/crwu/crwu-dev-audit-optimize/references/02-/346/226/271/346/241/210/346/250/241/346/235/277/344/270/216/347/241/256/350/256/244/351/227/250/347/246/201.md +92 -0
  183. package/skills/crwu/crwu-dev-audit-optimize/references/03-AI/344/272/272/345/267/245/345/267/256/350/267/235/345/210/206/346/236/220/346/265/201/347/250/213.md +95 -0
  184. package/skills/crwu/crwu-dev-audit-optimize/scripts/examples/gap-analysis.sample.json +116 -0
  185. package/skills/crwu/crwu-dev-audit-optimize/scripts/gap_analysis.schema.json +33 -0
  186. package/skills/crwu/crwu-dev-audit-optimize/scripts/gap_analysis_delivery.py +475 -0
  187. package/skills/crwu/crwu-dev-audit-optimize/scripts/test_gap_analysis_delivery.py +855 -0
  188. package/skills/crwu/crwu-dev-audit-optimize/template/NOTICE.echarts.txt +7 -0
  189. package/skills/crwu/crwu-dev-audit-optimize/template/echarts.min.js +45 -0
  190. package/skills/crwu/crwu-dev-audit-optimize/template/gap-analysis-report.html +71 -0
  191. package/skills/crwu/crwu-dev-audit-skill-maintainer/SKILL.md +74 -0
  192. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/00-responsibility-and-modes.md +62 -0
  193. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/01-kb-source-discovery.md +89 -0
  194. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/02-child-skill-contract.md +99 -0
  195. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/03-review-item-execution-contract.md +51 -0
  196. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/04-registry-and-mapping-update.md +73 -0
  197. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/05-validation-and-delivery.md +127 -0
  198. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/06-live-routing-reconciliation.md +136 -0
  199. package/skills/crwu/crwu-dev-audit-skill-maintainer/references/07-kb-skill-map.md +411 -0
  200. package/skills/crwu/crwu-dev-audit-skill-maintainer/scripts/README.md +200 -0
  201. package/skills/crwu/crwu-dev-audit-skill-maintainer/scripts/check_audit_skill_mappings.py +2477 -0
  202. package/skills/crwu/crwu-dev-audit-skill-maintainer/scripts/kb_tool.py +577 -0
  203. package/skills/crwu/crwu-dev-audit-skill-maintainer/scripts/test_audit_skill_maintainer.py +5097 -0
  204. package/skills/dws/LICENSE +178 -0
  205. package/skills/dws/NOTICE +17 -0
  206. package/skills/dws/dingtalk-aisearch/SKILL.md +83 -0
  207. package/skills/dws/dingtalk-aisearch/references/aisearch.md +249 -0
  208. package/skills/dws/dingtalk-aisearch/references/intent-guide.md +15 -0
  209. package/skills/dws/dingtalk-aisearch/references/lite-recipes.md +31 -0
  210. package/skills/dws/dingtalk-aitable/SKILL.md +139 -0
  211. package/skills/dws/dingtalk-aitable/references/06-data-analytics.md +11 -0
  212. package/skills/dws/dingtalk-aitable/references/aitable/aitable-advperm.md +251 -0
  213. package/skills/dws/dingtalk-aitable/references/aitable/aitable-attachment.md +49 -0
  214. package/skills/dws/dingtalk-aitable/references/aitable/aitable-best-practices.md +48 -0
  215. package/skills/dws/dingtalk-aitable/references/aitable/aitable-cell-value.md +346 -0
  216. package/skills/dws/dingtalk-aitable/references/aitable/aitable-dashboard-chart.md +46 -0
  217. package/skills/dws/dingtalk-aitable/references/aitable/aitable-data-analysis-sop.md +131 -0
  218. package/skills/dws/dingtalk-aitable/references/aitable/aitable-datasource.md +272 -0
  219. package/skills/dws/dingtalk-aitable/references/aitable/aitable-error-recovery.md +128 -0
  220. package/skills/dws/dingtalk-aitable/references/aitable/aitable-export-import.md +113 -0
  221. package/skills/dws/dingtalk-aitable/references/aitable/aitable-field-properties.md +238 -0
  222. package/skills/dws/dingtalk-aitable/references/aitable/aitable-field.md +186 -0
  223. package/skills/dws/dingtalk-aitable/references/aitable/aitable-filter-sort.md +135 -0
  224. package/skills/dws/dingtalk-aitable/references/aitable/aitable-form.md +130 -0
  225. package/skills/dws/dingtalk-aitable/references/aitable/aitable-formula-guide.md +224 -0
  226. package/skills/dws/dingtalk-aitable/references/aitable/aitable-primary-doc.md +58 -0
  227. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-create.md +52 -0
  228. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-delete.md +20 -0
  229. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-history.md +97 -0
  230. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-name-key.md +47 -0
  231. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-query.md +120 -0
  232. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-share.md +54 -0
  233. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-stats.md +59 -0
  234. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-update.md +66 -0
  235. package/skills/dws/dingtalk-aitable/references/aitable/aitable-record-upsert.md +89 -0
  236. package/skills/dws/dingtalk-aitable/references/aitable/aitable-view-config.md +140 -0
  237. package/skills/dws/dingtalk-aitable/references/aitable/aitable-view-extras.md +195 -0
  238. package/skills/dws/dingtalk-aitable/references/aitable/aitable-view-types.md +95 -0
  239. package/skills/dws/dingtalk-aitable/references/aitable/aitable-workflow.md +384 -0
  240. package/skills/dws/dingtalk-aitable/references/aitable-record-ops.md +80 -0
  241. package/skills/dws/dingtalk-aitable/references/aitable-section.md +30 -0
  242. package/skills/dws/dingtalk-aitable/references/aitable.md +59 -0
  243. package/skills/dws/dingtalk-aitable/references/field-rules.md +17 -0
  244. package/skills/dws/dingtalk-aitable/references/intent-guide.md +17 -0
  245. package/skills/dws/dingtalk-aitable/references/recipes/conventions.md +44 -0
  246. package/skills/dws/dingtalk-aitable/references/url-patterns.md +144 -0
  247. package/skills/dws/dingtalk-aitable/scripts/aitable_export_via_task.py +210 -0
  248. package/skills/dws/dingtalk-aitable/scripts/aitable_import_via_task.py +173 -0
  249. package/skills/dws/dingtalk-aitable/scripts/bulk_add_fields.py +273 -0
  250. package/skills/dws/dingtalk-aitable/scripts/import_records.py +333 -0
  251. package/skills/dws/dingtalk-aitable/scripts/upload_attachment.py +190 -0
  252. package/skills/dws/dingtalk-calendar/SKILL.md +137 -0
  253. package/skills/dws/dingtalk-calendar/references/03-meeting.md +56 -0
  254. package/skills/dws/dingtalk-calendar/references/calendar.md +760 -0
  255. package/skills/dws/dingtalk-calendar/references/intent-guide.md +9 -0
  256. package/skills/dws/dingtalk-calendar/references/lite-recipes.md +25 -0
  257. package/skills/dws/dingtalk-calendar/scripts/calendar_free_slot_finder.py +199 -0
  258. package/skills/dws/dingtalk-calendar/scripts/calendar_schedule_meeting.py +233 -0
  259. package/skills/dws/dingtalk-calendar/scripts/calendar_today_agenda.py +139 -0
  260. package/skills/dws/dingtalk-chat/SKILL.md +127 -0
  261. package/skills/dws/dingtalk-chat/references/01-messaging.md +103 -0
  262. package/skills/dws/dingtalk-chat/references/card/callback.md +10 -0
  263. package/skills/dws/dingtalk-chat/references/card/create.md +27 -0
  264. package/skills/dws/dingtalk-chat/references/card/schema.md +14 -0
  265. package/skills/dws/dingtalk-chat/references/card/update.md +13 -0
  266. package/skills/dws/dingtalk-chat/references/chat/chat-bot.md +187 -0
  267. package/skills/dws/dingtalk-chat/references/chat/chat-conversation.md +165 -0
  268. package/skills/dws/dingtalk-chat/references/chat/group-admin.md +144 -0
  269. package/skills/dws/dingtalk-chat/references/chat/group-discovery.md +121 -0
  270. package/skills/dws/dingtalk-chat/references/chat/message-actions.md +132 -0
  271. package/skills/dws/dingtalk-chat/references/chat/message-media.md +76 -0
  272. package/skills/dws/dingtalk-chat/references/chat/message-query.md +135 -0
  273. package/skills/dws/dingtalk-chat/references/chat/thread.md +46 -0
  274. package/skills/dws/dingtalk-chat/references/chat-emoji-list.md +216 -0
  275. package/skills/dws/dingtalk-chat/references/chat.md +140 -0
  276. package/skills/dws/dingtalk-chat/references/contracts.md +70 -0
  277. package/skills/dws/dingtalk-chat/references/intent-guide.md +56 -0
  278. package/skills/dws/dingtalk-contact/SKILL.md +122 -0
  279. package/skills/dws/dingtalk-contact/references/08-directory.md +51 -0
  280. package/skills/dws/dingtalk-contact/references/contact.md +595 -0
  281. package/skills/dws/dingtalk-contact/references/intent-guide.md +14 -0
  282. package/skills/dws/dingtalk-contact/references/lite-recipes.md +30 -0
  283. package/skills/dws/dingtalk-contact/references/recipes/conventions.md +44 -0
  284. package/skills/dws/dingtalk-contact/scripts/contact_dept_members.py +130 -0
  285. package/skills/dws/dingtalk-doc/SKILL.md +112 -0
  286. package/skills/dws/dingtalk-doc/references/04-document.md +57 -0
  287. package/skills/dws/dingtalk-doc/references/contracts.md +40 -0
  288. package/skills/dws/dingtalk-doc/references/doc/doc-block.md +39 -0
  289. package/skills/dws/dingtalk-doc/references/doc/doc-comment.md +29 -0
  290. package/skills/dws/dingtalk-doc/references/doc/doc-create.md +54 -0
  291. package/skills/dws/dingtalk-doc/references/doc/doc-export.md +30 -0
  292. package/skills/dws/dingtalk-doc/references/doc/doc-import.md +28 -0
  293. package/skills/dws/dingtalk-doc/references/doc/doc-info.md +12 -0
  294. package/skills/dws/dingtalk-doc/references/doc/doc-media.md +46 -0
  295. package/skills/dws/dingtalk-doc/references/doc/doc-read.md +56 -0
  296. package/skills/dws/dingtalk-doc/references/doc/doc-update.md +87 -0
  297. package/skills/dws/dingtalk-doc/references/doc/format/doc-jsonml-cookbook.md +469 -0
  298. package/skills/dws/dingtalk-doc/references/doc/format/doc-jsonml-schema.json +526 -0
  299. package/skills/dws/dingtalk-doc/references/doc/format/doc-jsonml-schema.md +711 -0
  300. package/skills/dws/dingtalk-doc/references/doc/style/doc-create-workflow.md +414 -0
  301. package/skills/dws/dingtalk-doc/references/doc/style/doc-style-guideline.md +404 -0
  302. package/skills/dws/dingtalk-doc/references/doc/style/doc-update-workflow.md +316 -0
  303. package/skills/dws/dingtalk-doc/references/doc.md +82 -0
  304. package/skills/dws/dingtalk-doc/references/intent-guide.md +20 -0
  305. package/skills/dws/dingtalk-drive/SKILL.md +128 -0
  306. package/skills/dws/dingtalk-drive/references/drive.md +126 -0
  307. package/skills/dws/dingtalk-drive/references/folder-sync.md +40 -0
  308. package/skills/dws/dingtalk-drive/references/intent-guide.md +22 -0
  309. package/skills/dws/dingtalk-drive/references/lite-recipes.md +39 -0
  310. package/skills/dws/dingtalk-event/SKILL.md +106 -0
  311. package/skills/dws/dingtalk-event/references/event-im-keys.md +54 -0
  312. package/skills/dws/dingtalk-event/references/event-im-lifecycle.md +35 -0
  313. package/skills/dws/dingtalk-event/references/event-im-operations.md +52 -0
  314. package/skills/dws/dingtalk-event/references/event-im-output.md +39 -0
  315. package/skills/dws/dingtalk-event/references/event-im.md +58 -0
  316. package/skills/dws/dingtalk-event/references/event-oa.md +155 -0
  317. package/skills/dws/dingtalk-event/references/event-todo.md +50 -0
  318. package/skills/dws/dingtalk-event/references/event-voip.md +69 -0
  319. package/skills/dws/dingtalk-mail/SKILL.md +113 -0
  320. package/skills/dws/dingtalk-mail/references/09-mail.md +45 -0
  321. package/skills/dws/dingtalk-mail/references/intent-guide.md +8 -0
  322. package/skills/dws/dingtalk-mail/references/lite-recipes.md +45 -0
  323. package/skills/dws/dingtalk-mail/references/mail.md +1889 -0
  324. package/skills/dws/dingtalk-mail/references/recipes/conventions.md +44 -0
  325. package/skills/dws/dingtalk-mail/scripts/mail_send_with_cc.py +122 -0
  326. package/skills/dws/dingtalk-mail/scripts/mail_unread_summary.py +114 -0
  327. package/skills/dws/dingtalk-minutes/SKILL.md +116 -0
  328. package/skills/dws/dingtalk-minutes/references/07-minutes.md +149 -0
  329. package/skills/dws/dingtalk-minutes/references/10-minutes-speaker-match.md +32 -0
  330. package/skills/dws/dingtalk-minutes/references/11-minutes-speaker-correct.md +40 -0
  331. package/skills/dws/dingtalk-minutes/references/intent-guide.md +79 -0
  332. package/skills/dws/dingtalk-minutes/references/lite-recipes.md +25 -0
  333. package/skills/dws/dingtalk-minutes/references/minutes.md +210 -0
  334. package/skills/dws/dingtalk-minutes/references/recipes/conventions.md +33 -0
  335. package/skills/dws/dingtalk-minutes/scripts/minutes_recent_summary.py +196 -0
  336. package/skills/dws/dingtalk-minutes/scripts/test_minutes_scripts.py +222 -0
  337. package/skills/dws/dingtalk-misc/SKILL.md +55 -0
  338. package/skills/dws/dingtalk-misc/references/05-reporting.md +29 -0
  339. package/skills/dws/dingtalk-misc/references/agoal.md +302 -0
  340. package/skills/dws/dingtalk-misc/references/attendance-intent-guide.md +7 -0
  341. package/skills/dws/dingtalk-misc/references/attendance-report.md +109 -0
  342. package/skills/dws/dingtalk-misc/references/attendance-schedule.md +115 -0
  343. package/skills/dws/dingtalk-misc/references/attendance-vacation.md +129 -0
  344. package/skills/dws/dingtalk-misc/references/attendance.md +149 -0
  345. package/skills/dws/dingtalk-misc/references/dev/app.md +42 -0
  346. package/skills/dws/dingtalk-misc/references/dev/connect.md +108 -0
  347. package/skills/dws/dingtalk-misc/references/dev/credentials.md +26 -0
  348. package/skills/dws/dingtalk-misc/references/dev/dev-index.md +13 -0
  349. package/skills/dws/dingtalk-misc/references/dev/event.md +47 -0
  350. package/skills/dws/dingtalk-misc/references/dev/mcp.md +106 -0
  351. package/skills/dws/dingtalk-misc/references/dev/member.md +19 -0
  352. package/skills/dws/dingtalk-misc/references/dev/permission.md +56 -0
  353. package/skills/dws/dingtalk-misc/references/dev/recipes.md +58 -0
  354. package/skills/dws/dingtalk-misc/references/dev/robot.md +74 -0
  355. package/skills/dws/dingtalk-misc/references/dev/security.md +21 -0
  356. package/skills/dws/dingtalk-misc/references/dev/version.md +99 -0
  357. package/skills/dws/dingtalk-misc/references/dev/webapp.md +22 -0
  358. package/skills/dws/dingtalk-misc/references/devapp.md +148 -0
  359. package/skills/dws/dingtalk-misc/references/devdoc-intent-guide.md +7 -0
  360. package/skills/dws/dingtalk-misc/references/devdoc.md +42 -0
  361. package/skills/dws/dingtalk-misc/references/ding.md +184 -0
  362. package/skills/dws/dingtalk-misc/references/hrbrain.md +67 -0
  363. package/skills/dws/dingtalk-misc/references/live.md +23 -0
  364. package/skills/dws/dingtalk-misc/references/markdown.md +167 -0
  365. package/skills/dws/dingtalk-misc/references/oa/oa-form-components.md +345 -0
  366. package/skills/dws/dingtalk-misc/references/oa/oa-process-nodes.md +374 -0
  367. package/skills/dws/dingtalk-misc/references/oa.md +1014 -0
  368. package/skills/dws/dingtalk-misc/references/openapi-explorer.md +47 -0
  369. package/skills/dws/dingtalk-misc/references/pat.md +52 -0
  370. package/skills/dws/dingtalk-misc/references/profile.md +41 -0
  371. package/skills/dws/dingtalk-misc/references/recruit.md +89 -0
  372. package/skills/dws/dingtalk-misc/references/report-conventions.md +44 -0
  373. package/skills/dws/dingtalk-misc/references/report-intent-guide.md +11 -0
  374. package/skills/dws/dingtalk-misc/references/report-lite-recipes.md +25 -0
  375. package/skills/dws/dingtalk-misc/references/report.md +109 -0
  376. package/skills/dws/dingtalk-misc/references/sheet/sheet-batch-operations.md +104 -0
  377. package/skills/dws/dingtalk-misc/references/sheet/sheet-chart.md +69 -0
  378. package/skills/dws/dingtalk-misc/references/sheet/sheet-comment.md +166 -0
  379. package/skills/dws/dingtalk-misc/references/sheet/sheet-conditional-format.md +59 -0
  380. package/skills/dws/dingtalk-misc/references/sheet/sheet-dimension-operations.md +60 -0
  381. package/skills/dws/dingtalk-misc/references/sheet/sheet-dropdown.md +103 -0
  382. package/skills/dws/dingtalk-misc/references/sheet/sheet-export.md +135 -0
  383. package/skills/dws/dingtalk-misc/references/sheet/sheet-filter-view.md +51 -0
  384. package/skills/dws/dingtalk-misc/references/sheet/sheet-filter.md +181 -0
  385. package/skills/dws/dingtalk-misc/references/sheet/sheet-formula.md +192 -0
  386. package/skills/dws/dingtalk-misc/references/sheet/sheet-import.md +76 -0
  387. package/skills/dws/dingtalk-misc/references/sheet/sheet-media-image.md +263 -0
  388. package/skills/dws/dingtalk-misc/references/sheet/sheet-pivot-table.md +292 -0
  389. package/skills/dws/dingtalk-misc/references/sheet/sheet-range-operations.md +161 -0
  390. package/skills/dws/dingtalk-misc/references/sheet/sheet-read-data.md +60 -0
  391. package/skills/dws/dingtalk-misc/references/sheet/sheet-revision-changeset.md +46 -0
  392. package/skills/dws/dingtalk-misc/references/sheet/sheet-search-replace.md +120 -0
  393. package/skills/dws/dingtalk-misc/references/sheet/sheet-style-format.md +71 -0
  394. package/skills/dws/dingtalk-misc/references/sheet/sheet-version.md +84 -0
  395. package/skills/dws/dingtalk-misc/references/sheet/sheet-workbook.md +59 -0
  396. package/skills/dws/dingtalk-misc/references/sheet/sheet-write-data.md +93 -0
  397. package/skills/dws/dingtalk-misc/references/sheet-conventions.md +44 -0
  398. package/skills/dws/dingtalk-misc/references/sheet-intent-guide.md +12 -0
  399. package/skills/dws/dingtalk-misc/references/sheet.md +166 -0
  400. package/skills/dws/dingtalk-misc/references/skill.md +98 -0
  401. package/skills/dws/dingtalk-misc/references/unsupported-scripts.md +49 -0
  402. package/skills/dws/dingtalk-misc/references/url-patterns.md +144 -0
  403. package/skills/dws/dingtalk-misc/references/whiteboard/compose.md +200 -0
  404. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/01-overview.md +57 -0
  405. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/02-query.md +288 -0
  406. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/03-update.md +249 -0
  407. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/04-text-style.md +395 -0
  408. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/05-shape-frame-group-connector.md +186 -0
  409. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/06-vector-icon-path.md +313 -0
  410. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/07-examples-errors-write-support.md +270 -0
  411. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1/08-catalogs.md +61 -0
  412. package/skills/dws/dingtalk-misc/references/whiteboard/open-nodes-v1.md +54 -0
  413. package/skills/dws/dingtalk-misc/references/whiteboard/replace.md +41 -0
  414. package/skills/dws/dingtalk-misc/references/whiteboard/vector.md +51 -0
  415. package/skills/dws/dingtalk-misc/references/whiteboard.md +115 -0
  416. package/skills/dws/dingtalk-misc/scripts/aiapp_create_and_poll.py +138 -0
  417. package/skills/dws/dingtalk-misc/scripts/attendance_my_record.py +91 -0
  418. package/skills/dws/dingtalk-misc/scripts/attendance_report_checkin.py +440 -0
  419. package/skills/dws/dingtalk-misc/scripts/attendance_report_common.py +1698 -0
  420. package/skills/dws/dingtalk-misc/scripts/attendance_report_daily.py +558 -0
  421. package/skills/dws/dingtalk-misc/scripts/attendance_report_detail.py +809 -0
  422. package/skills/dws/dingtalk-misc/scripts/attendance_report_monthly.py +758 -0
  423. package/skills/dws/dingtalk-misc/scripts/attendance_report_record.py +947 -0
  424. package/skills/dws/dingtalk-misc/scripts/attendance_schedule_export.py +344 -0
  425. package/skills/dws/dingtalk-misc/scripts/attendance_schedule_import.py +498 -0
  426. package/skills/dws/dingtalk-misc/scripts/attendance_team_shift.py +89 -0
  427. package/skills/dws/dingtalk-misc/scripts/attendance_vacation_balance.py +617 -0
  428. package/skills/dws/dingtalk-misc/scripts/finance_daily_cashflow.py +70 -0
  429. package/skills/dws/dingtalk-misc/scripts/finance_expense_flow.py +138 -0
  430. package/skills/dws/dingtalk-misc/scripts/oa_batch_approve.py +158 -0
  431. package/skills/dws/dingtalk-misc/scripts/oa_pending_review.py +132 -0
  432. package/skills/dws/dingtalk-misc/scripts/report_received_today.py +315 -0
  433. package/skills/dws/dingtalk-misc/scripts/yida_custom_page_update.py +401 -0
  434. package/skills/dws/dingtalk-misc/scripts/yida_form_builder.py +526 -0
  435. package/skills/dws/dingtalk-misc/scripts/yida_form_fields.py +315 -0
  436. package/skills/dws/dingtalk-misc/scripts/yida_form_inspector.py +311 -0
  437. package/skills/dws/dingtalk-misc/scripts/yida_form_update.py +185 -0
  438. package/skills/dws/dingtalk-misc/scripts/yida_jsx_pipeline.py +1588 -0
  439. package/skills/dws/dingtalk-misc/scripts/yida_page_compiler.py +1441 -0
  440. package/skills/dws/dingtalk-misc/scripts/yida_page_generate.py +514 -0
  441. package/skills/dws/dingtalk-misc/scripts/yida_page_schema.py +519 -0
  442. package/skills/dws/dingtalk-misc/scripts/yida_page_self_check.py +398 -0
  443. package/skills/dws/dingtalk-misc/scripts/yida_process_flow.py +1478 -0
  444. package/skills/dws/dingtalk-misc/scripts/yida_process_update.py +589 -0
  445. package/skills/dws/dingtalk-misc/scripts/yida_report_builder.py +494 -0
  446. package/skills/dws/dingtalk-misc/scripts/yida_report_charts.py +1434 -0
  447. package/skills/dws/dingtalk-misc/scripts/yida_report_update.py +244 -0
  448. package/skills/dws/dingtalk-misc/scripts/yida_schema_common.py +340 -0
  449. package/skills/dws/dingtalk-shared/SKILL.md +83 -0
  450. package/skills/dws/dingtalk-shared/references/capability-limits.md +31 -0
  451. package/skills/dws/dingtalk-shared/references/channel-login.md +26 -0
  452. package/skills/dws/dingtalk-shared/references/confirmation.md +59 -0
  453. package/skills/dws/dingtalk-shared/references/error-codes.md +155 -0
  454. package/skills/dws/dingtalk-shared/references/global-reference.md +122 -0
  455. package/skills/dws/dingtalk-shared/references/intent-guide.md +537 -0
  456. package/skills/dws/dingtalk-shared/references/recipes/conventions.md +45 -0
  457. package/skills/dws/dingtalk-shared/references/recipes/lite-catalog.md +275 -0
  458. package/skills/dws/dingtalk-shared/references/recipes/meta.md +67 -0
  459. package/skills/dws/dingtalk-shared/references/routing.md +52 -0
  460. package/skills/dws/dingtalk-shared/references/runtime-contract.md +11 -0
  461. package/skills/dws/dingtalk-shared/references/schema-usage.md +88 -0
  462. package/skills/dws/dingtalk-shared/references/url-patterns.md +144 -0
  463. package/skills/dws/dingtalk-shared/references/workflow-routing.md +45 -0
  464. package/skills/dws/dingtalk-todo/SKILL.md +119 -0
  465. package/skills/dws/dingtalk-todo/references/02-task.md +70 -0
  466. package/skills/dws/dingtalk-todo/references/intent-guide.md +15 -0
  467. package/skills/dws/dingtalk-todo/references/lite-recipes.md +68 -0
  468. package/skills/dws/dingtalk-todo/references/recipes/conventions.md +44 -0
  469. package/skills/dws/dingtalk-todo/references/todo.md +517 -0
  470. package/skills/dws/dingtalk-todo/scripts/todo_batch_create.py +354 -0
  471. package/skills/dws/dingtalk-todo/scripts/todo_daily_summary.py +153 -0
  472. package/skills/dws/dingtalk-todo/scripts/todo_overdue_check.py +101 -0
  473. package/skills/dws/dingtalk-wiki/SKILL.md +117 -0
  474. package/skills/dws/dingtalk-wiki/references/intent-guide.md +14 -0
  475. package/skills/dws/dingtalk-wiki/references/lite-recipes.md +44 -0
  476. package/skills/dws/dingtalk-wiki/references/wiki-members.md +32 -0
  477. package/skills/dws/dingtalk-wiki/references/wiki-node-ops.md +46 -0
  478. package/skills/dws/dingtalk-wiki/references/wiki.md +65 -0
  479. package/skills/dws/provenance.json +27 -0
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """阶段二:按阶段一排除清单定向取回复核记录(只读守卫 + 留痕)。
4
+
5
+ 纪律:
6
+ - **只接受显式 fileId 清单**(= 阶段一 allowlist 排除时落盘的清单),不现编、不猜测;
7
+ - 落 `<案例>/复核-人工/`,**禁止写入 `材料-源/`**;
8
+ - 逐件校验字节数,不符即记失败、不落盘;
9
+ - 写 `.fetch-manifest.json` 留痕(来源清单、逐件大小、失败原因)。
10
+
11
+ 用法:
12
+ python3 scripts/fetch_review_records.py --case <案例目录> \\
13
+ --manifest <排除清单.json> --source <阶段一下载全集目录>
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import sys
21
+
22
+
23
+ def main() -> int:
24
+ ap = argparse.ArgumentParser(description="阶段二按清单取回复核记录(只读守卫 + 留痕)")
25
+ ap.add_argument("--case", required=True)
26
+ ap.add_argument("--manifest", required=True, help="阶段一排除清单 JSON:[{fileId,name,sizeBytes}, …]")
27
+ ap.add_argument("--source", required=True, help="阶段一下载全集目录(复核件被排除但文件仍在其内)")
28
+ ap.add_argument("--out", default=None, help="落盘目录(默认 <案例>/复核-人工)")
29
+ args = ap.parse_args()
30
+
31
+ case = os.path.abspath(args.case)
32
+ source = os.path.abspath(args.source)
33
+ out = os.path.abspath(args.out or os.path.join(case, "复核-人工"))
34
+ src_materials = os.path.join(case, "材料-源")
35
+
36
+ if out == src_materials or out.startswith(src_materials + os.sep):
37
+ print(f"error: 复核件落盘目录不得在材料-源内:{out}")
38
+ return 2
39
+ if not os.path.isdir(source):
40
+ print(f"error: 阶段一源目录不存在:{source}")
41
+ return 2
42
+
43
+ manifest = json.loads(open(args.manifest, encoding="utf-8").read())
44
+ if not isinstance(manifest, list):
45
+ print("error: 清单必须是数组 [{fileId,name,sizeBytes}, …]")
46
+ return 2
47
+
48
+ os.makedirs(out, exist_ok=True)
49
+ results = []
50
+ for entry in manifest:
51
+ file_id = entry.get("fileId")
52
+ name = entry.get("name")
53
+ size = entry.get("sizeBytes")
54
+ if not file_id or not name:
55
+ results.append({"fileId": file_id, "name": name, "ok": False,
56
+ "reason": "清单项缺 fileId/name"})
57
+ continue
58
+ candidate = os.path.join(source, name)
59
+ rec = {"fileId": file_id, "name": name, "ok": False}
60
+ if not os.path.isfile(candidate):
61
+ rec["reason"] = f"阶段一源目录内未找到:{name}"
62
+ else:
63
+ actual = os.path.getsize(candidate)
64
+ if size is not None and actual != int(size):
65
+ rec["reason"] = f"字节数不符(清单 {size} / 实际 {actual}),拒绝落盘"
66
+ else:
67
+ dst = os.path.join(out, name)
68
+ with open(candidate, "rb") as fin, open(dst, "wb") as fout:
69
+ fout.write(fin.read())
70
+ rec["ok"] = True
71
+ rec["localPath"] = dst
72
+ rec["sizeBytes"] = os.path.getsize(dst)
73
+ results.append(rec)
74
+ print(f"[{'OK ' if rec.get('ok') else 'NG '}] {name} {rec.get('reason', '')}", flush=True)
75
+
76
+ with open(os.path.join(out, ".fetch-manifest.json"), "w", encoding="utf-8") as fh:
77
+ json.dump({"manifest": args.manifest, "source": source, "out": out, "results": results},
78
+ fh, ensure_ascii=False, indent=1)
79
+ ok = sum(1 for r in results if r.get("ok"))
80
+ print(f"\n取回 {ok}/{len(results)};落盘 {out};失败项见 .fetch-manifest.json")
81
+ return 0
82
+
83
+
84
+ if __name__ == "__main__":
85
+ raise SystemExit(main())
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """媒体证据抽取通道(`crwu-audit` 编排层专用,随技能安装)。
4
+
5
+ 归属:本技能 `scripts/media_extract.py`,由 `scripts/prepare_materials.py` 调用;
6
+ 口径见 `references/00-input-and-route-profile.md` §Excel 非单元格证据。
7
+
8
+ **为什么需要它(真实失效 2026-302150-LX9757-BG8677)**:重建"工作版"只复制单元格值与
9
+ `number_format`,**必然**丢弃 `xl/media`、`word/media` 等图片部件;而叶子只接收工作版,
10
+ 于是"证据在图片里"的检查项(询价截图、可比实例位置图、现场照片、复核意见截图)被
11
+ **结构性**判成"为空/缺失"(假阳性)。此前只登记了一个媒体计数,规则却要求叶子"解析 raw
12
+ 原件包"——raw 只由编排层持有,通道始终是空的。本模块把媒体**导出**到 `<案例>/媒体证据/`
13
+ 并产出放行清单 `媒体索引.json`,交由宿主多模态读图(DSH `read_image`)。
14
+
15
+ 纪律:
16
+
17
+ 1. **未核 ≠ 缺失**:解析不到锚点、缺依赖、无渲染能力时一律记「未核验」原因,禁止表述为
18
+ "为空/缺失/未列示",也不得据此产生差异。
19
+ 2. **H0(人工隐藏区禁读禁报)同样适用于媒体锚点**:锚点落在隐藏 sheet / 隐藏行 / 隐藏列上的
20
+ xlsx 媒体**不导出、不定位**,只记数量;锚点不可判定(`absoluteAnchor`、未被引用部件、
21
+ 页眉页脚、图表)**保守不导出**并记未核原因;隐藏结构不可得时**整件不导出**(fail-closed)。
22
+ 3. **源材料目录零写入**:导出物全部落 `<案例>/媒体证据/`。
23
+ 4. 上限硬护栏:单件媒体数与总量超限即停并如实记账,不静默截断。
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import os
29
+ import re
30
+ import shutil
31
+ import subprocess
32
+ import tempfile
33
+ import zipfile
34
+
35
+ # 可交宿主视觉读取的位图/矢量图后缀(本机无 OCR/渲染,判读由宿主多模态承担)
36
+ IMAGE_EXTS = (".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tif", ".tiff", ".emf", ".wmf")
37
+
38
+ MAX_MEDIA_ITEMS = 500 # 单件媒体数上限
39
+ MAX_MEDIA_BYTES = 200 * 1024 * 1024 # 单件媒体总量上限
40
+
41
+ # 命名空间前缀容错:Excel/openpyxl/WPS 用 `xdr:`/`a:`/`r:`,其余实现可能用别的前缀或省略
42
+ _NS = r"(?:[A-Za-z_][\w.\-]*:)?"
43
+ _ANCHOR = re.compile(r"<" + _NS + r"(oneCellAnchor|twoCellAnchor|absoluteAnchor)\b.*?</" + _NS + r"\1>", re.S)
44
+ _FROM = re.compile(r"<" + _NS + r"from\b[^>]*>(.*?)</" + _NS + r"from>", re.S)
45
+ _TO = re.compile(r"<" + _NS + r"to\b[^>]*>(.*?)</" + _NS + r"to>", re.S)
46
+ _ROW = re.compile(r"<" + _NS + r"row>(\d+)</" + _NS + r"row>")
47
+ _COL = re.compile(r"<" + _NS + r"col>(\d+)</" + _NS + r"col>")
48
+ _BLIP = re.compile(r"<" + _NS + r"blip\b[^>]*?\s" + _NS + r"embed\s*=\s*\"([^\"]+)\"")
49
+ _SHEET_DRAWING = re.compile(r"<" + _NS + r"drawing\b[^>]*?\s" + _NS + r"id\s*=\s*\"([^\"]+)\"")
50
+ _REL = re.compile(r"<Relationship\b([^>]*)/?>")
51
+ _ATTR = re.compile(r'([A-Za-z_:][\w:.\-]*)\s*=\s*"([^"]*)"')
52
+ _SHEET_TAG = re.compile(r"<" + _NS + r"sheet\b([^>]*)/?>")
53
+ _DOC_TOKEN = re.compile(
54
+ r"<" + _NS + r"p\b[^>]*>" # 段落开始
55
+ r"|</" + _NS + r"p>" # 段落结束
56
+ r"|(?P<drawing><" + _NS + r"drawing\b.*?</" + _NS + r"drawing>)"
57
+ r"|(?P<text><" + _NS + r"t\b[^>]*>(.*?)</" + _NS + r"t>)",
58
+ re.S,
59
+ )
60
+
61
+
62
+ # ---------------------------------------------------------------- 通用小件
63
+
64
+ def _read_part(z: zipfile.ZipFile, name: str):
65
+ try:
66
+ return z.read(name)
67
+ except (KeyError, OSError):
68
+ return None
69
+
70
+
71
+ def _text(z: zipfile.ZipFile, name: str) -> str:
72
+ data = _read_part(z, name)
73
+ return data.decode("utf-8", "replace") if data is not None else ""
74
+
75
+
76
+ def _rels_map(z: zipfile.ZipFile, rels_part: str) -> dict:
77
+ """`<Relationship Id Target>` → {Id: Target}(不假设属性顺序)。"""
78
+ out = {}
79
+ body = _text(z, rels_part)
80
+ if not body:
81
+ return out
82
+ for m in _REL.finditer(body):
83
+ a = dict(_ATTR.findall(m.group(1)))
84
+ if a.get("Id") and a.get("Target"):
85
+ out[a["Id"]] = a["Target"]
86
+ return out
87
+
88
+
89
+ def _resolve(rels_part: str, target: str) -> str | None:
90
+ """把 rels 的 Target 解析为包内部件路径(相对 rels 所属部件所在目录)。"""
91
+ if not target:
92
+ return None
93
+ if target.startswith("/"):
94
+ return target.lstrip("/")
95
+ base = os.path.dirname(os.path.dirname(rels_part))
96
+ return os.path.normpath(os.path.join(base, target)).replace(os.sep, "/")
97
+
98
+
99
+ def _rels_part_for(part: str) -> str:
100
+ d, b = os.path.split(part)
101
+ return (d + "/_rels/" + b + ".rels").lstrip("/")
102
+
103
+
104
+ def _unescape(s: str) -> str:
105
+ for a, b in (("&lt;", "<"), ("&gt;", ">"), ("&quot;", '"'), ("&apos;", "'"), ("&amp;", "&")):
106
+ s = s.replace(a, b)
107
+ return s
108
+
109
+
110
+ def _norm_sheet(name) -> str:
111
+ return str(name or "").replace("\u3000", " ").strip()
112
+
113
+
114
+ def _col_index(letters) -> int:
115
+ n = 0
116
+ for ch in str(letters).upper():
117
+ if "A" <= ch <= "Z":
118
+ n = n * 26 + (ord(ch) - 64)
119
+ return n
120
+
121
+
122
+ def _safe_name(name: str) -> str:
123
+ s = re.sub(r'[\\/:*?"<>|\r\n\t]', "_", str(name)).strip().strip(".")
124
+ s = re.sub(r"\.+$", "", s)
125
+ return s or "media"
126
+
127
+
128
+ def _sha256(data: bytes) -> str:
129
+ return hashlib.sha256(data).hexdigest()
130
+
131
+
132
+ def _write_media(dest_dir: str, name: str, data: bytes) -> str:
133
+ os.makedirs(dest_dir, exist_ok=True)
134
+ out = os.path.join(dest_dir, _safe_name(name))
135
+ i = 1
136
+ stem, ext = os.path.splitext(out)
137
+ while os.path.exists(out):
138
+ out = f"{stem}-{i}{ext}"
139
+ i += 1
140
+ with open(out, "wb") as f:
141
+ f.write(data)
142
+ return out
143
+
144
+
145
+ class _Budget:
146
+ """单件媒体数与总量的硬护栏:超限即停并留痕,不静默截断。"""
147
+
148
+ def __init__(self, items: int = MAX_MEDIA_ITEMS, nbytes: int = MAX_MEDIA_BYTES):
149
+ self.items, self.nbytes = items, nbytes
150
+ self.used_items = self.used_bytes = 0
151
+ self.media_seq = 0
152
+ self.stopped = False
153
+
154
+ def next_id(self) -> str:
155
+ """案例内全局媒体编号(跨文件唯一,供交付物 `locator`/追溯引用)。"""
156
+ self.media_seq += 1
157
+ return f"M{self.media_seq:04d}"
158
+
159
+ def take(self, size: int) -> bool:
160
+ if self.stopped:
161
+ return False
162
+ if self.used_items + 1 > self.items or self.used_bytes + size > self.nbytes:
163
+ self.stopped = True
164
+ return False
165
+ self.used_items += 1
166
+ self.used_bytes += size
167
+ return True
168
+
169
+
170
+ class _Ctx:
171
+ """一次媒体抽取的上下文:来源信息 + 落盘目录 + 元数据。"""
172
+
173
+ def __init__(self, path, outdir, case_dir, rel_path, stage, origin, budget=None):
174
+ self.path = path
175
+ self.name = os.path.basename(path)
176
+ self.outdir = outdir
177
+ self.case_dir = case_dir
178
+ self.rel_path = rel_path
179
+ self.stage = stage
180
+ self.origin = origin
181
+ self.budget = budget or _Budget()
182
+ self.dest_dir = os.path.join(outdir, *[p for p in (stage, os.path.basename(path)) if p])
183
+
184
+ def entry(self, kind, local_path, locator, **extra):
185
+ data = {}
186
+ with open(local_path, "rb") as f:
187
+ data = f.read()
188
+ rec = {
189
+ "sourcePath": self.rel_path,
190
+ "stage": self.stage,
191
+ "kind": kind,
192
+ "locator": locator,
193
+ "localPath": os.path.relpath(local_path, self.case_dir),
194
+ "sizeBytes": len(data),
195
+ "sha256": _sha256(data),
196
+ "evidenceChannel": "host-vision",
197
+ "note": "由编排层从 raw 原件导出;判读须经宿主多模态读图,读不到只能出「未核验」",
198
+ }
199
+ if self.origin:
200
+ rec["originArchive"] = self.origin
201
+ rec.update(extra)
202
+ return rec
203
+
204
+
205
+ # ---------------------------------------------------------------- xlsx
206
+
207
+ def _xlsx_sheets(z: zipfile.ZipFile) -> list:
208
+ """[{name, state, part}]——只读 workbook.xml / rels 的结构元数据。"""
209
+ body = _text(z, "xl/workbook.xml")
210
+ if not body:
211
+ return []
212
+ rels = _rels_map(z, "xl/_rels/workbook.xml.rels")
213
+ out = []
214
+ for m in _SHEET_TAG.finditer(body):
215
+ a = dict(_ATTR.findall(m.group(1)))
216
+ rid = a.get("r:id") or a.get("id") or ""
217
+ out.append({"name": a.get("name"), "state": a.get("state") or "visible",
218
+ "part": _resolve("xl/_rels/workbook.xml.rels", rels.get(rid, ""))})
219
+ return out
220
+
221
+
222
+ def _anchor_span(block: str):
223
+ """锚点块 → (起行, 止行, 起列, 止列, 可判定);行列为 1-based。
224
+
225
+ `oneCellAnchor` 只有起点(`<from>`),止行/止列取起点并标注 `anchorKind`;
226
+ `absoluteAnchor` 无行列信息 → 不可判定(H0 保守:不导出)。
227
+ """
228
+ f = _FROM.search(block)
229
+ t = _TO.search(block)
230
+ if not f:
231
+ return 0, 0, 0, 0, False
232
+ fr, fc = _ROW.search(f.group(1)), _COL.search(f.group(1))
233
+ if not (fr and fc):
234
+ return 0, 0, 0, 0, False
235
+ r0, c0 = int(fr.group(1)) + 1, int(fc.group(1)) + 1
236
+ if t:
237
+ tr, tc = _ROW.search(t.group(1)), _COL.search(t.group(1))
238
+ r1 = int(tr.group(1)) + 1 if tr else r0
239
+ c1 = int(tc.group(1)) + 1 if tc else c0
240
+ else:
241
+ r1, c1 = r0, c0
242
+ return r0, max(r0, r1), c0, max(c0, c1), True
243
+
244
+
245
+ def xlsx_media(path, hidden, ctx, dest_dir, source_path=None):
246
+ """导出 `xl/media` 内**可见锚点**的媒体;返回 (entries, hiddenSkipped, unresolvedReasons)。"""
247
+ entries, skipped, unresolved = [], 0, []
248
+ try:
249
+ z = zipfile.ZipFile(path)
250
+ except Exception as e: # noqa: BLE001
251
+ return [], 0, [f"原件包不可读({type(e).__name__}):媒体本次未核"]
252
+ with z:
253
+ raw = [n for n in z.namelist() if n.startswith("xl/media/") and not n.endswith("/")]
254
+ if not raw:
255
+ return [], 0, []
256
+ if not hidden:
257
+ # H0 fail-closed:隐藏结构不可得时不得导出(无法判定锚点是否落在隐藏区)
258
+ return [], 0, [f"隐藏结构不可得:{len(raw)} 个媒体部件未导出(H0 保守,未核 ≠ 缺失)"]
259
+ hidden_sheets = {_norm_sheet(s) for s in (hidden.get("hiddenSheets") or [])}
260
+ hid_rows = {_norm_sheet(k): set(v) for k, v in (hidden.get("hiddenRows") or {}).items()}
261
+ hid_cols = {_norm_sheet(k): {_col_index(c) for c in v}
262
+ for k, v in (hidden.get("hiddenCols") or {}).items()}
263
+ referenced, seq = set(), 0
264
+ for sh in _xlsx_sheets(z):
265
+ name, part = _norm_sheet(sh["name"]), sh["part"]
266
+ if not part:
267
+ continue
268
+ sheet_xml = _text(z, part)
269
+ srels = _rels_map(z, _rels_part_for(part))
270
+ # 隐藏 sheet(含 sheet_state)整体跳过:不定位、不导出(其媒体仍记为"已被引用",
271
+ # 以免被误算成"未引用部件",但绝不出现在导出清单里)
272
+ is_hidden = name in hidden_sheets or sh["state"] != "visible"
273
+ for did in _SHEET_DRAWING.findall(sheet_xml):
274
+ dpart = _resolve(_rels_part_for(part), srels.get(did, ""))
275
+ if not dpart:
276
+ continue
277
+ dxml = _text(z, dpart)
278
+ drels = _rels_map(z, _rels_part_for(dpart))
279
+ for m in _ANCHOR.finditer(dxml):
280
+ blk, kind = m.group(0), m.group(1)
281
+ r0, r1, c0, c1, ok = _anchor_span(blk)
282
+ for rid in _BLIP.findall(blk):
283
+ mpart = _resolve(_rels_part_for(dpart), drels.get(rid, ""))
284
+ if not mpart or not mpart.startswith("xl/media/"):
285
+ continue
286
+ referenced.add(mpart)
287
+ if is_hidden:
288
+ skipped += 1
289
+ continue
290
+ seq += 1
291
+ if not ok:
292
+ unresolved.append(
293
+ f"{name}!drawing 锚点不可判定({kind})→ {mpart} 未导出(H0 保守)")
294
+ continue
295
+ if (set(range(r0, r1 + 1)) & hid_rows.get(name, set())
296
+ or set(range(c0, c1 + 1)) & hid_cols.get(name, set())):
297
+ skipped += 1 # H0:隐藏行/列锚点整体跳过(不导出、不定位)
298
+ continue
299
+ data = _read_part(z, mpart)
300
+ if data is None:
301
+ unresolved.append(f"{mpart} 读取失败:未导出")
302
+ continue
303
+ if not ctx.budget.take(len(data)):
304
+ unresolved.append("单件媒体数/总量超上限:剩余媒体未导出(未核 ≠ 缺失)")
305
+ break
306
+ ext = os.path.splitext(mpart)[1] or ".png"
307
+ local = _write_media(dest_dir, f"{name}-图片{seq}{ext}", data)
308
+ entries.append(ctx.entry(
309
+ "xlsx内嵌图", local,
310
+ f"{name}!图片#{seq}(锚点 {r0}:{r1})",
311
+ rawPart=mpart, sheet=sh["name"], anchorRows=[r0, r1],
312
+ anchorKind="oneCell" if r0 == r1 and kind == "oneCellAnchor" else kind))
313
+ orphan = [n for n in raw if n not in referenced]
314
+ if orphan:
315
+ unresolved.append(
316
+ f"{len(orphan)} 个媒体部件未被任何 drawing 引用(含页眉页脚/图表/已删除图):未导出")
317
+ return entries, skipped, unresolved
318
+
319
+
320
+ # ---------------------------------------------------------------- docx / doc
321
+
322
+ def docx_media(path, ctx, dest_dir, container=None):
323
+ """导出 `.docx` 正文内联图;返回 (entries, hiddenSkipped, unresolvedReasons)。
324
+
325
+ 正文图按**段落序**定位(附前一段文字便于人工核对);页眉页脚与未被正文引用的
326
+ `word/media` 部件按版式证据处理——本次不导出,记未核原因(未核 ≠ 缺失)。
327
+ """
328
+ entries, unresolved = [], []
329
+ container = container or path
330
+ try:
331
+ z = zipfile.ZipFile(container)
332
+ except Exception as e: # noqa: BLE001
333
+ return [], 0, [f"原件包不可读({type(e).__name__}):媒体本次未核"]
334
+ with z:
335
+ media = [n for n in z.namelist() if n.startswith("word/media/") and not n.endswith("/")]
336
+ if not media:
337
+ return [], 0, []
338
+ doc = _text(z, "word/document.xml")
339
+ rels = _rels_map(z, "word/_rels/document.xml.rels")
340
+ # 页眉页脚引用的媒体(版式证据)
341
+ hf = set()
342
+ for n in z.namelist():
343
+ if re.match(r"word/(header|footer)\d*\.xml$", n):
344
+ r2 = _rels_map(z, _rels_part_for(n))
345
+ for tgt in r2.values():
346
+ p2 = _resolve(_rels_part_for(n), tgt)
347
+ if p2:
348
+ hf.add(p2)
349
+ used, seq, para, nearby = set(), 0, 0, ""
350
+ for m in _DOC_TOKEN.finditer(doc):
351
+ tok = m.group(0)
352
+ if m.group("drawing"):
353
+ for rid in _BLIP.findall(m.group("drawing")):
354
+ mpart = _resolve("word/_rels/document.xml.rels", rels.get(rid, ""))
355
+ if not mpart or not mpart.startswith("word/media/"):
356
+ continue
357
+ used.add(mpart)
358
+ data = _read_part(z, mpart)
359
+ if data is None:
360
+ unresolved.append(f"{mpart} 读取失败:未导出")
361
+ continue
362
+ if not ctx.budget.take(len(data)):
363
+ unresolved.append("单件媒体数/总量超上限:剩余媒体未导出(未核 ≠ 缺失)")
364
+ continue
365
+ seq += 1
366
+ ext = os.path.splitext(mpart)[1] or ".png"
367
+ local = _write_media(dest_dir, f"第{max(para, 1)}段-图{seq}{ext}", data)
368
+ hint = nearby[:20]
369
+ entries.append(ctx.entry(
370
+ "docx内嵌图", local,
371
+ f"{ctx.name}!第{max(para, 1)}段图片#{seq}" + (f"(前文:{hint}…)" if hint else ""),
372
+ paragraphIndex=max(para, 1), rawPart=mpart,
373
+ nearbyText=nearby[:80] or None))
374
+ elif tok.startswith("<") and re.match(r"<" + _NS + r"p\b", tok):
375
+ para += 1
376
+ elif m.group("text") is not None:
377
+ t = _unescape(re.sub(r"<[^>]+>", "", m.group(3) or "")).strip()
378
+ if t:
379
+ nearby = t
380
+ rest = [n for n in media if n not in used]
381
+ hf_rest = [n for n in rest if n in hf]
382
+ other = [n for n in rest if n not in hf]
383
+ if hf_rest:
384
+ unresolved.append(f"{len(hf_rest)} 张页眉页脚内图片(版式证据,本次不导出)")
385
+ if other:
386
+ unresolved.append(f"{len(other)} 个媒体部件未被正文引用(图表/嵌入对象等):未导出")
387
+ return entries, 0, unresolved
388
+
389
+
390
+ def doc_media(path, ctx, dest_dir):
391
+ """.doc(OLE):先用 textutil 转 docx 到临时目录,再按 docx 抽取。
392
+
393
+ textutil 转换可能降级(文本框内图片有丢失风险),盘点会标注该通道为派生化。
394
+ """
395
+ if not shutil.which("textutil"):
396
+ return [], 0, [".doc 需 textutil 转 docx 才能取媒体,本机不可得:媒体本次未核"]
397
+ with tempfile.TemporaryDirectory() as td:
398
+ out = os.path.join(td, "converted.docx")
399
+ try:
400
+ r = subprocess.run(["textutil", "-convert", "docx", "-output", out, path],
401
+ capture_output=True, text=True)
402
+ except Exception as e: # noqa: BLE001
403
+ return [], 0, [f"textutil 调用失败({type(e).__name__}):媒体本次未核"]
404
+ if r.returncode != 0 or not os.path.exists(out):
405
+ return [], 0, [f"textutil 转 docx 失败:{r.stderr.strip()[:100]}(媒体本次未核)"]
406
+ entries, skipped, unresolved = docx_media(path, ctx, dest_dir, container=out)
407
+ if unresolved or entries:
408
+ unresolved.append(".doc 媒体经 textutil→docx 派生化抽取,保真度低于原生 docx")
409
+ return entries, skipped, unresolved
410
+
411
+
412
+ # ---------------------------------------------------------------- pdf
413
+
414
+ def pdf_media(path, ctx, dest_dir):
415
+ """PDF:取每页**内嵌位图**;无文本层且无内嵌图 → 记「疑扫描件,未核」(本机无渲染)。"""
416
+ try:
417
+ from pypdf import PdfReader
418
+ except ImportError:
419
+ return [], 0, ["缺 pypdf:PDF 文本与内嵌图本次未核(未核 ≠ 缺失)"]
420
+ try:
421
+ reader = PdfReader(path)
422
+ except Exception as e: # noqa: BLE001
423
+ return [], 0, [f"PDF 打开失败({type(e).__name__}):媒体本次未核"]
424
+ entries, unresolved = [], []
425
+ for i, page in enumerate(reader.pages, 1):
426
+ try:
427
+ imgs = list(page.images)
428
+ except Exception: # noqa: BLE001
429
+ imgs = []
430
+ if not imgs:
431
+ try:
432
+ txt = page.extract_text() or ""
433
+ except Exception: # noqa: BLE001
434
+ txt = ""
435
+ if not txt.strip():
436
+ unresolved.append(f"第 {i} 页无文本层且无内嵌图(疑扫描件):需渲染,本机不可得,本次未核")
437
+ continue
438
+ for j, im in enumerate(imgs, 1):
439
+ try:
440
+ data = im.data
441
+ except Exception: # noqa: BLE001
442
+ unresolved.append(f"第 {i} 页第 {j} 张内嵌图读取失败:未导出")
443
+ continue
444
+ if not data:
445
+ continue
446
+ if not ctx.budget.take(len(data)):
447
+ unresolved.append("单件媒体数/总量超上限:剩余媒体未导出(未核 ≠ 缺失)")
448
+ break
449
+ name = getattr(im, "name", None) or f"第{i}页图{j}.png"
450
+ ext = os.path.splitext(name)[1] or ".png"
451
+ local = _write_media(dest_dir, f"第{i}页-图{j}{ext}", data)
452
+ entries.append(ctx.entry("pdf内嵌图", local, f"{ctx.name}#第{i}页图片#{j}",
453
+ page=i, rawPart=name))
454
+ return entries, 0, unresolved
455
+
456
+
457
+ # ---------------------------------------------------------------- 独立图片
458
+
459
+ def standalone_image(path, ctx, dest_dir):
460
+ """独立图片文件本身即证据:原样落盘并登记(本机无 OCR,判读交宿主视觉)。"""
461
+ try:
462
+ with open(path, "rb") as f:
463
+ data = f.read()
464
+ except OSError as e:
465
+ return [], 0, [f"图片读取失败({e.__class__.__name__}):未导出"]
466
+ if not ctx.budget.take(len(data)):
467
+ return [], 0, ["单件媒体数/总量超上限:未导出(未核 ≠ 缺失)"]
468
+ local = _write_media(dest_dir, ctx.name, data)
469
+ return [ctx.entry("独立图片", local, f"{ctx.name}(整图)")], 0, []
470
+
471
+
472
+ # ---------------------------------------------------------------- 入口
473
+
474
+ def plan_media(path, ext, kind, hidden, outdir, case_dir, rel_path, stage,
475
+ origin=None, budget=None):
476
+ """按格式抽取媒体证据。
477
+
478
+ 返回 `{"entries": [...], "summary": {...}}`;`summary` 为**计数与未核原因**,
479
+ 不含任何未导出媒体的内容或定位。
480
+ """
481
+ ext = (ext or "").lower()
482
+ ctx = _Ctx(path, outdir, case_dir, rel_path, stage, origin, budget)
483
+ dest = ctx.dest_dir
484
+ if ext == ".xlsx":
485
+ entries, skipped, unresolved = xlsx_media(path, hidden, ctx, dest)
486
+ elif ext == ".docx":
487
+ entries, skipped, unresolved = docx_media(path, ctx, dest)
488
+ elif ext == ".doc" and kind == "ole":
489
+ entries, skipped, unresolved = doc_media(path, ctx, dest)
490
+ elif ext == ".pdf":
491
+ entries, skipped, unresolved = pdf_media(path, ctx, dest)
492
+ elif ext == ".xls" and kind == "ole":
493
+ entries, skipped, unresolved = [], 0, [
494
+ "xls 老二进制:本机无媒体提取通道(需 soffice 转换),若有内嵌图本次未核(未核 ≠ 缺失)"]
495
+ elif kind == "png" or ext in IMAGE_EXTS:
496
+ entries, skipped, unresolved = standalone_image(path, ctx, dest)
497
+ else:
498
+ return {"entries": [], "summary": None}
499
+ if not entries and not skipped and not unresolved:
500
+ return {"entries": [], "summary": None}
501
+ for e in entries:
502
+ e["mediaId"] = ctx.budget.next_id()
503
+ summary = {
504
+ "exportedCount": len(entries),
505
+ "hiddenSkippedCount": skipped,
506
+ "unresolvedCount": len(unresolved),
507
+ "unresolvedReasons": unresolved,
508
+ "evidenceChannel": "host-vision",
509
+ "localDir": os.path.relpath(dest, case_dir) if entries else None,
510
+ "note": ("媒体证据已导出到案例目录(工作版不含媒体):判读经宿主多模态读图;"
511
+ "读不到只能出「未核验」——未核 ≠ 缺失;锚点落在隐藏区的媒体按 H0 不导出"),
512
+ }
513
+ return {"entries": entries, "summary": summary}