Releases: cnkids/dsh-office-toolkit
Release list
v0.3.24
Full Changelog: v0.3.23...v0.3.24
v0.3.23
v0.3.22 — 精简更新说明
更新方式(重要)
更新请用带版本号的直链:
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/download/v0.3.22/dsh-office-toolkit-0.3.22.tgz
releases/latest/download/... 的内容会随发版变化,pnpm 可能复用缓存不刷新(pnpm < 11.10 尤其明显)。若要用它,请先确认 pnpm -v ≥ 11.10;拉不到新版时也可以直接下 -offline.zip 解压后用 link: 安装。
装完确认版本:DSH 启动日志里的 [dsh-office-toolkit] v0.3.22 …,或 profile 里那份 package.json 的 version。
本次改动(纯文档)
- README「更新」只保留结论与命令,删掉排查过程叙述
docs/usage.md删掉与 README 重复的小节docs/changelog.md近期条目压成一行一条
无代码变更;112 项测试全过;离线包三道门禁通过。
v0.3.21 — 更新装不到新版的根因与彻底解法
更新方式
更新请用带版本号的直链;releases/latest/download/... 的内容会随发版变化,pnpm 可能复用缓存不刷新(pnpm < 11.10 尤其明显)。装完可用启动日志或 profile 里 package.json 的 version 确认。
详见 README「安装 / 更新」。
本次为纯文档改动,无代码变更。
v0.3.20 — 主部件按包关系解析 · 版本号进日志 · 修正更新指引
先说结论:你机器上装的是 0.3.7,不是最新版
reused 174, downloaded 0 就是证据 —— pnpm 按 URL 复用了本地缓存,没有真正下载。releases/latest/download/... 这个地址永远不变,所以 pnpm 认为「还是同一份」,命令显示成功、版本却没变。这台机器从来没拿到过任何读取修复,这才是同一份文件一直报 Could not find main document part 的原因。
立刻可用(带版本号的直链,URL 每次发版都不同,不会被缓存复用)
dsh plugin --profile web remove dsh-office-toolkit
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/download/v0.3.20/dsh-office-toolkit-0.3.20.tgz装完务必确认版本(这次新增了启动日志,一眼可见):
(Get-Content "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-office-toolkit\package.json" | ConvertFrom-Json).version
# 或看 dsh web 启动日志:[dsh-office-toolkit] v0.3.20 已注册 6 个 Office 工具这一版的内容
1. 主文档部件按「包关系」解析
以前只按规范名 word/document.xml 找主部件。现在与真正的 OOXML 读取器一致:先读 _rels/.rels 里的 officeDocument 关系拿到 Target,再退回规范名,最后退回「以 document.xml 结尾」的容忍查找。主部件放在非规范路径(如 word/main.xml)的合法文件也能读;格式嗅探同样改成关系优先(此前只看规范名,会把这类文件误判成 BAD_CONTAINER)。顺带收紧兜底:不再把 word/styles.xml 之类误当主部件。
2. 版本号进日志与报错
- 启动日志:
[dsh-office-toolkit] v0.3.20 已注册 6 个 Office 工具 - 读取失败的诊断:插件版本 + zip 条目数 + 主部件路径 + 修正条目数
- 无法识别的容器:列出实际条目名 + 插件版本
以后再出问题,把报错原文发来就能直接定位(也能立刻看出跑的是哪一版)。
3. 修正文档里错误的更新指引
README 的「更新」已重写:说清 latest 直链的缓存陷阱,推荐带版本号的直链,并给出确认版本的命令;FAQ 增补「更新后版本没变 / reused … downloaded 0」条目。发版步骤也补了「带版本号的资产才是可靠更新通道」。
质量
112 项测试全过(新增:主部件按关系解析、非规范路径端到端读取、损坏容器诊断);npm audit 0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,质量门通过(Sonar 服务端本次多次 TLS 握手失败,重试 5 次后成功);离线包三道门禁全部通过。
v0.3.19 — 非标准 zip:条目名与引用一起改 + 读不出时兜底
安装 / 更新(建议更新:这次针对的正是「读不出来」)
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/latest/download/dsh-office-toolkit.tgz
重跑这一条就是更新。装完重启 dsh web 并新建会话。
为什么 0.3.17 / 0.3.18 还是读不出来
0.3.17 只把 zip 条目名改成规范写法,没有同步改引用它的部件。当文件里 _rels/.rels、[Content_Types].xml 的 Target 也写成反斜杠(或不同大小写)时,改名后两边反而对不上,于是照样报:
Could not find main document part. Are you sure this is a valid .docx file?
这一版的做法
lib/core/ooxml.js 重写
- 条目名原地按字节改名:直接改中央目录与本地文件头的名字字段,不解压任何条目。反斜杠→斜杠、大小写归一化都不改变名字字节长度,正好适用;顺带避免了大文件重新压缩的开销。
- 同步改写引用部件:
[Content_Types].xml与各*.rels(共 4 个已知小部件),大小写不敏感匹配,覆盖/path、path、\path、/path四种写法。 - 名字长度会变的少见情形(如
word//document.xml)不做字节改写,交给读取侧的容忍查找。
lib/core/word.js 解析兜底:mammoth 认不出(找不到主文档部件 / 没有 body)时,用内置解析器从 word/document.xml 取正文,并在 meta.reader 里标明用了哪种读法 —— 保证「能开 zip 就读得出字」,而不是整条读取失败。
验证:四种真实写法全部读通
| 变体 | 结果 |
|---|---|
| 条目 + rels 均为反斜杠 | ✅ 自动修正 17 处 |
| 条目反斜杠 + rels 正斜杠 | ✅ 自动修正 17 处 |
条目 + rels 均为大写(Word/) |
✅ 自动修正 9 处 |
| 大小写 + 反斜杠混合 | ✅ |
这四种都进了回归测试(含此前失败的两种)。
顺带
Sonar 报了一个新安全热点,查出来是测试里用 Math.random() 生成临时文件名(S2245),已改为确定性编号;顺手修掉 S3776 / S6557。现在 security_hotspots = 0。
质量
109 项测试全过(核心 57 · 跨平台 16 · 插件 36);npm audit 0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,质量门通过;离线包三道门禁(依赖自包含 145 处 / 112 包、核心自测、插件冒烟)全部通过。
v0.3.18 — 热修:读取非标准文件不再崩
安装 / 更新(这是一次热修,建议尽快更新)
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/latest/download/dsh-office-toolkit.tgz
重跑这一条就是更新。装完重启 dsh web 并新建会话。
修 0.3.17 引入的回归
实测报错:
Error: Cannot add property containerNote, object is not extensible
0.3.17 为了在读取结果里提示「已自动修正 N 个非标准 zip 条目名」,直接往调用方的参数对象上写了一个属性 —— 而 DSH 宿主传入的参数是冻结的,赋值即抛。
最讽刺的是它的触发条件:只有需要修复的非标准文件才会加这条提示,所以普通文件一切正常,而反斜杠 zip、改过后缀的文件 100% 直接崩 —— 恰好是 0.3.17 想帮的那一类。
修法:需要加注时新建对象({ ...opts, containerNote }),不再改动调用方参数。全库审计确认这是唯一一处改写调用方参数(io 均为新建对象,ops / data 从未被改写)。
把这个 bug 类别钉死在 CI 里
- 契约测试:8 条调用(含带注的非标准 zip、
withFormatting)全部用Object.freeze传参,必须正常执行;并断言调用方参数未被改写。 - 假宿主改为冻结参数:
plugin-smoke现在像真实宿主一样冻结每次工具调用的参数对象,26 项冒烟(所有工具 × 各种参数组合)都在冻结条件下跑。
也就是说,以后任何「往宿主参数上写属性」的改法都会在测试里立刻失败。
质量
107 项测试全过(核心 55 · 跨平台 16 · 插件 36);npm audit 0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,质量门通过;离线包三道门禁(依赖自包含 145 处 / 112 包、核心自测、插件冒烟)全部通过。
v0.3.17 — 保证 Word/Excel 都能正常读取
安装 / 更新
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/latest/download/dsh-office-toolkit.tgz
重跑这一条就是更新。装完重启 dsh web 并新建会话。
这一版只做一件事:保证文件真的读得出来
修掉了那个「读不出来」的真因
有一类 Word 文件的 zip 条目名用的是反斜杠(word\document.xml)—— 部分国产工具、或在 Windows 上手工重打包过的文件会这样。mammoth / ExcelJS 按 word/document.xml 去找,找不到就报:
Could not find main document part. Are you sure this is a valid .docx file?
现在读取前会自动修正条目名,并在结果里提示修正了几个。此问题已在本机复现并加入回归测试。
不只信扩展名,按文件真实内容判断
| 文件的样子 | 现在的行为 |
|---|---|
| zip 条目名带反斜杠 | 自动修正后正常读取 |
后缀 .doc,内容其实是 .docx |
按 docx 读,并提示「文件内容其实是 .docx」 |
后缀 .docx,内容其实是老的 OLE .doc |
按老格式读,不再报「docx 无法解压」 |
后缀 .xlsx,内容其实是 CSV / TSV 文本 |
按分隔符文本读,并提示实际格式 |
| zip 里没有 Office 主文档 | 报 BAD_CONTAINER,并列出实际条目名,便于判断文件真身 |
错误语义保持不变:文件不存在 → NOT_FOUND,传入目录 → NOT_A_FILE,不支持的后缀 → UNSUPPORTED_FORMAT。
顺带修一个真 bug
0.3.16 引入的默认样式解析有错:attrOf 对「属性不存在」和「属性为空」都返回空串,导致每个样式都被当成 w:default="1",所有段落都套用了最后一个样式(字体字号因此全错)。改用 attrIn 区分「不存在」后,默认样式与主题字体解析正确。
验证
- 真实文件普查:桌面 / 文档 / 下载 / 项目里共 26 个 Word 文件,全部读取成功;21 个
.docx的格式提取 21/21 通过。 - 新增 4 项兼容性测试(反斜杠条目名、改后缀的 docx / CSV / TSV、OLE 伪装、损坏 zip)。
- 测试 106 项全过;
npm audit0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,质量门通过;离线包三道门禁(依赖自包含 145 处 / 112 包、核心自测、插件冒烟)全部通过。
说明
上一轮试做的「公文规则检查」工具已按反馈移除 —— 规则不写进插件。office_read 的 withFormatting 保留:它只负责把字体、字号、行距、首行缩进、对齐、页面页边距读出来,规则怎么判由你自己决定。
v0.3.16 — 读出 Word 排版格式(字体/字号/行距/缩进)
安装 / 更新
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/latest/download/dsh-office-toolkit.tgz
重跑这一条就是更新。装完重启 dsh web 并新建会话。
新能力:读出 Word 的排版格式
office_read 对 .docx 传 withFormatting: true,正文之后会附一份格式报告,结构化数据在 meta.formatting。用于把文档与行文规则逐条比对(例如「正文三号仿宋、行距固定值 28.8 磅、首行缩进 2 字符」)。
{ "path": "通知.docx", "withFormatting": true }每段给出:中/西文字体、字号、行距、首行缩进、悬挂缩进、左缩进、对齐、样式名、是否在表格内;另有纸张尺寸与页边距、文档默认字体/字号/行距,以及两份便于比对的汇总:
- 格式分布 —— 字体 / 字号 / 行距 / 缩进 / 对齐各自的出现次数,主流值即比对基准
- 偏离主流格式的段落 —— 逐条列出,直接定位不合规处
取值按 Word 的真实优先级合并:docDefaults → 隐式默认样式(w:default="1",Word 会套到没写 w:pStyle 的段落上)→ basedOn 样式链 → 段落直接格式(w:ind / w:spacing / w:jc)→ run 级覆盖(取覆盖文字最多的 run)。主题字体也会解析:Word 默认模板用 w:eastAsiaTheme="minorEastAsia" 这类引用而不是字体名,插件会读 word/theme/theme1.xml 并按 <a:font script="Hans"> 取出中文实际字体。
真实文件验证
| 文件 | 读出结果 |
|---|---|
| 附件1:公文格式规范说明.docx | 仿宋_GB2312 ×24、黑体 ×6、方正小标宋简体 ×1;字号 16pt(三号) ×30、22pt ×1;页边距 上35/下35/左28/右25 mm |
| 市场调查技术试卷.docx | 修复前 75 段读不出字体(主题字体未解析),现在正确读出 宋体 ×75、黑体 ×5,12pt ×79 |
| 中级经济基础口诀(56条).docx | 284 段、微软雅黑 ×247,页面 210×280 mm |
限制:格式提取目前只支持 .docx(.doc / .rtf / .odt 会返回一行提示)。
顺带
按 SonarQube 规则重构了新增模块(拆开超复杂度函数、消除嵌套模板字面量与连续 push);并把文本尾部 trim 从回溯正则 /\s+$/u 改为线性扫描 —— 后者正是本次新出现的那个安全热点,与项目一贯的「不用回溯正则」保持一致。
质量
102 项测试全过(核心 50 · 跨平台 16 · 插件 36);npm audit 0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,新代码覆盖率 91.3%,质量门通过。离线包三道上线门禁(依赖自包含 145 处 / 112 包、核心自测、插件冒烟)全部通过。
v0.3.15 — 离线包验过完整 · 缺依赖报错说人话
安装 / 更新
dsh plugin --profile web add https://github.com/cnkids/dsh-office-toolkit/releases/latest/download/dsh-office-toolkit.tgz
重跑这一条就是更新。装完重启 dsh web 并新建会话。
离线包这次是「验过」的
离线安装要开箱即用,就必须自带完整且自包含的依赖 —— 不能靠 profile 目录兜底,否则到了别人机器上就是运行时缺包。本版新增发布门禁 test/offline-check.mjs,在隔离目录里逐包核对:本包声明的每个依赖、以及包内每个依赖包声明的依赖,都必须解析到包目录以内;同时要求依赖树里没有任何安装脚本。
本版 -offline.zip 的实测结果:
| 门禁 | 结果 |
|---|---|
| 依赖完整性(隔离目录、禁止借用外部目录) | 145 处依赖 / 112 个包 全部落在包内 |
| 依赖树安装脚本 | 0 个(dsh plugin add 不会被 pnpm 构建脚本审批打断) |
| 核心自测 | 47/47 |
| 插件层冒烟(真实读写每个工具) | 36/36 |
npm run verify:offline 已写入发版流程,任一条不过就不发版。
依赖装不全时不再「猜」
起因:profile 里依赖只装了一半时,插件抛的是原始 ERR_MODULE_NOT_FOUND,智能体看不懂就自己退化成写文本 —— 于是「创建的 Excel」变成了 TSV 内容。现在:
- 重依赖改为懒加载:插件本身始终能加载,只有真正用到该模块的工具会失败,其它工具照常可用。已实测:只缺
docx时读.xlsx正常,只缺@wekanteam/exceljs时读.docx正常。 - 报错点名:
缺少依赖 @wekanteam/exceljs(用于:读写 .xlsx)。依赖安装不完整,请重跑下面这条命令补全,然后重启 dsh web: …,并保留原始报错。 - 加载即自检:缺哪个包直接写进 DSH 日志,不用等用户撞上。
二进制输出自检
写入 .xlsx / .docx / .odt 时校验输出必须是真正的 OOXML(PK 头),否则中止并报 BAD_OUTPUT_FORMAT —— 插件不再可能留下「扩展名是 Office、内容却是文本」的假文件。
顺带修复
.tsv转换此前误用逗号分隔(SheetJS 没有 tsv bookType,改为csv+FS: '\t')。- 同格式复制的分支引用了未定义变量。
质量
99 项测试全过(核心 47 · 跨平台 16 · 插件 36);npm audit 0 条;Sonar 0 缺陷 / 0 漏洞 / 0 代码异味 / 0 安全热点,质量门通过。