chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
This commit is contained in:
1 parent
c70d5d860e
commit
5ad755116e
173 files changed
+27632
No files matched your search
@@ -0,0 +1,121 @@
|
||||
# 92-官方推荐插件列表按 dsh 版本过滤(2026-09-14 落地)
|
||||
|
||||
- 日期:2026-09-14 | 状态:✅ **已上线**(插件 **0.3.16** + 平台后端已构建重启)
|
||||
- 触发:用户原话 ——「**设置选项下插件管理中的官方推荐插件列表 只能显示匹配当前dsh版本 和 超过当前版本的插件(超过的要明确标注)**」
|
||||
|
||||
> **TL;DR**
|
||||
> 官方目录(`awesome-dsh-plugin.com/plugins.json`)**不带 dsh 版本字段** ⇒ 只能**逐包去 npm 查声明**
|
||||
> (精简 packument 的 `peerDependencies / engines`,不下载 tarball),再与**平台真实版本**比。
|
||||
> 分类 `match / none / newer / older / unknown`;**默认隐藏 `older`**(只声明了更旧版本),
|
||||
> `newer`(要求更高的 dsh)**橙色显著标注**并给出「需 ≥ X,当前 Y」。
|
||||
> ⚠️ **两个"静默降级"陷阱(本轮实测踩到,都必须钉住)**:
|
||||
> ① `satisfies` **必须带 `includePrerelease: true`** —— 平台是 prerelease(`0.1.5-rc.1` / 子包 `0.1.5-rc.2`),
|
||||
> 默认语义下 `^0.1.2` 甚至 `*` 都不满足 ⇒ TOP300 会把 **`dsh-univer-office`、`dshmarket` 这些正在跑的**
|
||||
> 判成「只兼容更旧」(`match 97 / older 139` → 容忍后 `match 214 / older 22`);
|
||||
> ② **伞包 `@deepseek-ai/dsh` 必须单独解析** —— 它**不在自己的 `node_modules` 里**,
|
||||
> 而实测的 3 条 `newer` **全部只写在伞包上**(复用它 ⇒ 这 3 条永远判不出来)。
|
||||
> 结果:TOP300 里 `match 231 / none 66 / newer 2 / unknown 1`,**无 `older`**;`dsh-zotero`、`dsh-md-notes` 被标注为「需 dsh ≥ 0.1.5-rc.2」。
|
||||
|
||||
---
|
||||
|
||||
## 一、需求拆解与判据选择
|
||||
|
||||
| 问题 | 结论 | 依据 |
|
||||
|---|---|---|
|
||||
| 官方目录有没有现成的 dsh 版本字段? | **没有**。3632 条只有 `name/owner/url/page/category/description/npm/version/stars/downloads/install/added/tarball/screenshots` | 实抓 `plugins.json` 统计字段出现次数(`version` 是**插件自身**版本) |
|
||||
| 那版本要求写在哪? | 写在各 npm 包的 **`peerDependencies`(少数用 `engines.dsh`)**,且**分布在几十个 `@deepseek-ai/dsh-*` 子包上**,不是只写在 `dsh` 伞包 | 抽样 TOP60 + 随机 40:`NONE` 26 / 其余全是各种 `peer:@deepseek-ai/dsh-*` |
|
||||
| 拿什么跟它比? | **平台该包的真实版本**(`dshScopeDir()` 下各包的 `package.json`),与 `plugin-compat.ts` 判据 A 同源 | §三 的陷阱 ② |
|
||||
| 成本? | 一条 = 1 次**精简 packument**(`accept: application/vnd.npm.install-v1+json`,十几 KB)。结果按 `<npm>@<版本>` 落盘缓存 7 天 | 首访 5.6s(冷)→ 命中缓存 **143ms** |
|
||||
|
||||
**分类规则**(`src/web/plugin-dsh-compat.ts`):
|
||||
|
||||
| kind | 含义 | 展示 |
|
||||
|---|---|---|
|
||||
| `match` | 声明范围(prerelease 容忍)覆盖当前版本 | 绿色「匹配」 |
|
||||
| `none` | 没声明任何 `@deepseek-ai/*` 依赖 | 灰色「未声明」(**不等于不兼容**) |
|
||||
| `newer` | 有范围要求**比平台更高**(`minVersion(range) > 平台版本`) | **橙色「需更高版本 ≥ X」** ← 用户点名要标注的一类 |
|
||||
| `older` | 不满足且不要求更高(只允许更旧的) | **默认隐藏**,灰色「仅兼容更旧」 |
|
||||
| `unknown` | 取不到元数据 / semver 不可用 / 范围不可解析 | 灰色「未验证」,**照常显示** |
|
||||
|
||||
> 判定顺序:**先判 `newer`**(只要有一条要求更高就归 `newer`)—— 它比"只兼容更旧"更重要。
|
||||
|
||||
## 二、为什么默认过滤 + 为什么留逃生口
|
||||
|
||||
- 用户要求「**只能显示**匹配 + 超过」⇒ 默认隐藏 `older`。
|
||||
- 但**隐藏必须可见**(本库反复强调的"别静默"):说明行会写「已按 dsh {cur} 过滤:**本次候选中**隐藏 {n} 个仅兼容更旧版本的插件」,并给出勾选框「**含仅兼容更旧版本的**」(= 后端 `dshCompat=all`,只标注不过滤)。
|
||||
- `unknown` **不隐藏**:一次网络抖动 ≠ 不兼容。首访冷缓存时可能出现若干「未验证」,说明行同时给出「兼容判定仍在后台预热」提示。
|
||||
|
||||
## 三、两个"静默降级"陷阱(实测数据)
|
||||
|
||||
```
|
||||
平台:伞包 0.1.5-rc.1 / 子包 0.1.5-rc.2(240 个 @deepseek-ai/* 包)
|
||||
|
||||
TOP300 抽样:
|
||||
严格语义(默认) match 97 | none 60 | older 139 | newer 3 | unknown 1
|
||||
① prerelease 容忍 match 214 | none 60 | older 22 | newer 3 | unknown 1
|
||||
② 再把伞包单独解析后 newer 3 条全部认出(否则全是 unknown)
|
||||
```
|
||||
|
||||
- **① 的代价**:默认语义下 `older 139` 里包含 `dsh-univer-office`(我们**正在跑**)、`dshmarket`(插件市场本身)、
|
||||
`dsh-context`、`dsh-cost-meter`… ⇒ 照着默认语义过滤会把半张列表连**能用的**一起藏掉。
|
||||
根因:semver 默认**不允许 prerelease 命中不含 prerelease 的范围**(`0.1.5-rc.2 ∉ ^0.1.2`,`∉ *`)。
|
||||
- **② 的代价**:`plugin-compat.ts` 的 `platformPkgDir()` = `<dsh 包>/node_modules/@deepseek-ai/<短名>`,
|
||||
而伞包 `dsh` **不在自己的 `node_modules` 里** ⇒ 它对伞包恒返回 `null`,被当作"平台没这个包,不算不兼容"。
|
||||
于是只写在伞包上的那 3 条(`dsh-zotero` `^0.1.5-rc.2`、`dsh-any-background` `>=0.1.5-rc.2`、`dsh-md-notes` `>=0.1.5-rc.2 <0.1.6`)
|
||||
全部判不出来,而它们**正是用户要的那一类**。
|
||||
|
||||
> ⚠️ **不要顺手把 `plugin-compat.ts` 也"统一"成 prerelease 容忍**:那里用默认语义是**有意的**
|
||||
> (注释原文:「与 pnpm 的实际安装判定一致」)—— 它是上传/导入的**闸门**,语义不同是正确的。
|
||||
> 因此本功能**另起模块** `src/web/plugin-dsh-compat.ts`,**零改动** `plugin-compat.ts`。
|
||||
>
|
||||
> 📌 **顺带发现(未改,属别人 lane)**:`plugin-compat.ts` 判不到伞包 ⇒ 声明 `@deepseek-ai/dsh: ^0.1.6`
|
||||
> 的插件在**上传闸门**处会被放行。是否收紧要另立单(改它就是改既有闸门行为)。
|
||||
|
||||
## 四、改了什么
|
||||
|
||||
| 层 | 文件 | 内容 |
|
||||
|---|---|---|
|
||||
| 新模块 | `src/web/plugin-dsh-compat.ts`(199 行) | 平台版本表(**含伞包**)、semver 软载入(`minVersion` + `includePrerelease`)、`platformDeclsOf()`、`judgeDshCompat()` |
|
||||
| 路由 | `src/web/routes/whitelist.ts` | `GET /api/plugins/whitelist` 新增 `dshCompat` 参数(默认过滤,`=all` 只标注);响应新增 `dshVersion` / `hiddenByDsh` / `countAll` / `compatFiltered`,`plugins[]` 新增 `dshCompat` / `dshRequires`;判定落盘缓存 `whitelist-cache/npm-dsh-compat.json`(TTL 7 天,结构版本号 `COMPAT_VERSION`);**后台预热**(进程内一次,并发 6);请求内**限并发 12 + 6s 预算**,超预算判 `unknown` 并照常显示;判定窗口 360(> 页面上限 300,过滤后仍能填满) |
|
||||
| 前端 | `poc/business-plugins/lib/client.js`(**0.3.16**) | 表格新增「dsh 版本」列(+1 列 ⇒ `paemptyRow` 6→7);`wlCompat()` 徽章(`newer` 用 `pending` 橙色);工具条新增「含仅兼容更旧版本的」勾选;说明行给出过滤说明 + 隐藏计数 + 预热提示;`pa.pl.hintOfficial` 文案补充过滤规则 |
|
||||
| 校验 | `scripts/verify-dsh-compat.mjs`(新) | 17 条**回归锚点**:prerelease 语义、伞包解析、判定顺序、只隐藏 `older`、`hiddenByDsh`/`dshVersion` 存在、落盘缓存、预热、前端徽章/逃生口/说明行 |
|
||||
| 校验 | `scripts/verify-models-dict.mjs` | 引用键扫描**从 `ms.*` 扩到全词典**(`pa.*` 一样会漏)+ 新增 `package.json` 合法性断言(本轮真把 JSON 写坏过一次) |
|
||||
| 校验 | `package.json` | `npm run verify` 链追加上述两个脚本 |
|
||||
|
||||
## 五、验证
|
||||
|
||||
| 层 | 手段 | 结果 |
|
||||
|---|---|---|
|
||||
| L1 类型/语法 | 本机 `npm run typecheck` + `node --check` | ✅ |
|
||||
| L2 判据锚点 | `verify-dsh-compat.mjs` 17 项 + `verify-models-dict.mjs`(全词典) | ✅ 全绿 |
|
||||
| L3 算法预演 | 上服务器用**真平台版本表**跑 dry-run,对比两种语义分布 | ✅ §三 的两组数字即此产出 |
|
||||
| L4 服务器构建 | `scp` 源码(**LF**)→ `bash scripts/ci.sh` | ⚠️ typecheck ✅、build ✅;**单测 1 项失败但与本次无关**(见 §六) |
|
||||
| L5 接口真调 | 临时 session(直插 DB、用完即删)→ `GET /api/plugins/whitelist` | ✅ 13 项断言全绿:**默认响应不含 `older`**、`dshVersion=0.1.5-rc.1`、`hiddenByDsh=30`、`newer` 2 条带 `dshRequires`;`dshCompat=all` 时 `older` 23 条**出现** |
|
||||
| L6 上线实证 | 产物 sha1 比对 + 两 profile 装到 0.3.16 + 运行中实例启动时刻**晚于**包落地 | ✅ manifest 里 `wlCompat`/`pa.pl.compatCol` 均在 |
|
||||
|
||||
**铺发**:后端 = `tar`(LF)→ `/opt/dshs` → `npm run build` → `systemctl restart dshs`(重启后 portal 200);
|
||||
插件 = `npm pack` → `/opt/dsh/artifacts/business-plugins-0.3.16.tgz` → `ensure-biz-plugins.cjs --all --restart`(admin `bundles=7` / guest `bundles=6`)。
|
||||
|
||||
## 六、⚠️ 与本单无关但必须上报:`ci.sh` 里 1 项单测长期红
|
||||
|
||||
`test/db.test.mjs` 的 **「sqlite: concurrent setCredentialKey keeps exactly one enabled」** 失败:
|
||||
断言 `exactly one key enabled`,实际 `5 !== 1`。
|
||||
**根因 = 档案 87 的有意行为变更**:口径①「条目**各自开关、可同时启用**」已删掉互斥 ⇒
|
||||
该测试的期望值**过时**,不是本次改动引起(本次只碰 `whitelist.ts` / 新模块 / 插件客户端,均不进 `db.test.mjs` 的依赖图)。
|
||||
📌 **建议**:由 档案 87 的 lane 把该断言改为「允许并存的 N 把 key 均可启用」,或直接改造为按新口径断言。**本单未动**(跨 lane)。
|
||||
|
||||
## 七、回滚
|
||||
|
||||
- **插件**:删 `/opt/dsh/artifacts/business-plugins-0.3.16.tgz` → 再跑 `ensure-biz-plugins.cjs --all --restart`(会自动选回 0.3.15)。
|
||||
- **后端**:备份在 `/opt/dsh/backups/pre-92-20260914-204352/`(`src/web/routes/whitelist.ts` + `package.json`)
|
||||
⇒ `cp` 回去 + `npm run build` + `systemctl restart dshs`。
|
||||
原文件 sha256:`whitelist.ts 2fb4bf79…`、`package.json 2bc1f453…`。
|
||||
- 新增文件 `src/web/plugin-dsh-compat.ts` 与缓存目录 `whitelist-cache/npm-dsh-compat.json` 可直接删(无依赖)。
|
||||
- **无数据迁移、无 DB 变更**。
|
||||
|
||||
## 八、遗留
|
||||
|
||||
- ⚠️ 代码侧与文档库本轮仍未提交(工作树留痕)。
|
||||
- 「`older` 是否该彻底隐藏」是**产品判断**:当前是「默认隐藏 + 勾选可看」。若用户希望**永久不给看**,去掉勾选框即可。
|
||||
- 判定窗口 360 / 页面 300 / 预算 6s 都是**可调常量**(在 `whitelist.ts` 顶部成组),若日后官方目录暴涨需要重新标定。
|
||||
- 未做「按需重新判定单个插件」(现在只能等 TTL 过期或删缓存文件)。
|
||||
Reference in new issue
Block a user