# 更新日志 [← 返回首页](README.md) | [安装](docs/installation.md) · [卸载](docs/uninstall.md) · [故障排查](docs/troubleshooting.md) 本文件记录 `dsh-plugin-office-markdown` 的每个发布版本,版本号与 Git tag 一致。 - 推 `v*` tag 时,`.github/workflows/release.yml` 会从本文件提取对应小节并作为 GitHub Release 的说明,因此请**保留 `## [x.y.z]` 这一标题写法**,也不要修改已发布版本的标题。 - 本文件**不随 `.tgz` 发布**(`package.json` 的 `files` 白名单里没有它),只存在于仓库中。 - 分类沿用 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/):`Added` / `Changed` / `Fixed` / `Breaking Changes` / `Known Issues`。 - 本文件**主要由 AI 生成**(逐项对照源码与 git tag 写成),经维护者审阅后发布;如与实现有出入,**请以代码为准**。说明见 [README 的「关于本文档」](README.md#关于本文档)。 ## 版本一览 | 版本 | 日期 | 状态 | 主题 | | --- | --- | --- | --- | | [1.2.2](#122---2026-10-05) | 2026-10-05 | 🟢 **推荐使用** | 失效的环境记录能自愈;npm 发布改走 OIDC 可信发布 | | [1.2.1](#121---2026-10-03) | 2026-10-03 | 🟢 **推荐使用** | 卸载不再往 `~/.dsh` 里留东西 | | [1.2.0](#120---2026-10-02) | 2026-10-02 | 🟢 **推荐使用** | 产物保真度、结构索引、陈旧产物清理、批量与目录展开 | | [1.1.1](#111---2026-10-01) | 2026-10-01 | 🟡 过渡版本 | 安装脚本 `-RegisterBundle`,转换能力与 1.1.0 相同 | | [1.1.0](#110---2026-10-01) | 2026-10-01 | 🟠 可用但功能不全 | 数据目录不再写死;卸载时脱离宿主补清 Python 环境 | | [1.0.0](#100---2026-10-01) | 2026-10-01 | 🔴 **无法正常使用** | 首版,已废弃 | **新装请用 v1.2.2。** 版本之间的安装、升级与卸载差异见 [安装](docs/installation.md) 与 [卸载](docs/uninstall.md)。 ## 关于日期 `v1.0.0`、`v1.1.0`、`v1.1.1` 的 tag 与 Release 是 **2026-10-01 同一天一次性补建**的 (三个 tag 的 tagger 时间同为 `20:58:23`,三个 Release 的发布时间相隔 2 秒),所以这三个版本的日期相同; 项目实际开发自 2026-08 起 —— 补档不等于发布。`v1.2.0`(2026-10-02)与 `v1.2.1`(2026-10-03)是真实发布日。 --- ## [1.2.2] - 2026-10-05 **主题**:失效的环境记录能自愈;npm 发布改走 OIDC 可信发布。 ### Fixed - **失效的环境记录不再变成一个卸不掉的按钮**(`lib/env.js`、`lib/client.js`)。 环境记录里保存的是当初安装 MarkItDown 用的 Python 解释器绝对路径。如果那个解释器后来 消失了(清理磁盘时删掉了 DSH 运行时、或 DSH 升级换了运行时路径),旧行为是: `pip uninstall` 连进程都起不来(Windows 报 `ENOENT`,实测 2 毫秒即失败), 而卸载失败时又**故意保留**记录以便重试 —— 设置页于是永远显示一个 「由本插件安装 · 可卸载 N 个包」的幽灵环境,点「卸载」永远失败,用户没有任何办法清掉它。 现在: - 设置页如实标出 **`环境已失效`**(新增 `snapshotSummary().stale`),并说明解释器已不存在; - 点按钮会**直接清理这条失效记录**并正常返回成功,不再去 spawn 一个不存在的解释器; - 按钮文案相应变为「清理这条失效记录」; - **失效判定只认 `ENOENT`**(`interpreterGone()`):权限不足、网络盘离线时包还在原地, 记录原样保留,不会被误删。 - **卸载看门狗不再盲目 spawn**(`lib/removal-watchdog.js`)。1.2.1 起它已经用 `child.on('error')` 兜住了 `ENOENT` 以免把宿主进程带崩,但那只是吞掉错误、什么都没留下。 现在它在启动前先判断解释器是否还在:不在就写一行结论到卸载日志、 删掉失效记录与临时文件,不做无谓的 spawn。 ### Changed - **npm 发布改为 GitHub Actions OIDC 可信发布**(`.github/workflows/release.yml`)。 工作流新增 `id-token: write` 权限,Node 升到 24 并把 npm 升到最新, 打包后直接 `npm publish --access public --provenance`。推 `v*` tag 即可同时完成 npm 发布与 GitHub Release,仓库与本地都不再需要长期 npm token,并自动附带 build provenance 签名。 ### 安装渠道 本版起 **npm 上有了正式发布**,可以在 DSH 的 **设置 → 插件 → 安装** 里直接填包名安装: ``` dsh-plugin-office-markdown ``` 要锁版本就写 `dsh-plugin-office-markdown@1.2.2`。原来那条 GitHub 安装方式仍然可用, 两个来源装的是同一份代码 —— `v*` tag 会同时发布到 npm 与 GitHub Release。 ## [1.2.1] - 2026-10-03 **主题**:卸载不再往 `~/.dsh` 里留东西。 ### Changed - **卸载日志:清理成功时只留一行摘要**(`lib/removal-watchdog.js`)。看门狗在 `pip` 退出码 0、 以及「无需清理」(没有环境记录 / 记录里没有要负责的包)这两类结果下,把 `~/.dsh/dsh-plugin-office-markdown-removal.log` 压成一行 `[时间] 卸载完成:清理 N 个包,pip 退出码 0`。`pip` 非 0、调用 `pip` 失败、环境记录里 没有解释器路径、等待 120 秒仍未确认这四种情况**仍然保留完整转录** —— 只有「没清干净」时, 这份日志才是有用的排查材料。 - **`install.ps1 -Uninstall` 会删除卸载日志**(彻底卸载的语义就是不留东西), 新增 `-KeepRemovalLog` 可以保留它。 - README 更正了日志裁剪的说明:裁剪发生在看门狗**启动**时,而不是「写入前」;并说明了成功路径 会把日志压成一行,只有失败 / 超时才会累积到裁剪阈值。 ### Fixed - **`install.ps1` 补回 UTF-8 BOM**:脚本自己的注释一直写着「本脚本自身带 UTF-8 BOM」, 但仓库里的文件其实没有 BOM。Windows PowerShell 5.1 读无 BOM 的 `.ps1` 会按 ANSI 解码, 中文提示会全部乱码(已在本机 PowerShell 5.1.26100 实测确认)。现在按注释所说的带 BOM 保存。 ### Breaking Changes - 无。 ### Known Issues - 无新增。转换能力与 v1.2.0 完全相同。 --- ## [1.2.0] - 2026-10-02 **主题**:把「转换好了却读不到」「读不到还硬读」「产物越攒越多」这三类浪费一次性修掉。 ### Added - **产物保真度标记**:每个 `.md` 的第一行会写入 ``。 命中缓存时从产物本身读回真实的转换器与保真度,而不是无条件写「保真度高」。 由 1.1.x 生成的旧产物没有这个标记,会如实报告为「保真度未知」,并提示可以用 `force: true` 重转。 - **`action: "outline"`**:只返回已转换产物的标题 / 工作表 / 幻灯片索引(含大致行号),**绝不触发转换**; 转换结果里的返回值也带上同一份索引,并新增 `lineCount`。 - **`action: "clean"`**:默认 `dryRun: true`,只报告会删掉哪些陈旧产物; 范围严格限制在同一个目录、文件名匹配 `<原名>-<8位十六进制>.md` 的普通文件, 绝不递归、绝不删除当前缓存键指向的那一份,也绝不会碰到用户自己写的 `.md`。 - **配置 `pruneStaleArtifacts`**(默认 `false`):开启后每次成功转换会顺手清掉同一源文件的旧产物。 - **批量**:`paths: ["a.xlsx", "b.docx"]`,或直接把目录交给 `path`(默认不递归,`recursive: true` 才进子目录; 一次最多 200 个文件,超出会提示)。逐文件串行转换以避免同时拉起一堆解释器,结果按「一行一个文件」汇总; 并说明目录展开到底做了什么 —— 是否递归、扫到几个文件、挑出几个 Office / PDF、跳过几个非 Office 文件、 有哪些子目录没进去。即使只挑出 1 个文件也会说明,免得「只处理了 1 个文件」被误读成「这个文件夹只有 1 个文件」。 - **设置页「试转一个文件」**:不进模型、不走缓存,直接在本机跑一遍完整转换链, 返回转换器、保真度、体积、行数、tokens、耗时与 500 字预览; 试转产物固定叫 `<文件名>-test.md`,放在源文件旁边(配了 `tmpDir` 就放那里), 每次覆盖、不会堆积,也不会被工具误当成缓存结果。 - **环境探测并发化**:每个解释器另有独立的超时预算,卡死的会被标成「⏱ 未响应」而不是拖住整个设置页; 探测结果按 `probeTtlMs` 缓存(默认 10 分钟),页面上多了一个「重新检查」可强制重探; 安装 / 卸载任务结束后缓存会自动失效。 ### Changed - **体积与读取策略**:返回值给出明确的读取建议「先读前 200 行看结构,再用 grep 在同一个 `.md` 里定位, 不要整份读入」;**产物本身永远不被裁剪** —— 彻底交给模型的是一个完整的 Markdown 文件。 - `preview: 0` 不再把整份 Markdown 读进内存:体积、行数、token 估算改为流式统计; `action: "read"` 的预览上限改为遵循配置里的 `maxPreviewChars`(原先硬编码 4000)。 - `read` 守卫在产物已经存在时,直接给出那个 `.md` 的路径,而不是让模型「先去转换一遍」; 源文件此后有改动时会说明这是较早的产物。拦截提示也改得更明确: 换成别的路径去读同一个文件并不能绕过守卫,正确做法是关闭 `guardReadTool`。 - **兜底产物会自动升级**:缓存里是内置兜底转换的结果、而这台机器现在已经有 MarkItDown 时, 不再默默复用低保真产物,而是重新转换一次并说明原因。 - 每次转换成功 / 失败各记一行日志,内容只有路径、大小、耗时与转换器 ID, 不记录文件正文,也不记录完整解释器路径。 - `package.json` 补齐 `repository` / `homepage` / `bugs` / `author`。 - 新增 CI(`.github/workflows/ci.yml`):语法检查、tag 与 `package.json` 版本号一致性、 `npm pack` 后确认 `.tgz` 顶层目录是 `package/` 且关键文件都在。 - 新增发布工作流(`.github/workflows/release.yml`):推 `v*` tag 时自动创建 Release 并上传 `.tgz`, 说明文字用 `gh release create --notes-file` 传入,避免中文在 Windows 上被写坏。 ### Fixed - **内容没变就不再重复转换**:修改时间变了但内容没变(`git checkout`、复制、从备份还原)时, 用记录在产物里的源文件指纹比对内容哈希,确认没变就复用已有 `.md` 并把它重新挂到当前缓存键上, 不再写第二份一模一样的文件。 ### Breaking Changes - 无。 ### Known Issues - 无。 --- ## [1.1.1] - 2026-10-01 相对 v1.1.0 **没有新的转换能力**,主要补安装体验。 ### Added - `install.ps1` 新增 **`-RegisterBundle`**:把源码打成 tarball 放进 `\local-packages\`, 在 profile `package.json` 里同时登记 `dsh.profile.bundles` 与 `dependencies` 的 `file:` 指向, 再用 DSH 自带的 pnpm 跑一次 install —— 装完插件会出现在「设置 → 插件」页面里,并且能一键卸载。 该模式下不再写 `cordis.patch.yml` 受管块(bundle 自带一份),若检测到旧的受管块会被移除, 避免同一插件被加载两次。 - 脚本内补充说明:pnpm 对 `file:` tarball 是按「路径 + 版本号」缓存的, 改完 `lib\*.js` 之后必须先升 `package.json` 的 `version`,否则装上去的还是旧内容。 ### Changed - `README.md` 调整安装章节;`package.json` 更新。 ### Breaking Changes - 无。 ### Known Issues - 转换能力与 v1.1.0 相同:不具备 v1.2.0 的产物保真度标记、结构索引、批量与目录展开、 `action: "clean"` / `pruneStaleArtifacts`、内容未变复用、试转入口;环境探测是串行且可能卡住设置页。 - **升级时不要走「卸载 → 重装」** —— 卸载会触发 Python 环境清理(v1.1.0 引入的行为)。 --- ## [1.1.0] - 2026-10-01 ### Added - **`lib/paths.js`**:不再把 `~/.dsh` 写死在代码里,改为三级回退 「profile 目录反推 → 环境变量 `DSH_HOME` → `~/.dsh`」。 换数据目录、或用非默认 profile,都不会再找错环境记录与日志。 - **`lib/removal-watchdog.js`**:卸载插件时派发一个**脱离宿主**的看门狗进程。 DSH 的卸载流程是「先销毁插件、几秒后才删目录」,宿主里的定时器一旦遇到重启就全没了; 看门狗自己轮询,确认插件真的被移除后才执行 `pip uninstall`。 它带单实例锁与 120 秒寿命上限,平时关闭 / 重启 DSH 不会启动任何常驻进程。 ### Changed - `lib/convert.js`、`lib/env.js`、`lib/index.js` 更新;`install.ps1` 整体重写; `README.md`、`package.json` 更新。 ### Breaking Changes - 无。 ### Known Issues - 不具备 v1.2.0 的能力:无产物保真度标记、无 `lineCount` / `outline`、无批量与目录展开、 无 `action: "clean"` / `pruneStaleArtifacts`、无「内容没变就复用」、无试转入口; 环境探测是串行且可能卡住设置页。 --- ## [1.0.0] - 2026-10-01 ### Added - 首个发布版本:`read_office_as_markdown` 工具、内置兜底转换器、设置页「Office 转换」, 以及在 DSH 里卸载插件时一并清理它登记过的 Python 包。 ### Known Issues - **该版本无法正常使用**,已被 v1.1.0 及之后的版本取代。**请勿安装,直接使用 v1.2.0。** - DSH 数据目录被写死为 `~/.dsh`:profile 或数据目录不在默认位置时,环境记录与日志会找错地方, 卸载时该清的包清不掉。 - 没有独立的看门狗进程:DSH 卸载是「先销毁插件、几秒后才删目录」, 中途重启一次 DSH,宿主里的定时器就全没了,Python 环境会永远残留。 - 缺少后续版本的产物管理能力:没有结构索引(`outline`)、不返回行数、不能批量处理目录、 没有试转入口、没有陈旧产物清理。