dsh-db-tool 1.2.0 → 1.3.1

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.
@@ -1,95 +0,0 @@
1
- # 代码审查发现与处置记录
2
-
3
- 三路并行审查(lib 核心 / 8 适配器 / client+tests+scripts),共 38 条 findings,全部处置完毕。
4
- 处置分类:✅已修复 / 🧪已补测试 / ⏸有意保留(附理由)。
5
-
6
- ## 一、lib 核心(store / guard / manager / http / index)
7
-
8
- | 级别 | 位置 | 问题 | 处置 |
9
- |---|---|---|---|
10
- | critical | lib/manager.ts(原 vm 沙箱 run_script) | `console.log.constructor('return process')()` 逃逸 vm | ✅ runScript 改子进程隔离:lib/script/worker.cjs(vm.runInNewContext + Node permission model)+ lib/script/runner.ts(fork、SIGKILL 强超时 60s) |
11
- | high | lib/manager.ts challenge | challenge 未绑定 connId/projectKey,可跨连接重放 | ✅ ChallengeScope{connId,projectKey},create/consume 双向校验 |
12
- | high | lib/store/connections.ts redactUrl | 正则 `[^/]*@` 密码含 `/` 时明文入库 | ✅ 重写为 authority 段 lastIndexOf('@')/lastIndexOf(':) 解析 |
13
- | high | lib/manager.ts adapterCache | 缓存键不含 mode,ro→rw 升级后仍用旧连接 | ✅ 缓存键 `connId@mode=...`;grant/revoke/update/remove 均 dropAdapters |
14
- | high | lib/guard MONGO_WRITE_OPS | 缺 deleteMany/findAndModify/mapReduce 等 | ✅ 补齐 + READ_OPS 改为 import 适配器导出白名单(单一事实来源) |
15
- | high | lib/guard query 通道 | Redis/Mongo 写命令经 query 仅 warning 放行 | ✅ classifyRedis/classifyMongo 接收通道参数,query 命中写/未识别命令一律 danger(fail-closed) |
16
- | high | lib/guard 语句分析 | `WITH ... DELETE` / `EXPLAIN DELETE` 免确认 | ✅ SQL_WRITE_BODY_RE 读头命中后补扫语句体;HIDDEN_DANGER_RE 改 `[\s\S]*` |
17
- | medium | guard FOR UPDATE | 多行语句漏检 | ✅ 同上(全语句扫描) |
18
- | medium | manager updateConnection | 更新后不清适配器缓存 | ✅ void this.dropAdapters(id) |
19
- | medium | manager runScript 超时 | 超时后后台写继续执行 | ✅ 子进程 SIGKILL + 关会话适配器 |
20
- | medium | manager runScript | 单 challenge 可确认多条语句 | ✅ 子进程化后每次 db.execute 独立走 guard(逐条 challenge) |
21
- | medium | manager 审计时序 | 审计在执行前记 ok:true | ✅ 审计后置,成功/失败如实记录 |
22
- | medium | store remove 级联 | 非原子,中途失败留脏数据 | ✅ 失败开放顺序 grants→secrets→connections |
23
- | medium | manager params | 参数未校验类型 | ✅ 非数组抛 INVALID_ARGUMENT |
24
- | medium | http parseSsl | `ssl="false"` 被 Boolean() 转为 true | ✅ 严格布尔解析,非法值 400 |
25
- | medium | store normalize | 仅小写盘符不处理其他大小写语义 | ⏸ 保留:NTFS 大小写不敏感语义即此;已补 `..` 穿越用例 🧪 |
26
- | medium | store audit tail | 全量读文件再截取 | ⏸ 保留:审计文件预期规模小(JSONL 追加),优化属 YAGNI |
27
- | **security** | guard classifySql(变异测试增补发现) | ① body 正则作用于未剥注释原文 → 注释内写词误报 danger;② SQL_DDL/DML/MAINT 锚定原文 → 前导块注释 `/* x */ DROP` 使 DDL 漏判、execute 通道仅 warning **绕过确认** | ✅ 提取 stripSqlComments,语句体分析与 DDL/DML 锚定一律基于剥注释文本(误报与绕过同修) |
28
-
29
- ## 二、8 适配器
30
-
31
- | 级别 | 位置 | 问题 | 处置 |
32
- |---|---|---|---|
33
- | high | mysql ro query | 无语句校验、无会话只读 | ✅ SET SESSION TRANSACTION READ ONLY + query 首词白名单 |
34
- | high | mongodb aggregate | 未拒 $out/$merge(可写副作用) | ✅ 双清单递归:JS_INJECTION_KEYS + WRITE_STAGE_KEYS(含 $unionWith) |
35
- | high | mongodb assertNoWhere | 缺 $function/$accumulator | ✅ 递归拦截 |
36
- | medium | mongodb DANGEROUS_OPS | 缺复数/别名操作 | ✅ 8→11(createIndexes/dropIndexes/renameCollection) |
37
- | medium | redis DANGEROUS_COMMANDS | 缺 ACL dangerous 类 | ✅ 13→19(SLAVEOF/SORT_RO/MODULE/FUNCTION/SCRIPT/RESET) |
38
- | medium | pg-like ro SET | SET 失败静默 | ✅ fail-closed release(err) |
39
- | medium | mysql defaultDb | URL pathname 未校验 | ✅ 过 assertIdent |
40
- | medium | redis SCAN | 不透传 MATCH/COUNT | ✅ 透传(clamp 200) |
41
- | medium | redis HGETALL 等 | 无截断 | ✅ 500 行 + truncated 标注 |
42
- | medium | oracle/dmdb query | autoCommit:false 事务悬挂 | ✅ execOpts 默认 autoCommit:true,tx 内显式 false 覆盖 |
43
-
44
- ## 三、client / tests / scripts
45
-
46
- | 级别 | 位置 | 问题 | 处置 |
47
- |---|---|---|---|
48
- | high | client.js 分页 | 无 offset 翻页无效 | ✅ 后端补 previewRows 第 4 参 offset 全链路(types/manager/http/tool/schema + 8 适配器),client 带 offset |
49
- | medium | client.js ConsoleView | database 输入框无效 | ✅ 移除 |
50
- | medium | client.js BrowseView | useEffect 竞态 | ✅ cancelled 标志 |
51
- | medium | gaussdb.test vendorReady | 探测路径错(旧布局) | ✅ 改 vendor/gaussdb-pg/packages/pg/package.json;顺带发现并修复 lib/adapters/gaussdb/index.ts VENDOR_PATH 三级相对路径真 bug(`../../` 只到 lib/) |
52
- | medium | build 脚本 skip 检查 | 旧布局路径导致每次重建 | ✅ sh/ps1 均改 packages/pg/package.json |
53
- | medium | audit.spec | 断言缺字段 | ✅ 补 danger/confirmed/projectPathKey/rowsAffected |
54
- | medium | normalize.spec | 缺 `..` 穿越用例 | ✅ 已补(win32+posix 双分支) |
55
- | medium | api.spec | query 通道 challenge 三步无用例 | ✅ 已补(首次确认→成功→篡改重放拒→原句重放拒,符合防探测语义) |
56
- | medium | mysql/postgresql.test | beforeAll 在 describe.skip 外 | ✅ 移入 suite() 回调 |
57
-
58
- ## 变异测试
59
-
60
- - Stryker(vitest runner,范围 lib/guard + lib/manager + lib/store):初版 vitest@5 与 Stryker 兼容性缺陷(phantom survivors,假分 2%)→ 降级 vitest@4 后正常。
61
- - 基线 57.29%(654 killed / 367 survived / 122 no-cov),高价值存活项已定向补测试(dispose 生命周期、normalize/io 边界、redactUrl 变体、grants 边界、guard 注释剥离、manager 构造默认值)。
62
-
63
- ## 第二轮:全库审查+优化(5 teammate 按模块并行)
64
-
65
- 35 项修复 + 13 项报告保留,全量 277 passed / 18 skipped,tsc 0。
66
-
67
- ### 已修复(按模块)
68
-
69
- | 模块 | 修复 |
70
- |---|---|
71
- | lib 核心(guard-tool) | ① runScript 脚本内 dbQuery 句柄补传顶层 challengeId(原仅 dbExecute 传,行为不一致);② runner.ts IPC `msg:any` → unknown+结构类型,msg.args 加 Array.isArray 守卫;③ lib/index.ts 重复 import 合并;④ TOOL_DESCRIPTION run_script 描述与子进程实现同步;⑤ **[high] Mongo execute 通道未识别 op fail-open(none)→ warning(fail-closed,与 SQL/Redis 对齐)**;⑥ MONGO_WRITE_OPS 补命令文档形式 delete/deleteMany/update/create,`{drop:...}` 归 danger(等同 dropCollection,MONGO_DANGEROUS_EXTRA);⑦ **fork env 白名单 minimalEnv()(11 项),宿主敏感环境变量不再透传脚本子进程**;+5 测试用例(tests/tool/runner-env.spec.ts 新建) |
72
- | store(store-adapters-core) | ① **[high] writeJsonAtomic 临时文件以目标 fileMode 创建**(原 0644 出生,rename 前窗口期明文机密宽权限暴露);② DSH_HOME 空串产出相对路径 → `?? (…\|\|…)`;③ 删死类型 AdapterRegistry;④ driverMissingError cause 空时文案去多余冒号;⑤ io.spec 补 .bak 现场保留断言 |
73
- | SQL 适配器(sql-adapters) | ① **mysql-offline 假绿断言实锤**:`.catch()` 吞外层断言失败且 label 拼错(「列出数据库」vs 实际「列出表」),旧白名单回归完全逃逸测试 → 改先捕获再双断言;② mysql URL decodeURIComponent 非法编码裸 URIError → 人类可读提示;③ gaussdb 驱动加载失败附原始错误(区分未构建 vs 产物损坏);④ gaussdb 条件用例 if-return 假 pass → it.skipIf;⑤ MysqlTx/测试 inline 类型统一复用共享 TxHandle |
74
- | NoSQL/企业适配器(nosql-ent-adapters) | ① oracle resolveOracleConn url/fields 两分支 user 空串校验;② oracle/dmdb testConnect 在 v$version/V$VERSION 不可读(ORA-00942 受限环境)时兜底 `SELECT 1 FROM dual` 判活,不再误报连接失败;③ oracle 声明 TDZ 隐式依赖消除(上移到使用点前);④ dmdb DmPoolLike 删未使用的 execute 成员(与「Pool 无 execute」注释矛盾);⑤ redis buildUrl user 加 encodeURIComponent 且空串跳过(含 @/: 用户名坏 URL);⑥ redis OBJECT/MEMORY 缺 key 参数拦截;⑦ mongodb insertOne 删 cmd.insert 误导性别名;+4 测试用例 |
75
- | client/skill/docs(client-skill) | ① openTable 竞态:openSeq 序号守卫,旧翻页/切表响应晚到不再覆盖新数据;② auditTable 先截断 80 字符致 title 也是截断版 → 全文进 td+ellipsis,title 显示全文;③ CSS .dbt-tree* 双份定义去重、mono 字体 3 处硬编码统一 var(--dbt-mono);④ i18n ok 文案「确定」→「操作完成」(语义修正,key 冻结);⑤ SKILL.md run_script 段按子进程+permission model 重写、challengeId 示例改真实格式 c_[0-9a-f]{24}、补 {text,isError} 返回契约说明 |
76
-
77
- ### 报告保留(附理由)
78
-
79
- | 级别 | 项 | 理由 |
80
- |---|---|---|
81
- | medium | pg/gaussdb ro query 无应用层写白名单(仅服务器级只读会话) | CTE(WITH...INSERT)使首词白名单在 PG 语义下不可行;服务层 guard query 通道已拦写语句(SQL_WRITE_BODY_RE 覆盖 CTE 写),双防线已足够 |
82
- | medium | mysql ro 会话 SET 失败静默 catch | mysql2 无 release(err) 语义;destroy 有重连风暴风险,权衡后保留 |
83
- | medium | connections update url↔fields 互切残留旧机密 | 与客户端「留空=保留旧密码」语义自洽,url 优先约定由适配器工厂执行;将来可文档固化 |
84
- | medium | connections create 两阶段写无回滚(孤儿 secrets) | 失败开放方向正确(残留机密好过残留授权);将来可加启动期清扫 |
85
- | low | guard 字符串字面量内写词误报(SELECT 'delete me') | 方向安全(误报优于漏报),精确化需字符串感知剥离,成本大于收益 |
86
- | low | ro 连接 query+challenge 确认后由适配器只读兜底拒绝 | 语义无洞(ro 写不可能成功),仅体验 |
87
- | low | /api/grants PUT 不校验 connId 存在性 | 授权到不存在连接无实害 |
88
- | low | oracle/dmdb toExecResult 双份同构实现 | 合并需跨模块共享工具层,架构决策,收益低 |
89
- | low | mongodb aggregate/distinct truncated 用 rows.length>=500 近似 | 恰 500 行误标,精确判定需额外查询,代价大于收益 |
90
- | low | dm 免密(trust 登录)场景 user 空串未强制校验 | 保留免密可能性,不武断加校验 |
91
- | low | 事务悬挂时适配器 close 挂起 | 服务层 tx 已 finally rollback 覆盖正常路径;跨适配器行为变更留待需要时 |
92
- | low | ManageView testConn/loadAudit 无 busy 保护 | 幂等读,testInfo 按 id 分 key 不互踩 |
93
- | low | 翻页按钮 disabled 在 truncated undefined 时可点 | 可能翻到空页,无数据损害;后端已恒返回 truncated 后可收紧 |
94
-
95
- > 注:文档早前「DANGEROUS_OPS 8 项」实为 11 项(createIndexes/dropIndexes/renameCollection),以代码为准。
package/docs/skill.md DELETED
@@ -1,48 +0,0 @@
1
- # dsh-db-tool Skill:db-admin
2
-
3
- 本插件随附一个 DSH skill:`skills/db-admin/SKILL.md`。安装插件后,模型在涉及数据库操作的任务中会自动按该 skill 的指引调用 **DatabaseManager** 工具;用户也可显式唤起(如"帮我看看 xxx 库里有哪些表")。
4
-
5
- ## 定位与分工
6
-
7
- | 入口 | 面向 | 能力 |
8
- |---|---|---|
9
- | DatabaseManager 工具(skill 驱动) | 模型 | 6 个 action:查询、执行、浏览、预览等,详见下文 |
10
- | 侧边栏「数据库」tab(client/) | 用户 | 连接 CRUD、项目授权(ro/rw)、浏览预览、SQL 控制台、审计 |
11
-
12
- 授权关系只在侧边栏维护:**项目路径 → 连接 → ro/rw**。工具侧只消费授权结果,不能自我提权。
13
-
14
- ## 六个 action 速览
15
-
16
- 完整用法、方言差异与示例见 `skills/db-admin/SKILL.md`(模型实际读取的就是它),此处仅列签名:
17
-
18
- ```
19
- list_connections # 当前项目已授权的连接
20
- query { conn_id, sql, params? } # 只读查询
21
- execute { conn_id, statement, params? } # 单条写语句(rw;DDL/DML 触发确认)
22
- schema { conn_id, database?, table? } # 库列表 / 表列表 / 列结构(三级递进)
23
- preview { conn_id, table, limit?, database? } # 预览,limit 上限 50
24
- run_script { conn_id, code } # Redis 命令数组 JSON / Mongo BSON 文档 JSON
25
- ```
26
-
27
- 可选 `challenge_id` 用于危险操作确认后的重试。
28
-
29
- ## 危险确认通道
30
-
31
- 模型侧与侧边栏控制台共用同一套确认语义:
32
-
33
- 1. 首次执行危险语句返回 `{"ok":false,"code":"NEEDS_CONFIRMATION","challengeId","statement","danger","reason"}`。
34
- 2. 模型向用户说明并征得同意(侧边栏则弹 DangerDialog)。
35
- 3. 携同一 `challengeId` 重发;challenge 一次性、绑定语句 hash、5 分钟过期,失效返回 `INVALID_CHALLENGE` 需从头再来。
36
-
37
- ## SKILL.md 规范要点(维护者参考)
38
-
39
- - 目录形态:`skills/db-admin/SKILL.md`,frontmatter 的 `name` 必须是 kebab-case(`^[a-z0-9]+(-[a-z0-9]+)*$`),`description` 供模型路由、渲染上限 500 字符、单行。
40
- - 发现根:`<projectRoot>/.dsh/skills` 等(随插件安装的 skill 由插件分发)。
41
- - 可选 frontmatter:`whenToUse`、`disable-model-invocation`、`user-invocable`——本 skill 均用默认值(允许模型自动调用)。
42
- - 修改方言速查/确认流程时,保持 SKILL.md 与 `lib/adapters/types.ts`、`lib/guard/index.ts`、`docs/api-contract.md` 三处一致。
43
-
44
- ## 排错
45
-
46
- - 工具返回 `UNAUTHORIZED_PROJECT`:项目路径未授权 → 侧边栏「授权」面板授权。
47
- - 返回 `READ_ONLY`:ro 授权下执行写 → 需用户升级为 rw。
48
- - `INVALID_ARGUMENT: 未知 action`:只接受上文 6 个 action 名。
@@ -1,133 +0,0 @@
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 处交叉提及)