dsh-db-tool 0.1.8 → 0.1.9

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.
package/README.md CHANGED
@@ -52,7 +52,7 @@ GaussDB 官方驱动未发布 npm,需先构建 vendor:`npm run build:gaussdb
52
52
 
53
53
  症状:DSH 启动后插件加载报 `failed to import`,伴随 `TypeError: Cannot read properties of null (reading 'Symbol(Symbol.iterator)')`,栈指向 `dsh-app-boot` 的 `routeScoped`。
54
54
 
55
- 根因:上游 `@deepseek-ai/dsh-app-boot` 对 `createRequire(parent).resolve.paths(name)` 直接做 `for..of`,而 Node 对 core-module 同名包(`punycode` 等)返回 `null`,hoisted profile 下凡依赖树含此类 npm 包的插件都会炸。
55
+ 根因:上游 `@deepseek-ai/dsh-app-boot` 对 `createRequire(parent).resolve.paths(name)` 直接做 `for..of`,而 Node 对 core-module 同名包(`punycode` 等)返回 `null`,hoisted profile 下凡依赖树含此类 npm 包的插件都会炸。`0.1.7-rc.2` 中共两处同型缺陷:`routeScoped()` 与 `nativePackageDir()`,本补丁一并修复(两处分别独立判定,已打过旧版单处补丁的机器只会补上缺的那处)。
56
56
 
57
57
  一键修复(幂等,应用前自动备份为 `index.js.bak-hotfix`;`--revert` 可还原):
58
58
 
@@ -0,0 +1,309 @@
1
+ # dsh 插件安装/更新问题记录
2
+
3
+ - **记录时间**:2026-09-28
4
+ - **任务范围**:按 `G:\CLI\DSH插件包.txt` 的 12 个插件清单,对 dsh web profile 执行安装/更新
5
+ - **目标 profile**:`C:\Users\Sjc\.dsh\profiles\web`
6
+ - **环境**:Windows 11 + Git Bash;Node v24.19.0;dsh 起始版本 0.1.7-rc.1(任务中途升级至 0.1.7-rc.2);pnpm 双版本共存(`G:\Env\nodejs\pnpm` 12.3.4 / `C:\Users\Sjc\AppData\Roaming\npm\pnpm` 12.6.0);内网 registry `http://10.192.2.173:8081/repository/group-npm/`
7
+
8
+ ## 一、最终结果
9
+
10
+ | 项目 | 结果 |
11
+ |---|---|
12
+ | 版本达标 | **11/11**(清单第 12 个 `dsh-zh-thinking` 确认从未发布,跳过) |
13
+ | 服务状态 | dsh web 正常运行(PID 83020,监听 127.0.0.1:3080),11 个插件全部激活 |
14
+ | 附带修复 | ① 依赖残缺态恢复;② 3 条被覆写丢失的 MCP 配置恢复 + 拆块加固;③ dsh-db-tool 加载修复 |
15
+ | 遗留 | MongoDB 连接功能不可用(需上游修复宿主缺陷);`dsh-zh-thinking` 来源待确认 |
16
+
17
+ ---
18
+
19
+ ## 二、问题清单(按严重度)
20
+
21
+ | # | 问题 | 严重度 | 状态 |
22
+ |---|---|---|---|
23
+ | 1 | dsh-db-tool 插件加载失败(宿主 loader 缺陷) | 高 | 已修复(插件侧绕过)+ 上游待修 |
24
+ | 2 | MCP 配置被 mcp-manager 覆写丢失 3 条 | 高 | 已恢复 + 已加固 |
25
+ | 3 | `ERR_PNPM_UNUSED_PATCH` 阻断所有升级操作 | 高 | 已解决 |
26
+ | 4 | pnpm 崩溃 exit 3221226505 遗留依赖残缺态 | 中 | 已恢复(崩溃本身不可复现) |
27
+ | 5 | dsh-zh-thinking 不存在于任何 registry | 中 | 待确认 |
28
+ | 6 | better-sqlite3 构建失败(nodejs.org 被拦) | 中 | 已解决 |
29
+ | 7 | skill-explorer 宿主版本门槛不满足 | 中 | 已豁免(rc.2 后自然满足) |
30
+ | 8 | 诊断探针插件被 dsh 拒绝加载 | 低 | 已知规避 |
31
+ | 9 | mcp-intellij 未拉起 | 低 | 外部依赖(IDEA 未运行) |
32
+ | 10 | Git Bash 的 http_proxy 拦截本机 curl | 低 | 已知规避 |
33
+ | 11 | 清单记载的 rtk 路径错误 | 低 | 已澄清 |
34
+ | 12 | cordis.patch.yml 注释过期 | 低 | 已标注 |
35
+ | 13 | cpu-features 原生构建失败(既有问题) | 低 | 不阻塞 |
36
+ | 14 | GitHub 直装包 specifier 为浮动引用 | 低 | 已知风险 |
37
+
38
+ ---
39
+
40
+ ## 三、问题详情
41
+
42
+ ### 问题 1:dsh-db-tool 插件加载失败【高】
43
+
44
+ **现象**
45
+
46
+ 插件安装成功、版本正确(0.1.8),但启动日志始终出现:
47
+
48
+ ```
49
+ dsh: warning: 1 entry did not activate
50
+ db-tool (dsh-db-tool): failed to import
51
+ ```
52
+
53
+ 宿主 0.1.7-rc.1 与升级后的 0.1.7-rc.2 均复现;纯 Node 环境下导入该插件完全正常。
54
+
55
+ **根因(四层因果)**
56
+
57
+ 1. **宿主缺陷**:dsh 为实现 profile 感知解析,用 `node-addon-require-builtin` 深入 Node 内部模块并改写 CJS 的 `Module._resolveFilename`。每个请求都会经过 `dsh-app-boot/lib/index.js:1422`:
58
+
59
+ ```js
60
+ for (const searchPath of createRequire(parent).resolve.paths(name)) {
61
+ ```
62
+
63
+ 该行**无空值保护**(对比同类调用 `profile.ts:596` 有 `?? []`),在特定请求上抛:
64
+
65
+ ```
66
+ TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
67
+ at ResolutionRouter.routeScoped (.../dsh-app-boot/lib/index.js:1422:58)
68
+ at ResolutionRouter.routePath (...:1554)
69
+ at Module.wrappedFilename [as _resolveFilename] (...:1771)
70
+ ```
71
+
72
+ 2. **为何是 mongodb 触发**:dsh 的 `selfReferenceName()` 只在包声明了 `exports` 字段时才短路解析。`mongodb@7.6.0` 是**无 exports 的老式布局**(132 个 js 文件),内部请求全部经过缺陷循环;而 `bson@7.3.3` 有 exports,不触发。纯 Node 下穷举 mongodb(132 文件 × 8 种子路径请求)的 `resolve.paths` 调用:**零异常**,证明包本身无问题。
73
+
74
+ 3. **为何是 db-tool 撞上**:其加载链 `dist/index.js → manager.js → guard/index.js:10 静态导入 ../adapters/mongodb/index.js → 顶层 import 'mongodb'`。即**加载插件必然加载 mongodb**——而 db-tool 的适配器本设计为惰性加载(`adapters/index.js` 中 8 个驱动全是 `() => import(...)`),是 guard 为取两个常量做的静态导入破坏了惰性。
75
+
76
+ 4. **为何看不到线索**:dsh 启动审计把加载异常吞掉,只打印 `failed to import`(源头 `app-boot` 的 `inactiveEntries`)。
77
+
78
+ **定位方法**:影子入口二分法——在同一 profile 挂 7 个探针入口(分别导入 db-tool 的各子模块),一次实验即锁定:不碰 mongodb 的 `store`/`adapters` 成功,`guard`/`manager`/`http`/`script` 全部失败。
79
+
80
+ **修复(插件侧绕过,三处最小改动)**
81
+
82
+ 1. 新增 `dist/adapters/mongodb/ops.js`:把 `DANGEROUS_OPS`、`READ_OPS` 两个纯数据集合拆出;
83
+ 2. `dist/guard/index.js` 改为从 `ops.js` 导入这两个常量;
84
+ 3. `dist/adapters/mongodb/index.js` 改为从 `ops.js` 转发导出(对外接口不变)。
85
+
86
+ **固化**:pnpm patch 官方机制 —— `patches/dsh-db-tool@0.1.8.patch`(3454 字节)+ `pnpm-workspace.yaml` 的 `patchedDependencies`,重装后自动应用。
87
+
88
+ **验证**:纯 Node 导入成功且不再加载 mongodb → 实例启动 11/11 激活 → 生产服务重启后 `GET /dsh-db-tool/api/connections` 返回 **200**(修复前 404,响应体 `{"ok":true,"data":[]}`)。
89
+
90
+ **遗留**:真正建立 MongoDB 连接时仍会加载 mongodb 包、仍会踩中同一缺陷,**MongoDB 连接功能暂不可用**(其余 6 种数据库正常)。彻底修复需上游给 1422 行加空值保护(建议反馈上游)。
91
+
92
+ ---
93
+
94
+ ### 问题 2:MCP 配置被 mcp-manager 覆写丢失【高】
95
+
96
+ **现象**
97
+
98
+ `cordis.patch.yml` 在 2026-09-28 08:51(dsh web 运行期间)被从约 82 行改写为 49 行:原本 4 条 MCP 挂载只剩 `mcp-cbmem` 一条,`mcp-intellij`(IDEA)、`mcp-laya`、`mcp-mcphub` 三条丢失;且 `mcp-cbmem` 的 `serverName` 被从 `cbmem` 改成 `cbm`。
99
+
100
+ **根因(读 `@xxxyz/dsh-mcp-manager@2.2.7/lib/index.js` 确认)**
101
+
102
+ - 该插件按"块"而非"条目"处理:`insertBlockRange()`(678-691 行)一旦在某 `- insert:` 块内匹配到目标 id,返回范围就是**从 `- insert:` 到下一个顶层 `- ` 的整块**;
103
+ - `removeEntryAll()`(713-721 行)把整块并入删除集;UI 的"编辑"(1074-1079 行)与"删除"(1160-1163 行)都会先删整块再(或不)追加;
104
+ - 因此**同块内的无标记条目会被连带删除**——4 条 MCP 同处一个 insert 块,误操作一次即全丢;
105
+ - 给手工条目补 `# dsh-mcp-manager:server:<id>` 标记也**不能免疫**(`markerRanges()` 657-677 行同样按整块计算)。
106
+
107
+ 这与历史记录中的同类事故(`G:\CLI\github-项目安装核查报告.md` 第 507-509 行)完全吻合。
108
+
109
+ **处置**
110
+
111
+ 1. 恢复:按历史原文补回丢失的 3 条(`mcp-intellij` / `mcp-laya` / `mcp-mcphub`),`dsh --profile web --dump-config` 验证 4 条全部加载;
112
+ 2. **加固**:把 4 条 MCP 拆成 **4 个独立 `- insert:` 块**,把"误删一条导致全部丢失"降级为"只丢一条";
113
+ 3. 备份:`.backup-20260928/cordis.patch.yml.before-mcp-restore`(恢复前)、`cordis.patch.yml.before-split`(拆块前)。
114
+
115
+ **遗留与建议**
116
+
117
+ - ⚠️ **不要在 dsh-mcp-manager 的 UI 里"编辑/删除"任何 MCP 条目**(新增/启用/禁用/重启是安全的);
118
+ - `mcp-cbmem` 的 serverName 仍为 `cbm`(工具前缀 `mcp__cbm__*`,原基线 `cbmem`);恢复时未改回以避免二次风险,如需修回只能手工改文件 + 重启。
119
+
120
+ ---
121
+
122
+ ### 问题 3:`ERR_PNPM_UNUSED_PATCH` 阻断升级【高】
123
+
124
+ **现象**:`pnpm add billion-context@0.1.163` 稳定失败:
125
+
126
+ ```
127
+ Error: ERR_PNPM_UNUSED_PATCH
128
+ × adding a new package
129
+ ╰─▶ The following patches were not used: billion-context@0.1.147
130
+ ```
131
+
132
+ **根因**:`pnpm-workspace.yaml` 的 `patchedDependencies` 声明了 `billion-context@0.1.147: patches/billion-context@0.1.147.patch`。一旦执行升级命令、0.1.147 退出依赖树,该 patch 立即"未被使用",pnpm 直接拒绝整个操作。**这不是崩溃、不是网络问题,而是版本的确定性阻塞**——换任何 pnpm 版本都会失败。
133
+
134
+ **处置**:为 0.1.163 重新生成 patch(新版 dist 是压缩格式,旧 patch 上下文无法落位,必须重新生成)→ 复制 patch 文件 → 修改 `pnpm-workspace.yaml` 的 `patchedDependencies` 指向 0.1.163 → **紧接**执行升级 add(切换与升级之间不插入其他 pnpm 命令,最小化不一致窗口)。
135
+
136
+ **说明**:该 patch 修复的不是安装问题,而是 billion-context 的隧道 DNS 校验:本机内网 DNS 不解析外网域名,导致其内置 deepseek 路由被 403 拒绝。补丁让 DNS 解析失败时对回环客户端放行(非回环仍拒绝,SSRF 防护不变)。
137
+
138
+ **验证**:patch 文件 sha256 与 lock 记录一致;`node_modules/billion-context/dist/index.js` 中 `ctx.clientLoopback` 出现 3 次(原 2 + 补丁 1);`catch{` 后紧跟补丁注释与 `if(ctx.clientLoopback)return{ok:true}`。
139
+
140
+ ---
141
+
142
+ ### 问题 4:pnpm 崩溃 exit 3221226505 遗留依赖残缺态【中】
143
+
144
+ **现象**:2026-09-28 08:12 一次 `pnpm add "billion-context@0.1.163"` 以 Windows 崩溃码 **3221226505(0xC0000409,fail-fast)** 退出,日志 76 字节无输出;其后 node_modules 残缺,任何 `dsh plugin` 命令都在 reconcile 阶段报 `cannot resolve profile bundle` 并异常退出。
145
+
146
+ **诊断实验**:临时目录 × {pnpm 12.3.4, 12.6.0} × {小包, billion-context} × {共享 store, 干净 store} 全矩阵 —— **全部 exit 0,崩溃不可复现**;同期生产日志(08:41)显示同一命令已变为稳定的 `exit 1`(即 ERR_PNPM_UNUSED_PATCH),说明崩溃是一次偶发的 Windows fail-fast,非确定性缺陷。
147
+
148
+ **规避**:所有 dsh/pnpm 命令统一用 PATH 前置强制走 pnpm 12.6.0:
149
+
150
+ ```bash
151
+ PATH="/c/Users/Sjc/AppData/Roaming/npm:$PATH" dsh plugin --profile web <args>
152
+ ```
153
+
154
+ (dsh 从 PATH 解析 pnpm,`operations.js:271` 有 `options.command ?? 'pnpm'`;已验证前置生效。)
155
+
156
+ **遗留**:崩溃根因未定位(无输出、不可复现),升级 pnpm 属合理规避但无直接证据证明根治。
157
+
158
+ ---
159
+
160
+ ### 问题 5:dsh-zh-thinking 不存在于任何 registry【中】
161
+
162
+ **现象**:清单记载 `dsh-zh-thinking@0.1.1` 且给出 npm 链接,但三方核实全部 404:
163
+
164
+ | 来源 | 结果 |
165
+ |---|---|
166
+ | 内网 Nexus(group-npm) | 404 |
167
+ | 公网 registry.npmjs.org | 404(`{"error":"Not found"}`) |
168
+ | npmjs 网页 | 本机 TLS 不可达(环境问题,非包问题) |
169
+
170
+ **结论**:该包从未发布或已下架/改名。**待你确认来源**。另有相似包 `@hytime/dsh-thinking-effort@0.3.5`(用途不同),如需请告知。
171
+
172
+ ---
173
+
174
+ ### 问题 6:better-sqlite3 构建失败【中】
175
+
176
+ **现象**:安装 `dsh-db-tool@0.1.8` 时其依赖 `better-sqlite3@^13.0.3` 的 `node-gyp rebuild` 失败 —— 需要从 `https://nodejs.org` 下载 node headers,本机网络返回 SSL `wrong version number`(被代理/防火墙拦截)。经测试 CCProxy(`http://10.192.2.117:8085`)**只放行 github.com,不放行 nodejs.org**(HTTP 000)。
177
+
178
+ **处置(方案 2 成功,仅 16 秒)**:使用本机已有的 node headers 强制源码编译:
179
+
180
+ ```bash
181
+ npm_config_nodedir=C:/nodedir npm_config_build_from_source=true \
182
+ PATH="/c/Users/Sjc/AppData/Roaming/npm:$PATH" dsh plugin --profile web add -E dsh-db-tool@0.1.8
183
+ ```
184
+
185
+ `C:\nodedir` 内已有完整 headers 与 node.lib;node-gyp 找到 VS2022 与 Python 3.13.15,编译通过。
186
+
187
+ **验证**:`require('better-sqlite3')` 加载成功、SQL 往返 `{"x":42}` 通过。注:该版本 binding.gyp 为 `type:'none'`(prebuildify 设计),运行时加载包内 `prebuilds/win32-x64.node`,无 build/Release 产物属正常。
188
+
189
+ ---
190
+
191
+ ### 问题 7:skill-explorer 宿主版本门槛不满足【中】
192
+
193
+ **现象**:`@linxin666/dsh-client-ui-skill-explorer@0.4.3` 声明 `dsh.engines.dsh = ">=0.1.7-rc.2"`,而当时宿主为 0.1.7-rc.1,被兼容性预检拒绝。
194
+
195
+ **处置**:按 dsh 官方机制授予精确版本豁免:
196
+
197
+ ```bash
198
+ dsh plugin --profile web allow-version '@linxin666/dsh-client-ui-skill-explorer@0.4.3' \
199
+ --dsh-version 0.1.7-rc.1 --accept-risk
200
+ ```
201
+
202
+ 记录持久化在 `compatibility.json`:`{"@linxin666/dsh-client-ui-skill-explorer@0.4.3": ["0.1.7-rc.1"]}`。功能实测正常。
203
+
204
+ **后续**:宿主升级到 rc.2 后门槛自然满足、**不再需要豁免**;旧豁免记录保留无副作用(未撤销以避免无谓变更)。
205
+
206
+ ---
207
+
208
+ ### 问题 8:诊断探针插件被 dsh 拒绝加载【低】
209
+
210
+ **现象**:凡是在插件顶层或 `apply()` 内**包装 `Module._resolveFilename` 或 `Module.prototype.require`** 的探针插件,自身都会 `failed to import`;甚至顶层 `import Module from 'node:module'` 也可能触发。普通探针(只做动态 import、读文件)正常。
211
+
212
+ **解释**:dsh 对模块加载链有自我保护/一致性校验,包装会破坏其内部假设。
213
+
214
+ **规避**:诊断时改用"影子入口二分法"(只做静态导入、不包装加载器)或"层外探针 + 延迟执行"。
215
+
216
+ ---
217
+
218
+ ### 问题 9:mcp-intellij 未拉起【低】
219
+
220
+ **现象**:服务启动期 IDEA MCP 反复 11 轮 `io.ktor.client.plugins.sse.SSEClientException: Connection refused`,之后放弃;`curl 127.0.0.1:64342` 返回 000。
221
+
222
+ **原因**:IDE 未运行(其 MCP Server 未监听 64342),非配置问题(stdio 拉起命令被反复执行、Java 进程反复启停可证)。**启动 IDEA 后即可用。**
223
+
224
+ ---
225
+
226
+ ### 问题 10:Git Bash 的 http_proxy 拦截本机 curl【低】
227
+
228
+ **现象**:在本机探测 `http://127.0.0.1:3080` 等地址时,Git Bash 环境的小写 `http_proxy=http://10.192.2.117:8085` 会拦截请求,返回统一 403,导致误判"端口无监听"。
229
+
230
+ **规避**:本机探测一律加 `--noproxy '*'`:
231
+
232
+ ```bash
233
+ curl -s --noproxy '*' -o /dev/null -w "%{http_code}" http://127.0.0.1:3080/
234
+ ```
235
+
236
+ ---
237
+
238
+ ### 问题 11:清单记载的 rtk 路径错误【低】
239
+
240
+ 清单写 `~/.local/bin/rtk.exe`,实际路径为 `G:\CLI\Rtk\rtk.exe`(PATH 可用)。`dsh-pwsh-rtk-rewrite` 按 `RTK_BIN || 'rtk'` 解析,**功能不受影响**,仅文档偏差。
241
+
242
+ ---
243
+
244
+ ### 问题 12:cordis.patch.yml 注释过期【低】
245
+
246
+ 文件第 30-37 行的 billion-context 注释仍称补丁文件为 `billion-context@0.1.147.patch`,而实际引用的已是 0.1.163。旧 patch 文件已保留(未删除)但不再被引用。建议后续手工更新注释。
247
+
248
+ ---
249
+
250
+ ### 问题 13:cpu-features 原生构建失败【低】
251
+
252
+ `dsh-pwsh-rtk-rewrite` 与 `dsh-db-tool` 两次安装中均出现 `cpu-features`(`ssh2` 的可选依赖)原生构建失败(同问题 6 的 nodejs.org 拦截),但 pnpm 容忍、主操作成功。**既有问题,非本次引入,不阻塞。**
253
+
254
+ ---
255
+
256
+ ### 问题 14:GitHub 直装包 specifier 为浮动引用【低】
257
+
258
+ `dsh-ssh-tunnel` 的依赖 specifier 为 `github:thirsty5034/dsh-ssh-tunnel#main`(跟随 main)。当前 lockfile 已锁死 commit `c88da56b...`(与远端 main 一致),但**非 frozen 安装可能漂移**。`dsh-restart-fab` 则锁定 commit `99cd34aa...`(与远端最新一致,无漂移风险)。
259
+
260
+ ---
261
+
262
+ ## 四、避坑与操作建议
263
+
264
+ 1. **改插件版本前,先对齐 patch 声明**:`pnpm-workspace.yaml` 的 `patchedDependencies` 必须与实际依赖树匹配,否则一律 `ERR_PNPM_UNUSED_PATCH`。
265
+ 2. **不要用 dsh-mcp-manager 的 UI 编辑/删除 MCP 条目**(会把整块删掉);手工编辑 `cordis.patch.yml` 更安全(现已拆成 4 块进一步降低影响面)。
266
+ 3. **安装/升级前先停 dsh web**(Windows 文件占用会导致 EPERM/EBUSY);升级后必须重启服务才生效。
267
+ 4. **统一用 pnpm 12.6.0**:`PATH="/c/Users/Sjc/AppData/Roaming/npm:$PATH" dsh plugin ...`(规避 12.3.4 的偶发 fail-fast)。
268
+ 5. **构建原生模块时用本地 headers**:`npm_config_nodedir=C:/nodedir npm_config_build_from_source=true`(本机到 nodejs.org 被拦)。
269
+ 6. **本机 HTTP 探测加 `--noproxy '*'`**(Git Bash 的 http_proxy 会拦截回环请求)。
270
+ 7. **改配置前先备份**:本次全程滚动快照在 `.dsh\profiles\web\.backup-20260928\`。
271
+ 8. 兼容性被拒时用官方豁免(`dsh plugin --profile web allow-version <pkg>@<ver> --dsh-version <ver> --accept-risk`),并记录豁免清单。
272
+
273
+ ---
274
+
275
+ ## 五、关键路径索引
276
+
277
+ | 用途 | 路径 |
278
+ |---|---|
279
+ | 改造前基线快照 | `C:\Users\Sjc\.dsh\profiles\web\.backup-20260928\snapshot-0\` |
280
+ | 最终快照(含修复) | `…\.backup-20260928\snapshot-final2\` |
281
+ | db-tool 修复说明 | `…\.backup-20260928\db-tool-修复说明.md` |
282
+ | MCP 恢复前备份 | `…\.backup-20260928\cordis.patch.yml.before-mcp-restore` |
283
+ | MCP 拆块前备份 | `…\.backup-20260928\cordis.patch.yml.before-split` |
284
+ | billion-context patch | `…\patches\billion-context@0.1.163.patch` |
285
+ | db-tool patch | `…\patches\dsh-db-tool@0.1.8.patch` |
286
+ | 宿主缺陷代码位置 | `…\@deepseek-ai\dsh\node_modules\@deepseek-ai\dsh-app-boot\lib\index.js:1422` |
287
+
288
+ **回滚方式**:用目标快照覆盖 `package.json` / `pnpm-workspace.yaml` / `pnpm-lock.yaml` / `cordis.patch.yml`(+ `patches/`),再执行 `dsh plugin --profile web install`。
289
+
290
+ ---
291
+
292
+ ## 六、待办与上游反馈建议
293
+
294
+ > 解决方案详见 [dsh插件问题解决方案-20260928.md](./dsh插件问题解决方案-20260928.md)。
295
+
296
+ **待你决策**
297
+
298
+ 1. `dsh-zh-thinking` 的来源(清单给了 npm 链接但三方 404);
299
+ 2. MongoDB 连接功能是否接受"暂不可用"(等上游修复),或要求我尝试给宿主 app-boot 打补丁(改动全局 npm 包,dsh 升级会被覆盖)。
300
+
301
+ **建议反馈上游(deepseek-harness)**
302
+
303
+ 1. `dsh-app-boot` 的 `routeScoped` 中 `createRequire(parent).resolve.paths(name)` 缺空值保护(对比 `profile.ts:596` 有 `?? []`),建议统一加固;同一模式在 rc.2 中仍有 5 处无保护;
304
+ 2. `selfReferenceName()` 对无 `exports` 字段的老式包会走完整路由路径,建议增加降级处理;
305
+ 3. 启动审计对插件加载异常只输出 `failed to import`,建议附带原始错误与堆栈(可加调试开关)。
306
+
307
+ **建议反馈 `@xxxyz/dsh-mcp-manager`**
308
+
309
+ `insertBlockRange` / `removeEntryAll` 的"整块删除"语义会把同块内非目标条目一并删除,建议改为按条目粒度定位(配合缩进解析),或在文档中明确警示。
@@ -0,0 +1,166 @@
1
+ # dsh 插件问题解决方案
2
+
3
+ - **日期**:2026-09-28
4
+ - **目标 profile**:`C:\Users\ASUS\.dsh\profiles\web`(原记录文档中的 `C:\Users\Sjc\...` 为当时的记录口径,本机实际用户目录为 `C:\Users\ASUS`)
5
+ - **原问题记录**:[dsh插件安装问题记录-20260928.md](./dsh插件安装问题记录-20260928.md)(问题现象、完整排查过程与全部 14 项问题清单见该文档)
6
+ - **环境**:Windows 11 + Git Bash;Node v24.19.0;dsh 0.1.7-rc.2;pnpm 12.6.0;内网 registry `http://10.192.2.173:8081/repository/group-npm/`
7
+
8
+ ---
9
+
10
+ ## 一、问题 1 根因官方定论(dsh-db-tool 加载失败)
11
+
12
+ ### 官方语义依据
13
+
14
+ Node.js 官方文档([Modules: CommonJS modules — require.resolve.paths(request)](https://nodejs.org/api/modules.html))明确规定:
15
+
16
+ > `require.resolve.paths(request)`
17
+ > Returns:
18
+ > - `<string[]> | null`
19
+ >
20
+ > Returns an array containing the paths searched during resolution of `request` or **`null` if the `request` string references a core module, for example `fs` or `node:fs`**, or **if the request is a relative or absolute path identifier**(即以 `/`、`./`、`../` 开头的请求).
21
+
22
+ 即:**当 request 引用 core module(`fs`、`node:fs` 等)或以 `/`、`./`、`../` 开头的路径时,`resolve.paths` 必然返回 `null`**——这不是边界情况,而是文档写明的正常返回值。
23
+
24
+ ### 缺陷位置
25
+
26
+ 原记录所指缺陷形态(无空值保护的 `resolve.paths` 迭代)在 `@deepseek-ai/dsh-app-boot/lib/index.js` 中有多处。**0.1.7-rc.2 实测现状**:
27
+
28
+ ```js
29
+ // 原始缺陷形态(无保护,for..of 对 null 迭代即抛 TypeError):
30
+ for (const searchPath of createRequire(parent).resolve.paths(name)) {
31
+ ```
32
+
33
+ - `L1422-1429`(`routeScoped` 内):**已于 2026-09-25 被打入一份 local hotfix**(fallback 到 `_nodeModulePaths`),语义上已挡住该路径的崩溃,原堆栈中的 `1422:58` 即修复前的这里;
34
+ - **`L1247`(`nativePackageDir`):与上述缺陷形态逐字相同、无保护,即本次修复对象**;
35
+ - 另有 `L1482`、`L2026`、`L3270` 三处同类无保护调用(属同类隐患,归上游 issue 一并加固,本次不动)。
36
+
37
+ 该形态**无空值保护**(对比同项目 `profile.ts:596` 的同类调用带 `?? []` 兜底)。凡 core module 或相对/绝对路径请求流经此处,对 `null` 做 `for...of` 迭代即抛出。
38
+
39
+ ### 原错误堆栈(摘自原记录文档)
40
+
41
+ ```
42
+ TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
43
+ at ResolutionRouter.routeScoped (.../dsh-app-boot/lib/index.js:1422:58)
44
+ at ResolutionRouter.routePath (...:1554)
45
+ at Module.wrappedFilename [as _resolveFilename] (...:1771)
46
+ ```
47
+
48
+ ### 触发链(四层因果摘要)
49
+
50
+ 1. dsh 为实现 profile 感知解析,改写 CJS 的 `Module._resolveFilename`,**每个模块请求**都流经被改写解析路径上的 `resolve.paths` 调用(含本次缺陷行 `L1247`);
51
+ 2. `mongodb@7.6.0` 为**无 `exports` 字段的老式布局**(132 个 js 文件),`selfReferenceName()` 不短路,内部请求全部进入缺陷路径;请求 core module(如 `fs`)时 `resolve.paths` 返回 `null` → 崩溃(`bson@7.3.3` 有 exports,不触发);
52
+ 3. db-tool 的加载链 `dist/guard/index.js:10` 对 mongodb 适配器做了静态导入,**加载插件必然加载 mongodb**;
53
+ 4. dsh 启动审计吞掉原始异常,只打印 `failed to import`,无堆栈线索。
54
+
55
+ ---
56
+
57
+ ## 二、三层处置
58
+
59
+ ### ① 宿主最小修复(host-fixer 已执行完成,2026-09-28)
60
+
61
+ - 位置:`@deepseek-ai/dsh-app-boot/lib/index.js:1247`(`nativePackageDir`)
62
+ - 改法:`createRequire(parent).resolve.paths(name)` 后补 `?? []`(与同项目 `profile.ts:596` 的既有模式一致),一行最小改动,语义与 Node 官方文档的 `null` 返回约定对齐。
63
+ - **与既有 hotfix 的关系**:`L1422-1429` 已存在另一份 local hotfix(fallback `_nodeModulePaths`,2026-09-25 打入),本次未触碰,两者共存。
64
+ - **patch 留档**:`C:\Users\ASUS\.dsh\profiles\web\.backup-20260928\dsh-app-boot@0.1.7-rc.2-resolve-paths-fix.patch`(含修改前后对照与重放说明)。
65
+ - **重放说明**:该文件位于全局 npm 包 `@deepseek-ai/dsh` 内,**dsh 全局升级会覆盖此修改**。每次升级 dsh 后需重放 patch(含 2026-09-25 的 L1422 那份),直至上游根治版本发布。
66
+ - **产品化更新(2026-09-28)**:上述两处宿主修复已并入插件分发流程——其他电脑装好插件后运行 `npm run patch:dsh`(macOS/Linux 用 `patch:dsh:sh`)即可一键修复两处(`patches/dsh-app-boot-route-scoped-hotfix.patch` 含双 hunk;幂等,已打过旧版单处补丁的机器只补缺的那处;git apply 与内建替换双路径产出逐字节一致)。详见 README「Troubleshooting」。
67
+
68
+ ### ② 插件侧 patch:保持不变
69
+
70
+ - `patches/dsh-db-tool@0.1.8.patch`(3454 字节):将 `DANGEROUS_OPS` / `READ_OPS` 两个纯数据集合拆入 `dist/adapters/mongodb/ops.js`,使 `guard/index.js` 的静态导入不再牵连 mongodb → 插件激活不再踩中宿主缺陷。
71
+ - 该 patch 解决的是"插件能否激活";宿主缺陷未根治前,**实际建立 MongoDB 连接仍会加载 mongodb 并触发同一缺陷**。宿主修复(①)落地后两者配合,MongoDB 功能才能完整恢复。
72
+ - 升级 dsh-db-tool 版本时按官方流程重新生成 patch(见下文第四节)。
73
+
74
+ ### ③ 上游根治(issue 草稿另由专人撰写)
75
+
76
+ - 反馈目标:[deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) 的 `packages/boot/app-boot`。
77
+ - 核心诉求:给 `routeScoped` 中的 `resolve.paths` 调用补空值保护;同一无保护模式在 rc.2 中仍有多处(原记录文档第六节列有 5 处)。
78
+ - 配套建议:`selfReferenceName()` 对无 `exports` 的老式包增加降级处理;启动审计附带原始错误与堆栈。
79
+
80
+ ---
81
+
82
+ ## 三、修复验证标准
83
+
84
+ 宿主修复后依次满足以下三条,问题 1 方可判定关闭:
85
+
86
+ 1. **插件全激活**:dsh web 重启后 **11/11 插件激活**,启动日志无 `failed to import`;
87
+ 2. **API 可达**:`GET /dsh-db-tool/api/connections` 返回 **200**;
88
+ 3. **真实连接**:实际建立一次 MongoDB 测试连接**成功**(此前插件侧 patch 已达成 1、2 两条,但第 3 条一直失败——此项是宿主修复生效的直接判据)。
89
+
90
+ ---
91
+
92
+ ## 四、pnpm patch 官方升级流程
93
+
94
+ 引用:[pnpm 官方文档 — pnpm patch](https://pnpm.io/cli/patch)。当被 patch 的包需要升级版本时,按以下顺序操作(以 billion-context 从 0.1.147 → 0.1.163、dsh-db-tool 升级为例):
95
+
96
+ 1. `pnpm patch <pkg>@<新版本>` —— 按新版本重新生成可编辑副本,在新副本上重做补丁修改(新版 dist 若为压缩格式,旧 patch 上下文无法落位,**必须重新生成**);
97
+ 2. 用 `--patch-dir` 输出的目录内容生成/复制新 patch 文件到 `patches/`;
98
+ 3. 修改 `pnpm-workspace.yaml` 的 `patchedDependencies`,将声明指向新版本;
99
+ 4. **紧接**执行 `pnpm add <pkg>@<新版本>` —— 切换 patch 声明与升级之间**不插入任何其他 pnpm 命令**,否则旧版本退出依赖树的瞬间 patch 声明失配,触发 `ERR_PNPM_UNUSED_PATCH` 直接拒绝整个操作。
100
+
101
+ > 本机实证见原记录文档问题 3:0.1.147 声明未对齐时,`pnpm add billion-context@0.1.163` 稳定失败;按上述流程操作后通过。
102
+
103
+ ---
104
+
105
+ ## 五、原生模块本地 headers 构建(node-gyp nodedir)
106
+
107
+ **适用场景**:本机到 `https://nodejs.org` 被网络策略拦截(SSL `wrong version number`),`better-sqlite3`、`cpu-features` 等 node-gyp 原生构建下载 headers 失败时。
108
+
109
+ 依据 node-gyp 官方参数([npmjs — node-gyp](https://www.npmjs.com/package/node-gyp)):`--nodedir` 指定本地 node headers 目录;经 npm 配置传递即 `npm_config_nodedir`。本机验证可行的完整命令(沿用原记录文档问题 6 的成功方案,实测 16 秒完成):
110
+
111
+ ```bash
112
+ npm_config_nodedir=C:/nodedir npm_config_build_from_source=true \
113
+ PATH="/c/Users/ASUS/AppData/Roaming/npm:$PATH" dsh plugin --profile web add -E <包名>@<版本>
114
+ ```
115
+
116
+ - `C:\nodedir` 内需有完整 headers 与 `node.lib`;
117
+ - `npm_config_build_from_source=true` 强制源码编译(跳过 prebuild 下载);
118
+ - PATH 前置用于强制 pnpm 12.6.0(规避 12.3.4 的偶发 fail-fast,见原记录文档问题 4)。
119
+
120
+ ---
121
+
122
+ ## 六、dsh-zh-thinking 结论与替代品
123
+
124
+ **核实结论**:`dsh-zh-thinking@0.1.1` 经三方核查(内网 Nexus group-npm、公网 registry.npmjs.org、npmjs 网页)**全部 404**——该包从未发布或已下架,任何安装方式均不可行。
125
+
126
+ **功能相近替代品**(均已核实存在于 npm):
127
+
128
+ | 包名 | 版本 | 链接 |
129
+ |---|---|---|
130
+ | `dsh-thinking-levels` | 3.4.3 | https://www.npmjs.com/package/dsh-thinking-levels |
131
+ | `@hytime/dsh-thinking-effort` | 0.3.5 | https://www.npmjs.com/package/@hytime/dsh-thinking-effort |
132
+
133
+ **待用户决策项 ①**:`dsh-zh-thinking` 从插件清单中**移除**,还是**改用**上述替代品之一?
134
+
135
+ **待用户决策项 ②**:`mcp-cbmem` 的 `serverName` 当前为 `cbm`(工具前缀 `mcp__cbm__*`),与原基线 `cbmem` 不一致。是否改回 `cbmem`?如需修回,只能手工编辑 `cordis.patch.yml` + 重启(且不可使用 dsh-mcp-manager UI 编辑,见原记录文档问题 2 的整块删除风险)。
136
+
137
+ ---
138
+
139
+ ## 七、context7/mcphub MCP 待修复项
140
+
141
+ - **现象**:`mcp-context7` 与 `mcp-mcphub` 两个 MCP server 当前 `tools: 0`——工具未同步、资源接口拒绝。
142
+ - **修复方式**:通过 MCP 管理器对这两个 server 各执行一次**重启**,即可重新同步工具列表。
143
+ - **兜底路径**(重启无效时):直接使用 context7.com 公开 llms.txt API,已验证可用,形如:
144
+
145
+ ```
146
+ https://context7.com/<owner>/<repo>/llms.txt?topic=<topic>&tokens=<n>
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 参考链接
152
+
153
+ | 资料 | 链接 |
154
+ |---|---|
155
+ | Node.js 官方文档(`resolve.paths` 返回 `null` 语义) | https://nodejs.org/api/modules.html |
156
+ | pnpm 官方文档(patch 升级流程) | https://pnpm.io/cli/patch |
157
+ | node-gyp(`--nodedir` / `npm_config_nodedir`) | https://www.npmjs.com/package/node-gyp |
158
+ | deepseek-harness 上游仓库(`packages/boot/app-boot`) | https://github.com/deepseek-ai/deepseek-harness |
159
+ | dsh-thinking-levels(替代品) | https://www.npmjs.com/package/dsh-thinking-levels |
160
+ | @hytime/dsh-thinking-effort(替代品) | https://www.npmjs.com/package/@hytime/dsh-thinking-effort |
161
+
162
+ ---
163
+
164
+ > **执行状态(2026-09-28 更新)**:宿主 `L1247` 修复已完成并重启验证——dsh web 正常监听 127.0.0.1:3080;`GET /dsh-db-tool/api/connections` 返回 200(PostgreSQL 连接 c-muh7giif-gzbl 在列);`POST /dsh-db-tool/api/test-draft`(mongodb://127.0.0.1:27017/test)由 mongodb@7.6.0 驱动真实加载并返回结构化驱动错误(本机无 MongoDB 实例,`ECONNREFUSED` 为预期),不再 500/不再使插件崩溃。
165
+ >
166
+ > **因果边界说明**:主崩溃路径(原始堆栈中的 `routeScoped`,即 L1422 区域)在 2026-09-25 打入既有 hotfix 后即已挡住——hotfix 之后、本次 L1247 加固之前实测该接口已返回结构化错误;本次 L1247 修复属**同型隐患的防御性加固**(`nativePackageDir` 同处解析链上,加固前任何经它的 core-module 请求仍会崩)。原始缺陷完全未修时(两处修复之前)此操作必崩。插件激活数以 dsh web 启动日志为准(stdout 进控制台窗口,无文件日志可直接核对)。上游 issue 草稿见 `docs/上游issue草稿-20260928.md`。
package/docs/install.md CHANGED
@@ -73,5 +73,6 @@ rm -rf ../gaussdb-pg && cp -r packages/pg ../gaussdb-pg
73
73
  ## 常见问题
74
74
 
75
75
  - **侧边栏没有「数据库」tab**:确认 dsh-better-sidebar >= 0.12 已随 DSH 加载;本地开发模式确认 `--patch ./cordis.patch.yml` 生效。
76
+ - **插件导入失败(punycode / resolve.paths)**:宿主 `dsh-app-boot@0.1.7-rc.2` 已知缺陷,在插件目录运行 `npm run patch:dsh`(macOS/Linux 用 `patch:dsh:sh`)一键修复,详见 README「Troubleshooting」。
76
77
  - **API 全部报错**:host 端 webServer 须注册前缀 `/dsh-db-tool/api`(loopback only);前端与 host 前缀不一致是最常见的接错方式。
77
78
  - **连接测试通过但查询报 UNAUTHORIZED_PROJECT**:当前项目路径未被授权,在「授权」面板为该项目路径授 ro 或 rw。
@@ -0,0 +1,133 @@
1
+ # 上游 Issue 草稿(2026-09-28)
2
+
3
+ > 状态:**草稿,未提交**。仅供审阅后人工提交。
4
+ > 作者:issue-drafter(teammate)
5
+ > 环境:Windows 11 · Node v24.19.0 · dsh 0.1.7-rc.1 / rc.2 均复现
6
+ > 素材来源:dsh-db-tool 插件加载失败排查过程(详见 `docs/dsh插件安装问题记录-20260928.md`)
7
+
8
+ ---
9
+
10
+ ## 草稿一:deepseek-harness 宿主缺陷(app-boot 模块解析崩溃)
11
+
12
+ - 上游仓库:https://github.com/deepseek-ai/deepseek-harness
13
+ - 语言习惯:英文正文 + 中文备注
14
+ - 提交入口:https://github.com/deepseek-ai/deepseek-harness/issues/new
15
+
16
+ ### 建议标题
17
+
18
+ ```
19
+ [app-boot] TypeError: createRequire.resolve.paths is not iterable — legacy packages without `exports` (e.g. mongodb@7.6.0) crash Module._resolveFilename and break plugin loading
20
+ ```
21
+
22
+ ### 正文(可整段复制提交)
23
+
24
+ **Environment**
25
+
26
+ - OS: Windows 11
27
+ - Node.js: v24.19.0
28
+ - dsh: 0.1.7-rc.1 and 0.1.7-rc.2 (both affected)
29
+
30
+ **Summary**
31
+
32
+ Plugin loading fails with `failed to import` and no actionable error whenever a plugin dependency uses a legacy package layout without an `exports` field. Root cause is an unguarded call to `require.resolve.paths()` inside `ResolutionRouter.routeScoped`, which returns `null` for core-module requests per the [official Node.js docs](https://nodejs.org/api/modules.html).
33
+
34
+ **Crash location**
35
+
36
+ `@deepseek-ai/dsh-app-boot/lib/index.js` line 1422 (`ResolutionRouter.routeScoped`):
37
+
38
+ ```js
39
+ for (const searchPath of createRequire(parent).resolve.paths(name)) {
40
+ ```
41
+
42
+ `require.resolve.paths(request)` returns **null** for:
43
+
44
+ - core modules (`fs`, `node:fs`, …)
45
+ - requests beginning with `/`, `./`, or `../`
46
+
47
+ This line has no null guard, so a core-module request flowing through the rewritten `Module._resolveFilename` throws:
48
+
49
+ ```
50
+ TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
51
+ at ResolutionRouter.routeScoped (.../dsh-app-boot/lib/index.js:1422:58)
52
+ at ResolutionRouter.routePath (...:1554)
53
+ at Module.wrappedFilename [as _resolveFilename] (...:1771)
54
+ ```
55
+
56
+ Note: the sibling call in the same project (`profile.ts:596`) already guards with `?? []` — this line was missed.
57
+
58
+ **Trigger surface**
59
+
60
+ - `mongodb@7.6.0` ships a legacy layout with no `exports` field (132 js files). All of its internal requests route through the rewritten `Module._resolveFilename`; the first core-module request crashes.
61
+ - Packages that declare `exports` (e.g. `bson@7.3.3`) do **not** trigger the bug, because modern resolution bypasses this path.
62
+ - Impact: any plugin depending on `mongodb` (in our case `dsh-db-tool`) fails to load (`failed to import`), and MongoDB connectivity is completely unavailable.
63
+
64
+ **Suggested fix**
65
+
66
+ ```js
67
+ for (const searchPath of createRequire(parent).resolve.paths(name) ?? []) {
68
+ ```
69
+
70
+ We also audited the unmodified rc.2 sources and found **3 more unguarded `resolve.paths()` calls** (bundled `lib/index.js`, approximately lines 1482, 2026, 3270) — recommend hardening all of them in one pass.
71
+
72
+ **Additional observations (same area, lower severity)**
73
+
74
+ 1. `selfReferenceName()` short-circuits self-reference resolution only for packages that declare an `exports` field. Legacy packages without `exports` fall through to the full routing path; consider adding a fallback for them.
75
+ 2. The plugin startup audit only reports `failed to import` (originating from app-boot's `inactiveEntries`), swallowing the original error and stack trace. We could only localize this bug with a "shadow entry bisection" technique (7 probe entries on the same profile, each importing a submodule one by one). Please consider surfacing the original error and adding a debug flag.
76
+
77
+ > **中文摘要**:`dsh-app-boot` 第 1422 行对 `require.resolve.paths()` 的返回值无空值保护,core 模块请求直接抛 TypeError;无 `exports` 的老式包(mongodb@7.6.0)内部请求全部流经被改写的 `Module._resolveFilename`,导致依赖它的插件(dsh-db-tool)加载失败且 MongoDB 功能不可用。建议修复为 `resolve.paths(name) ?? []`,并加固同类另外 3 处无保护调用(bundled `lib/index.js` 约 L1482/L2026/L3270)。附带两个观察:`selfReferenceName()` 对无 `exports` 包缺少降级处理;插件审计日志吞掉原始错误与堆栈,建议输出原始错误并加调试开关。
78
+
79
+ ---
80
+
81
+ ## 草稿二:@xxxyz/dsh-mcp-manager 配置覆写缺陷(按块误删条目)
82
+
83
+ - 包:`@xxxyz/dsh-mcp-manager@2.2.7`
84
+ - 语言习惯:中文正文
85
+ - 提交入口:https://github.com/xxxyz/dsh-mcp-manager/issues/new
86
+
87
+ ### 建议标题
88
+
89
+ ```
90
+ 配置编辑/删除按「块」而非「条目」粒度处理,同一 `- insert:` 块内的无标记条目被连带删除
91
+ ```
92
+
93
+ ### 正文(可整段复制提交)
94
+
95
+ **环境**
96
+
97
+ - 包版本:`@xxxyz/dsh-mcp-manager@2.2.7`
98
+ - 平台:Windows 11 · Node v24.19.0 · dsh 0.1.7-rc.2
99
+
100
+ **摘要**
101
+
102
+ 插件通过 patch 文件向宿主配置追加 `- insert:` 块。经阅读 `lib/index.js` 确认根因:**所有定位与删除操作都以「块」为单位,而不是以「条目」为单位**。同一块内由用户手工添加的、未被插件标记的条目,会在一次编辑或删除操作中被连带删除。
103
+
104
+ **根因(源码定位,`lib/index.js`)**
105
+
106
+ | 位置 | 函数 | 行为 |
107
+ |---|---|---|
108
+ | 678–691 行 | `insertBlockRange()` | 匹配到目标 id 后,返回从 `- insert:` 行到下一个顶层 `- ` 行的**整块**范围 |
109
+ | 713–721 行 | `removeEntryAll()` | 将 `insertBlockRange()` 返回的**整块**并入删除集 |
110
+ | 1074–1079 行 | UI「编辑」 | 先删整块再重写 |
111
+ | 1160–1163 行 | UI「删除」 | 先删整块 |
112
+ | 657–677 行 | `markerRanges()` | 插件自身标记的范围同样按整块计算 |
113
+
114
+ **实际事故还原**
115
+
116
+ - 事故场景:4 条 MCP 配置位于同一个 `- insert:` 块内,用户在 UI 中编辑/删除其中 1 条,结果 4 条全部丢失。
117
+ - 文件证据:`cordis.patch.yml` 从 82 行被改写为 49 行,最终仅剩 1/4 条 MCP。
118
+ - 免疫尝试失败:给手工条目补上 `# dsh-mcp-manager:server:<id>` 标记注释也**不能**避免连带删除——`markerRanges()`(657–677 行)按整块计算标记范围,块内任何操作仍波及全部条目。
119
+
120
+ **建议修复**
121
+
122
+ 按「条目」粒度定位(解析条目缩进层级,定位单条 `- name:` 起始范围)替代整块删除;若短期内不便重构,至少在文档与 UI 中明确警示「同一 `- insert:` 块内的多条配置会被一起删除」。
123
+
124
+ > **中文摘要**:`insertBlockRange()` / `removeEntryAll()` / UI 编辑与删除均按整块处理,同块内无标记(或甚至有标记,`markerRanges()` 同样按块计算)的条目被连带删除,曾导致 4 条 MCP 配置一次操作全丢(cordis.patch.yml 82 行 → 49 行)。建议改为条目级定位删除,或在文档/UI 明确警示。
125
+
126
+ ---
127
+
128
+ ## 附:提交核对清单(提交前人工确认)
129
+
130
+ - [ ] 草稿一:确认 deepseek-harness 仓库 issue 模板要求(若有模板字段需补填)
131
+ - [ ] 草稿一:复现版本号与堆栈行号以最终提交时的 rc 版本为准
132
+ - [ ] 草稿二:确认 @xxxyz/dsh-mcp-manager 的 issue 入口(仓库地址按 npm 包实际 homepage 核对)
133
+ - [ ] 两份草稿均为独立 issue,不互相引用提交(可在正文 environment 处交叉提及)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-db-tool",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "DSH community plugin: chat-operated multi-database admin tool with sidebar management, project-scoped grants, ro/rw modes and dangerous-operation confirmation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,5 +1,7 @@
1
- # DSH ResolutionRouter routeScoped hotfix (upstream bug: createRequire(parent).resolve.paths(name)
1
+ # DSH dsh-app-boot resolve.paths hotfix (upstream bug: createRequire(parent).resolve.paths(name)
2
2
  # returns null for core-module names like "punycode", breaking hoisted profile installs).
3
+ # Covers BOTH unguarded for..of sites in 0.1.7-rc.2: routeScoped() (@@ -1419) and
4
+ # nativePackageDir() (@@ -1244).
3
5
  # Target-Version: 0.1.7-rc.2
4
6
  # Apply manually: git apply -p1 -d <dsh-app-boot package root> <this file>
5
7
  # Or one command: npm run patch:dsh (scripts/apply-dsh-hotfix.ps1)
@@ -24,3 +26,15 @@ index 8f6041d..89a138e 100644
24
26
  if (!searchPath.startsWith(layer.localPrefix)) break;
25
27
  localSearchPaths.push(searchPath);
26
28
  const candidate = localPackageCandidate(searchPath, name, flavor);
29
+ @@ -1244,7 +1244,10 @@
30
+ return treePrefix + (end < 0 ? rest : rest.slice(0, end));
31
+ }
32
+ function nativePackageDir(parent, name) {
33
+ - for (const searchPath of createRequire(parent).resolve.paths(name)) {
34
+ + /* local hotfix: nativePackageDir: resolve.paths returns null for core-module names
35
+ + (fs, node:fs, ...); an empty list means no local candidate, matching the
36
+ + guard in profile.ts:596. */
37
+ + for (const searchPath of createRequire(parent).resolve.paths(name) ?? []) {
38
+ const candidate = join(searchPath, name);
39
+ if (existsSync(join(candidate, "package.json"))) return candidate;
40
+ }
@@ -6,8 +6,13 @@ export declare const HOTFIX_MARKER: string;
6
6
  export declare const BOOT_INDEX_REL: string;
7
7
  export declare const BACKUP_SUFFIX: string;
8
8
  export declare const ORIGINAL_LINE: string;
9
+ export declare const ORIGINAL_NPD_BLOCK: string;
10
+ export declare const NPD_FIXED_LINE: string;
11
+ export declare const FIXED_NPD_BLOCK: string;
12
+ export declare const HOTFIX_BLOCK: string;
9
13
 
10
14
  export declare function isApplied(content: string): boolean;
15
+ export declare function isNpdApplied(content: string): boolean;
11
16
  export declare function applyToContent(content: string): {
12
17
  ok: boolean;
13
18
  reason?: string;
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * DSH ResolutionRouter hotfix —— 核心逻辑(纯 Node、零依赖、可被测试 import)。
3
+ * DSH dsh-app-boot resolve.paths hotfix —— 核心逻辑(纯 Node、零依赖、可被测试 import)。
4
4
  *
5
- * 修复的 bug:@deepseek-ai/dsh-app-boot 的 routeScoped() 直接对
6
- * `createRequire(parent).resolve.paths(name)` 做 for..of,而 Node 对 core-module
7
- * 同名包(punycode 等)返回 null,导致 hoisted profile 下 npm 安装插件导入失败
8
- * (TypeError: Cannot read properties of null)。
5
+ * 修复的 bug:@deepseek-ai/dsh-app-boot@0.1.7-rc.2 对
6
+ * `createRequire(parent).resolve.paths(name)` 的 for..of 无保护,而 Node 对
7
+ * core-module 同名包(punycode 等)返回 null,for..of null 直接抛 TypeError,
8
+ * 导致 hoisted profile 下 npm 安装插件导入失败。rc.2 中共两处同型缺陷:
9
+ * 1. routeScoped()(L1419 区域)—— 症状直接暴露;
10
+ * 2. nativePackageDir()(L1246 区域)—— 第二处,症状相同,一并修复。
9
11
  *
10
12
  * 用法:node scripts/hotfix-core.mjs [--dsh-root <dir>] [--patch-file <file>]
11
13
  * [--force] [--revert] [--dry-run]
@@ -29,8 +31,27 @@ export const BACKUP_SUFFIX = '.bak-hotfix';
29
31
  export const ORIGINAL_LINE =
30
32
  '\t\tfor (const searchPath of createRequire(parent).resolve.paths(name)) {';
31
33
 
34
+ /** 原始(未打补丁)nativePackageDir 块 —— rc.2 中第二处同型缺陷(函数签名行做锚,保证匹配唯一)。 */
35
+ export const ORIGINAL_NPD_BLOCK = [
36
+ 'function nativePackageDir(parent, name) {',
37
+ '\tfor (const searchPath of createRequire(parent).resolve.paths(name)) {',
38
+ ].join('\n');
39
+
40
+ /** nativePackageDir 修复后的 for 行(同时是幂等判据:`?? []` 形态全文件唯一)。 */
41
+ export const NPD_FIXED_LINE =
42
+ '\tfor (const searchPath of createRequire(parent).resolve.paths(name) ?? []) {';
43
+
44
+ /** nativePackageDir 热修复块 —— 与 patches/*.patch 第二个 hunk 的 + 行逐字节一致。 */
45
+ export const FIXED_NPD_BLOCK = [
46
+ 'function nativePackageDir(parent, name) {',
47
+ '\t/* local hotfix: nativePackageDir: resolve.paths returns null for core-module names',
48
+ '\t (fs, node:fs, ...); an empty list means no local candidate, matching the',
49
+ '\t guard in profile.ts:596. */',
50
+ NPD_FIXED_LINE,
51
+ ].join('\n');
52
+
32
53
  /** 热修复代码块 —— 与 patches/dsh-app-boot-route-scoped-hotfix.patch 的 + 行一致。 */
33
- const HOTFIX_BLOCK = [
54
+ export const HOTFIX_BLOCK = [
34
55
  '\t\t/* local hotfix: resolve.paths returns null for core-module names (e.g. "punycode"),',
35
56
  '\t\t which made the for..of throw TypeError and broke every hoisted profile install',
36
57
  '\t\t whose dependency tree requires such an npm package. Fall back to the standard',
@@ -46,28 +67,65 @@ export function isApplied(content) {
46
67
  return content.includes(HOTFIX_MARKER);
47
68
  }
48
69
 
70
+ /** nativePackageDir 修复是否已在(幂等判据:`?? []` 形态全文件唯一)。 */
71
+ export function isNpdApplied(content) {
72
+ return content.includes(NPD_FIXED_LINE);
73
+ }
74
+
75
+ /** 单处修复的三态应用:already(已在)/ applied(本次注入)/ missing(原始行不在)。 */
76
+ function applyOne(content, isDone, original, fixed) {
77
+ if (isDone(content)) return { content, status: 'already' };
78
+ if (content.includes(original)) {
79
+ return { content: content.replace(original, fixed), status: 'applied' };
80
+ }
81
+ return { content, status: 'missing' };
82
+ }
83
+
49
84
  /**
50
- * 在文件内容上应用热修复(幂等:已应用时返回 already-applied)。
85
+ * 在文件内容上应用全部热修复(幂等)。
86
+ * 两处缺陷(routeScoped / nativePackageDir)独立判定、独立注入:
87
+ * - 任一「本次注入」→ ok:true;
88
+ * - 全部缺失原始行 → target-line-not-found(可能不是目标文件);
89
+ * - 其余(全部已在)→ already-applied。
90
+ * 已打 FIX1 的机器只会补上 FIX2,反之亦然。
51
91
  * @returns {{ ok: boolean, reason?: string, content: string }}
52
92
  */
53
93
  export function applyToContent(content) {
54
- if (isApplied(content)) return { ok: false, reason: 'already-applied', content };
55
- if (!content.includes(ORIGINAL_LINE)) {
56
- return { ok: false, reason: 'target-line-not-found', content };
94
+ const r1 = applyOne(content, isApplied, ORIGINAL_LINE, HOTFIX_BLOCK);
95
+ const r2 = applyOne(r1.content, isNpdApplied, ORIGINAL_NPD_BLOCK, FIXED_NPD_BLOCK);
96
+
97
+ if (r1.status === 'applied' || r2.status === 'applied') {
98
+ return { ok: true, content: r2.content };
99
+ }
100
+ if (r1.status === 'missing' && r2.status === 'missing') {
101
+ return { ok: false, reason: 'target-line-not-found', content: r2.content };
57
102
  }
58
- return { ok: true, content: content.replace(ORIGINAL_LINE, HOTFIX_BLOCK) };
103
+ return { ok: false, reason: 'already-applied', content: r2.content };
59
104
  }
60
105
 
61
106
  /**
62
107
  * 还原热修复(优先用内建块反替换;调用方在磁盘层面应优先用 .bak-hotfix 备份)。
108
+ * 注意:内建反替换只认分发版英文注释块;手工打过的非标准块(如中文注释)
109
+ * 反替换不到,届时请用 .bak-hotfix 备份整体还原。
63
110
  * @returns {{ ok: boolean, reason?: string, content: string }}
64
111
  */
65
112
  export function revertContent(content) {
66
- if (!isApplied(content)) return { ok: false, reason: 'not-applied', content };
67
- if (!content.includes(HOTFIX_BLOCK)) {
68
- return { ok: false, reason: 'hotfix-block-not-found', content };
113
+ let changed = false;
114
+ let result = content;
115
+
116
+ if (isApplied(result)) {
117
+ if (!result.includes(HOTFIX_BLOCK)) {
118
+ return { ok: false, reason: 'hotfix-block-not-found', content };
119
+ }
120
+ result = result.replace(HOTFIX_BLOCK, ORIGINAL_LINE);
121
+ changed = true;
122
+ }
123
+ if (result.includes(FIXED_NPD_BLOCK)) {
124
+ result = result.replace(FIXED_NPD_BLOCK, ORIGINAL_NPD_BLOCK);
125
+ changed = true;
69
126
  }
70
- return { ok: true, content: content.replace(HOTFIX_BLOCK, ORIGINAL_LINE) };
127
+ if (!changed) return { ok: false, reason: 'not-applied', content };
128
+ return { ok: true, content: result };
71
129
  }
72
130
 
73
131
  /** 从补丁文本解析注释头声明的目标版本(无则返回 undefined)。 */
@@ -195,9 +253,13 @@ function commonGlobalRoots() {
195
253
  export function applyToIndexFile(indexFile, patchFile) {
196
254
  if (isGitApplyable(indexFile, patchFile)) {
197
255
  const bootRoot = path.dirname(path.dirname(indexFile));
198
- execFileSync('git', ['apply', '-p1', '--directory', '.', patchFile], {
199
- cwd: bootRoot,
200
- });
256
+ // autocrlf=false:阻止 Windows 上 git apply 把整个文件转成 CRLF——
257
+ // 转换会破坏与内建替换路径的逐字节等价,并使内建 revert 兜底失配。
258
+ execFileSync(
259
+ 'git',
260
+ ['-c', 'core.autocrlf=false', 'apply', '-p1', '--directory', '.', patchFile],
261
+ { cwd: bootRoot },
262
+ );
201
263
  return 'git-apply';
202
264
  }
203
265
  const content = fs.readFileSync(indexFile, 'utf8');
@@ -211,9 +273,11 @@ function isGitApplyable(indexFile, patchFile) {
211
273
  const bootRoot = path.dirname(path.dirname(indexFile));
212
274
  try {
213
275
  // --directory 只接受相对路径(git 拒绝绝对目标 "invalid path"),故以包根为 cwd。
214
- execFileSync('git', ['apply', '--check', '-p1', '--directory', '.', patchFile], {
215
- cwd: bootRoot,
216
- });
276
+ execFileSync(
277
+ 'git',
278
+ ['-c', 'core.autocrlf=false', 'apply', '--check', '-p1', '--directory', '.', patchFile],
279
+ { cwd: bootRoot },
280
+ );
217
281
  return true;
218
282
  } catch {
219
283
  return false;
@@ -300,18 +364,28 @@ export function run(argv = process.argv.slice(2)) {
300
364
  return 0;
301
365
  }
302
366
 
303
- if (isApplied(content)) {
304
- console.log('[dsh-hotfix] already applied; nothing to do.');
367
+ /** 当前内容中尚未修复的缺陷名列表(dry-run 预览与结果汇报共用)。 */
368
+ function pendingFixes(c) {
369
+ const pending = [];
370
+ if (!isApplied(c)) pending.push('routeScoped');
371
+ if (!isNpdApplied(c)) pending.push('nativePackageDir');
372
+ return pending;
373
+ }
374
+
375
+ if (isApplied(content) && isNpdApplied(content)) {
376
+ console.log('[dsh-hotfix] already applied (both fixes); nothing to do.');
305
377
  return 0;
306
378
  }
307
379
  if (dryRun) {
308
- console.log(`[dsh-hotfix] dry-run: would apply patch to ${indexFile}.`);
380
+ console.log(`[dsh-hotfix] dry-run: would fix [${pendingFixes(content).join(', ')}] in ${indexFile}.`);
309
381
  return 0;
310
382
  }
311
383
  const backup = indexFile + BACKUP_SUFFIX;
312
384
  if (!fs.existsSync(backup)) fs.copyFileSync(indexFile, backup);
313
385
  const method = applyToIndexFile(indexFile, patchFile);
314
- console.log(`[dsh-hotfix] applied to ${indexFile} (via ${method}; backup at ${backup}).`);
386
+ console.log(
387
+ `[dsh-hotfix] fixed [${pendingFixes(content).join(', ')}] in ${indexFile} (via ${method}; backup at ${backup}).`,
388
+ );
315
389
  return 0;
316
390
  }
317
391