dsh-dbhub-live 5.0.1 → 5.2.0-dev.3
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/AGENTS.md +95 -209
- package/README.en.md +251 -169
- package/README.md +251 -169
- package/doc/ARCHITECTURE.md +108 -0
- package/doc/CLIENT.md +124 -0
- package/doc/DESENSITIZE.md +56 -0
- package/doc/DEVELOPMENT.md +139 -0
- package/doc/IMPACT-MATRIX.md +36 -0
- package/doc/RELEASE.md +93 -0
- package/doc/REQUIREMENTS.md +177 -105
- package/doc/images/chat-query.png +0 -0
- package/doc/images/row-editor.png +0 -0
- package/doc/images/settings-page.png +0 -0
- package/lib/adhoc.mjs +412 -34
- package/lib/capability.mjs +157 -0
- package/lib/client.js +1243 -80
- package/lib/config.mjs +370 -21
- package/lib/i18n.mjs +119 -4
- package/lib/index.mjs +401 -43
- package/lib/mcp.mjs +496 -391
- package/lib/meta.mjs +267 -0
- package/lib/options.mjs +25 -5
- package/lib/policy.mjs +92 -0
- package/lib/toml.mjs +188 -0
- package/lib/tools.mjs +1093 -827
- package/lib/update.mjs +244 -0
- package/package.json +34 -10
- package/scripts/desensitize-check.mjs +10 -3
- package/scripts/desensitize.config.mjs +255 -237
- package/scripts/docs-check.mjs +214 -0
- package/scripts/release-preflight.mjs +180 -0
- package/scripts/render-check.mjs +1366 -843
- package/test/adhoc-toml.test.mjs +365 -0
- package/test/bridge.test.mjs +227 -185
- package/test/capability.test.mjs +231 -0
- package/test/client-format.test.mjs +515 -352
- package/test/configure-options.test.mjs +244 -0
- package/test/configure-ssh.test.mjs +232 -0
- package/test/copy-from.test.mjs +155 -0
- package/test/env-options.test.mjs +456 -0
- package/test/meta.test.mjs +234 -0
- package/test/options.test.mjs +160 -123
- package/test/policy.test.mjs +132 -0
- package/test/row-cache.test.mjs +237 -138
- package/test/ssh-probe.test.mjs +834 -0
- package/test/test-op-fastpath.test.mjs +208 -0
- package/test/toml.test.mjs +352 -0
- package/test/update.test.mjs +270 -0
- package/test/util.test.mjs +17 -0
- package/test/zero-knowledge.test.mjs +120 -78
package/README.md
CHANGED
|
@@ -1,169 +1,251 @@
|
|
|
1
|
-
# dsh-dbhub-live
|
|
2
|
-
|
|
3
|
-
**简体中文** · [English](README.en.md)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](#安装)
|
|
7
|
+
[](https://github.com/bytebase/dbhub)
|
|
8
|
+
[](https://www.npmjs.com/package/dsh-dbhub-live)
|
|
9
|
+
[](https://dsh-plugin.org/zh/plugins/mr-mihu/dsh-dbhub-live)
|
|
10
|
+
|
|
11
|
+
> 让 [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness) 里的 AI 直接查你的数据库——**密码只在界面里输入,不作为工具参数交给模型**。
|
|
12
|
+
|
|
13
|
+
装上它,你就能直接对 AI 说「查一下 orders 表最近 10 条」。AI 通过 [DBHub](https://dbhub.ai)(数据库 MCP 服务器)执行 SQL,但它只知道「哪个工作区的哪个环境」,以及 `mysql://host:3306/db` 这类元数据;**密码、账号、完整连接串始终留在你本机**,只在你面前的输入框里敲。
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary>📷 界面预览:设置 → DBHub 数据库工具</summary>
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
</details>
|
|
21
|
+
|
|
22
|
+
## 为什么用它
|
|
23
|
+
|
|
24
|
+
| | |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| 🔒 **凭据不进工具参数** | 模型只见类型 / 主机 / 端口 / 库名;密码与完整 DSN 不作为工具参数接收,只在界面输入 |
|
|
27
|
+
| ⚡ **一次性进程** | 无常驻 dbhub 服务:每次调用一条独立进程、跑完即杀;一次查询失败只影响它自己,多实例并行不互杀 |
|
|
28
|
+
| 🧮 **工具恒定 4 个** | 环境再多也不膨胀模型上下文 |
|
|
29
|
+
| 🗂 **按工作区 × 环境管理** | 一个工作区可配 `default` / `prod` / `dev`…(环境名支持中文),用 `source` 区分,绝不串库 |
|
|
30
|
+
| 🛡 **环境级只读** | 写操作由 dbhub 原生拒绝;**模型只能开、不能关**(关闭只由你在配置页操作) |
|
|
31
|
+
| 🔐 **手动 SSH 隧道** | 跳板机后面的库也能连;SSH 密码 / 密钥口令只在界面输入 |
|
|
32
|
+
| 📦 **开箱即用** | 没装 dbhub 就自动安装,之后按间隔自动更新 |
|
|
33
|
+
|
|
34
|
+
支持 **MySQL · PostgreSQL · MariaDB · SQLite · SQL Server**。
|
|
35
|
+
|
|
36
|
+
<!-- release-test:versions:begin -->
|
|
37
|
+
## 支持的版本范围
|
|
38
|
+
|
|
39
|
+
**DSH 0.1.5 起(0.1.5 / 0.1.6 / 0.1.7 / 0.2.0 / 0.2.1 共 5 条版本线,含预发布)** — 6 个声明版本已实测通过(最近验证 2026-10-24)。
|
|
40
|
+
|
|
41
|
+
<details>
|
|
42
|
+
<summary>逐版本实测明细(点开查看)</summary>
|
|
43
|
+
|
|
44
|
+
| DSH 版本 | 界面 | 启动 | 硬检查 | 结论 | 最近验证 |
|
|
45
|
+
| --- | --- | --- | --- | --- | --- |
|
|
46
|
+
| `0.1.5-rc.3` | 旧版 | 7.3s | 19 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
47
|
+
| `0.1.6-alpha.2` | 旧版 | 3.5s | 19 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
48
|
+
| `0.1.7-rc.2` | 旧版 | 4.7s | 20 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
49
|
+
| `0.2.0-rc.2` | 旧版 | 4.9s | 20 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
50
|
+
| `0.2.1-alpha.1` | 新版 | 5.1s | 20 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
51
|
+
| `0.2.1-rc.2` | 新版 | 5.3s | 20 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 4f6a854a |
|
|
52
|
+
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
完整证据、判定规则与已知偏差见 [doc/SUPPORT-MATRIX.md](doc/SUPPORT-MATRIX.md)。
|
|
56
|
+
<!-- release-test:versions:end -->
|
|
57
|
+
|
|
58
|
+
## 安装
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# web 端(dsh web)
|
|
62
|
+
dsh plugin --profile web add dsh-dbhub-live
|
|
63
|
+
|
|
64
|
+
# 桌面端
|
|
65
|
+
dsh plugin --profile desktop add dsh-dbhub-live
|
|
66
|
+
|
|
67
|
+
# 没装 dsh(用 npx):命令相同,前缀换成 npx @deepseek-ai/dsh
|
|
68
|
+
npx @deepseek-ai/dsh plugin --profile web add dsh-dbhub-live
|
|
69
|
+
npx @deepseek-ai/dsh plugin --profile desktop add dsh-dbhub-live
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
桌面端也可以**全程用界面装**:**左侧菜单 → 插件 → 新增 → 输入 `dsh-dbhub-live`(或从插件市场搜索)→ 安装**。**同一个插件在 web profile 与 desktop profile 上都能用**,界面与行为一致;桌面端的安装 / 更新 / 卸载都可以在这张「插件」页里完成。
|
|
73
|
+
|
|
74
|
+
装完 web 端**重启 `dsh web`**,打开 **设置 → DBHub 数据库工具** 即可看到配置页(也有左侧栏快捷入口和插件页那行的「配置」,三个入口是同一个页面)。
|
|
75
|
+
|
|
76
|
+
> 环境要求:`dsh` 0.1.5 起的全部版本线(含 0.2.x);**新界面需要 0.2.1+**(含 0.2.1 的预发布版)——宽屏整页、页头版本与升级提示、行内只读开关都只在 0.2.1 及以上出现;**0.1.x 仍受支持,但保持旧版布局与行为**。建议本机有 Node.js ≥ 18(用于自动安装 dbhub)。**只读模式与 SSH 隧道需要 dbhub 1.4.0+**,插件会自动检测并给出升级指引。每个版本的实测记录见 [doc/SUPPORT-MATRIX.md](doc/SUPPORT-MATRIX.md)(由发布测试自动生成,发版前必跑)。
|
|
77
|
+
|
|
78
|
+
## 更新
|
|
79
|
+
|
|
80
|
+
**Web 端**(命令行):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
dsh plugin --profile web up dsh-dbhub-live@<最新版本号>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
然后**重启 `dsh web`**。
|
|
87
|
+
|
|
88
|
+
**桌面端**:在 **左侧菜单 → 插件** 页里卸载该插件,再重新安装最新版本(桌面端更新走可视化插件页)。**连接与凭据不会被重装清掉**——它们都在 `~/.dsh/storages/dsh-dbhub-live`,与模块目录无关。
|
|
89
|
+
|
|
90
|
+
**不用自己记版本号**:配置页页头显示插件名与当前版本,检测到更新时会出现一行低调的「**↑ 可升级到 <latest>**」文字链接,**点一下就会复制** `dsh-dbhub-live@<latest>`;旁边的「检查更新」按钮可以随时手动检查。打开页面时也会自动检查一次(有节流,不会每次打开都打网络)。检查以 **npm registry 为主、GitHub Releases 为兜底**,发布说明见 [GitHub Releases](https://github.com/mr-mihu/dsh-dbhub-live/releases)。
|
|
91
|
+
|
|
92
|
+
> 版本与升级提示属于新界面,需要插件 5.2.0+ 且 DSH 0.2.1+;0.1.x 上不显示(见「配置页」)。
|
|
93
|
+
|
|
94
|
+
## 快速开始
|
|
95
|
+
|
|
96
|
+
不用记任何命令,直接跟 AI 说:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
你:mysql 192.0.2.10:3306 的 app 库,账号 ops
|
|
100
|
+
AI:(弹出输入框,你在里面填密码)→ 已保存,source = myapp_1a2b3c_default
|
|
101
|
+
你:查询 orders 表最近 3 条数据
|
|
102
|
+
AI:…(返回查询结果)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
<details>
|
|
106
|
+
<summary>📷 实际效果:向 AI 提问,直接拿到查询结果</summary>
|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
</details>
|
|
111
|
+
|
|
112
|
+
密码全程只在你面前的输入框里敲。若你只说了一部分信息(比如「连一下 192.0.2.10 上的 mydb」),插件会**先用这些非敏感信息试连**:能连上就直接保存(不需要密码的库全程零输入),只差密码就只问你「账号(未知时)+ 密码」,信息不全才让你选完整方式(输入 DSN / 填写分项 / 授权扫描项目配置文件)。
|
|
113
|
+
|
|
114
|
+
> **别把密码直接粘进对话。** 插件自己不把密码交给模型,但**你亲手粘进对话的内容一定会进入上下文**——这一条插件拦不住,也收不回来。
|
|
115
|
+
|
|
116
|
+
工作区里已有 `mise env` 或 `.env`(`DSN` / `DB_*`)时,插件会自动发现,无需手动配置。
|
|
117
|
+
|
|
118
|
+
## 工具
|
|
119
|
+
|
|
120
|
+
| 工具 | 说明 |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `dbhub_configure(...)` | 为工作区配置 / 修改连接。**不接受 `dsn` 参数**——密码与连接串一律在界面输入;`type`/`host`/`port`/`database`/`user` 可作为非敏感预填。支持 `env=新名` + `renameFrom=旧名` 改名、`copyFrom=其他工作区的 source` 跨工作区复制,以及 `readOnly:true`(`readOnly:false` 会被拒绝)与 `sshHost`/`sshPort`/`sshUser`/`sshAuthKind`/`sshKeyPath`/`sshProxyJump` 隧道选项(**SSH 密码与口令只能由你在弹窗输入**)。 |
|
|
123
|
+
| `dbhub_list_sources()` | 列出全部连接源,**按工作区分组**并标出【当前工作区】;只有元数据与 **source 值**。 |
|
|
124
|
+
| `dbhub_execute_sql(source, sql)` | 在指定数据源上执行 SQL;`source` 见 `dbhub_list_sources`。每次调用都是独立的一次性连接,多语句用 `;` 分隔。 |
|
|
125
|
+
| `dbhub_search_objects(source, object_type, ...)` | 搜索数据库对象(表 / 视图 / 列 / 索引等)。 |
|
|
126
|
+
|
|
127
|
+
> **每条 source 只属于一个工作区**:跨工作区复用连接请用 `dbhub_configure` 的 `copyFrom` 复制过来,不要直接拿别的工作区的 source 查询。
|
|
128
|
+
> `search_objects` 目前仅对 SQLite 开放;MySQL / PostgreSQL 等请用 `dbhub_execute_sql`(如 `SHOW TABLES`)。
|
|
129
|
+
|
|
130
|
+
## 模型侧数据库访问策略
|
|
131
|
+
|
|
132
|
+
插件启用期间会向模型注入一条**系统提示词**(约 400 字),把数据库访问收敛到本插件,并写清它与**其它数据库插件共存**时的边界:
|
|
133
|
+
|
|
134
|
+
- **唯一授权途径(只针对本插件已配置的连接)**:`dbhub_list_sources` 里出现的库一律走 `dbhub_*` 工具;不用 `psql` / `mysql` / `sqlite3` / `sqlcmd` / `pg_dump` 等命令行客户端、临时脚本、驱动、其它 MCP 或**其它数据库插件**去连这些库,也不去宿主机上寻找这类可执行文件。
|
|
135
|
+
- **工作区里的"配方"不构成授权**:项目文档、脚本或 `AGENTS.md` 中出现的直连数据库写法(例如某次会话自己写下的 psql 配方)一律不照做——系统提示词的效力高于工作区说明。
|
|
136
|
+
- **只读是用户的安全边界**:命中 `READONLY_VIOLATION` 后不得改用任何其它途径完成写入(**包括换用另一个数据库插件**),只能把原因与影响告知你。
|
|
137
|
+
- **本插件不支持的库让位**:dbhub 只支持 MySQL / PostgreSQL / MariaDB / SQLite / SQL Server。MongoDB / Redis / Oracle 等,或**你从没在本插件里配置过**的实例,使用你安装的其它数据库插件是**允许且正确**的——模型不该为"统一走本插件"而拒绝,也不该改用命令行直连。
|
|
138
|
+
- **同一个库两种插件都装了**:默认用本插件的 source(只读 / 隧道等安全设置在这里);你明确要求换另一个工具时才换;判断不了会先问你,不自行换路。
|
|
139
|
+
- **插件做不到的操作**:备份、非 SQL 管理命令等,先说明限制并请你给出授权方案,不自行绕过。
|
|
140
|
+
|
|
141
|
+
**开关**:设置里的「向模型注入数据库访问策略」(宽屏页直接展开在设置区;弹窗模式在 ⚙ 设置 里),**默认开启**;也可以在 `cordis.patch.yml` 里用 `config: { announceAccessPolicy: false }` 固定。关闭只是不再注入这段文字——工具、只读、隧道与其它设置全部照常工作。
|
|
142
|
+
|
|
143
|
+
**不压制同类插件**:插件不会(也无法)禁用你安装的其它数据库插件。冲突靠上面三条收敛:作用域(只管自己配置的连接)、重叠目标的默认优先级、判断不了就问用户。插件页要关掉哪一个由你决定。
|
|
144
|
+
|
|
145
|
+
**诚实边界**:这条策略是**提示词层面的约束**(软约束);只读本身由 dbhub 在插件内**硬拒绝**。绕开插件的 shell 级直连不受插件管制,策略明确禁止了这条路,但它靠模型遵守,不是硬拦截。
|
|
146
|
+
|
|
147
|
+
## 配置页
|
|
148
|
+
|
|
149
|
+
<details>
|
|
150
|
+
<summary>📷 连接行编辑器:只读模式与 SSH 隧道</summary>
|
|
151
|
+
|
|
152
|
+

|
|
153
|
+
|
|
154
|
+
</details>
|
|
155
|
+
|
|
156
|
+
同一套组件提供**三种布局**,按承载位与窗口宽度自动切换(新界面需要插件 5.2.0+ 且 **DSH 0.2.1+**;DSH 0.1.x 上保持旧版布局与行为,见本节末尾):
|
|
157
|
+
|
|
158
|
+
| 布局 | 进入方式 | 形态 |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| 弹窗模式 | **设置 → DBHub 数据库工具** | 窄列弹窗,密度与旧版一致,设置收在状态行的「设置」折叠里 |
|
|
161
|
+
| 宽屏模式 | 左侧菜单的 **DBHub** 入口 / 插件页该行的「配置」 | 整页宽列,页头带版本与升级入口,设置行直接展开 |
|
|
162
|
+
| 小屏模式 | 手机浏览器,宽度 < ~700px | 纵向堆叠的紧凑布局,控件按触摸尺寸 |
|
|
163
|
+
|
|
164
|
+
**页头:版本与升级入口**:标题是插件名,紧跟当前版本(灰色小字);有更新时右侧出现一行低调的「**↑ 可升级到 <latest>**」文字链接,**点开才展开**更新说明——里面是 `dsh-dbhub-live@<latest>`(点击复制)、按部署方式给的更新命令 / 步骤,以及更新说明链接;「↻」按钮随时手动检查一次。打开页面时也会自动检查(有节流)。检查以 npm registry 为主、GitHub Releases 为兜底。**更新是中性提示,不会用报错红。**
|
|
165
|
+
|
|
166
|
+
- **状态行**:只显示「运行中 / 已停用 · N 个连接」。工具数量、一次性执行模式、dbhub 版本这类实现细节收进页面底部的「ⓘ 说明与技术详情」折叠区,默认不展开。
|
|
167
|
+
- **连接列表**:按工作区组织。所有工作区都只有一个环境时用平铺卡片;任一工作区有多个环境时按工作区分组折叠(点组头展开,默认展开最近使用的那个)。每个连接只占一行:环境名 · 类型 / 主机 / 端口 / **库名** · **只读开关** · `source` 句柄(点它即复制) · 测试 / 修改 / 删除。窄窗口下先截断主机端口,**库名始终可见**。
|
|
168
|
+
- **设置区(DSH 主体样式,刻意弱化)**:宽屏把四项收成**一行紧凑设置条**——`插件设置 · 启用插件[启用] · 侧边栏入口[显示] · 访问策略[开启] · 自动更新[7]天[保存] · ⓘ说明`;弹窗与手机是紧凑设置行。字号与开关都收小,让**连接列表**成为页面主体。控件用 DSH 自己的开关(跟随主题 / 皮肤,带汉字状态词);解释性长文字都在「ⓘ 说明与技术详情」里。
|
|
169
|
+
- **只读开关(三种布局都在外面)**:每条连接行的数据源旁边就是该环境的「只读」小开关,点一下即改(开锁图标 + 「可写」= 可写;闭锁图标 + 「只读」= 已只读),不必打开编辑表单;弹窗 / 手机同样可用。编辑表单里的勾选也保留,两处是同一个状态。**连接串本身不再带锁形图标**——过去容易被误读成"需要密码",也和只读锁冲突。
|
|
170
|
+
- **增删改**:行内 ✏ 改连接串或环境名;「+ 添加连接」支持中文环境名。**同名环境保存前会先问「是否覆盖」**,取消则一个字都不写。
|
|
171
|
+
- **连接测试**:按该环境**真正会被使用的有效连接**(含只读 / 隧道)执行 `SELECT 1`,测试中显示已耗时。结果是一次性反馈,不持久化、不改变连接状态。
|
|
172
|
+
|
|
173
|
+
**「在侧边栏显示入口」不是禁用插件**:关掉它只是隐藏左侧栏的快捷入口,插件照常工作、模型照常可以查询;想整体停用才用「禁用」(禁用后入口一律隐藏)。无论哪种状态,**设置页与插件页那行的「配置」始终能进**,随时可以撤销。
|
|
174
|
+
|
|
175
|
+
**DSH 0.1.x 的兼容路径**:页头版本 / 升级提示、宽屏整页、行内只读开关与开关样式的设置区都只在 **DSH 0.2.1+**(含预发布版)渲染;0.1.x 上插件保持原有布局与行为,**不显示升级提示**——这是刻意保留的,不是缺陷。
|
|
176
|
+
|
|
177
|
+
## 🔒 只读模式与 SSH 隧道
|
|
178
|
+
|
|
179
|
+
两项都是**按环境**设置的连接选项,由 dbhub 原生能力执行。开启后,该环境的每次执行改用一份**临时生成的 dbhub 配置**(文件里只有 `${…}` 占位符、不含任何密码,真值只经子进程环境传入),用完即删;其余环境不受影响。
|
|
180
|
+
|
|
181
|
+
**只读模式**:三种布局里,连接行数据源旁都有一个「只读」开关,点一下即改(也可以打开 ✏ 在编辑表单里勾选;模型也能为某环境开启)。开启后 dbhub 只放行只读语句(MySQL 实测为 `select/with/explain/show/describe/desc`),写操作直接返回 `READONLY_VIOLATION`;对象搜索照常可用。**关闭只读只能由你在配置页操作**——模型尝试关闭会被拒绝。只读环境的环境名后会多一个「只读」小标。
|
|
182
|
+
|
|
183
|
+
**SSH 隧道**(手动配置,适用于库在跳板机后面的场景):
|
|
184
|
+
|
|
185
|
+
| 字段 | 说明 |
|
|
186
|
+
| --- | --- |
|
|
187
|
+
| SSH 主机 | **域名或 IP**(如 `203.0.113.10`);不要填 `~/.ssh/config` 里的别名 |
|
|
188
|
+
| 端口 | 默认 `22` |
|
|
189
|
+
| SSH 用户 | 登录跳板机使用的账号 |
|
|
190
|
+
| 认证方式 | **密钥**(填密钥路径;私钥有口令可再填口令;**留空 = 用默认 `~/.ssh/id_ed25519`**)或**密码** |
|
|
191
|
+
| ProxyJump | 可选,**只支持一跳**,格式 `[user@]host[:port]` |
|
|
192
|
+
| 密钥口令 / SSH 密码 | **只能在界面输入**、不回显;编辑时**留空 = 保持原值** |
|
|
193
|
+
|
|
194
|
+
「高级:SSH 隧道」里有**两个**测试按钮:
|
|
195
|
+
|
|
196
|
+
- **「测试 SSH 隧道」**只验证隧道这一层:先做 `ssh_host:ssh_port` 的 TCP 可达性探测(约 5 秒,失败会带 `ENOTFOUND` / `ECONNREFUSED` / `ETIMEDOUT` 等错误码),密钥认证时检查私钥文件存在可读,最后**故意**让 dbhub 对一个不可能在监听的地址建隧道,再按证据判定:报错带 **SSH 自身特征**(握手 / 认证 / 私钥)就是隧道断;带 **数据库层特征**(`PROTOCOL_CONNECTION_LOST`、`Connection lost`、`Access denied`、`Unknown database`、`ER_*`、`ECONNREFUSED` 等)就说明连接**已经穿过隧道**,结论是「SSH 隧道可用」;都没有才如实说「无法判定」并附输出末尾。
|
|
197
|
+
- **「测试该配置」**按「隧道 + 只读」的**有效连接**实测。
|
|
198
|
+
|
|
199
|
+
> **一句话记住**:报**数据库层**的错误 = 隧道已经通了,问题在隧道后面的数据库(认证 / 库名 / 目标地址),不必再怀疑 SSH。
|
|
200
|
+
>
|
|
201
|
+
> **诚实边界**:SSH 层测试证明的是"网络通 + 密钥在 + SSH 层起得来",不是一次带真实数据库的完整会话验证——隧道建起来不等于后面的库一定连得上,那一步由「测试该配置」回答。
|
|
202
|
+
|
|
203
|
+
排查顺序:① 本机能 `ssh 用户@跳板机` 登录;② **连接串里的地址必须是"从跳板机看过去"的地址**(库就在跳板机本机时写 `127.0.0.1`)——这是最常见的原因;③ 密钥路径存在且可读(加密私钥记得填口令);④ ProxyJump 只有一跳。
|
|
204
|
+
|
|
205
|
+
**前提与限制**:需要 dbhub **1.4.0+**(版本过旧会明确报错,不会静默忽略);**不支持**多跳、`ProxyCommand`、ssh-agent,也**不复用**任何其他插件(包括 DSH 自带的 SSH 工具)的 SSH 配置——隧道信息只来自你手动填写的内容。只读与 SSH 是两个独立开关,可以只开其中一个。
|
|
206
|
+
|
|
207
|
+
## 数据位置
|
|
208
|
+
|
|
209
|
+
所有配置与凭据都在模块目录之外,删掉该目录即完整清空:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
~/.dsh/storages/dsh-dbhub-live/
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
按 `DSH_HOME` 实例隔离,同一实例的多个 profile 共享(与 dsh 自身 `workspace.json` 同一约定)。连接按「工作区 × 环境」存储;旧格式与手改遗留会在启动时自动清洗并一次性迁移,运行目录被误删或写入被拦截时不会崩溃。**没有常驻进程、没有共享配置文件**,同机多实例并发运行互不干扰。**升级、重装或卸载插件都不会动这里**——包括桌面端「卸载后重装最新版」的升级方式:连接、只读 / 隧道设置与凭据都原样保留。
|
|
216
|
+
|
|
217
|
+
## 卸载
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# web 端
|
|
221
|
+
dsh plugin --profile web remove dsh-dbhub-live
|
|
222
|
+
|
|
223
|
+
# 桌面端
|
|
224
|
+
dsh plugin --profile desktop remove dsh-dbhub-live
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
桌面端也可以在 **左侧菜单 → 插件** 页里直接卸载。卸载不会删除连接与凭据(见「数据位置」);需要彻底清空时手动删除 `~/.dsh/storages/dsh-dbhub-live`。
|
|
228
|
+
|
|
229
|
+
## 故障排查
|
|
230
|
+
|
|
231
|
+
| 现象 | 处理 |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| 首次使用报「无法获取 dbhub」 | 确认本机有 npm 且能联网;离线可手动安装 `dbhub` 并加入 PATH。 |
|
|
234
|
+
| 某个环境连接被拒 / 认证失败 | 该环境是独立一次性连接,只影响这一次调用。错误末尾会附指引:让 AI 调用 `dbhub_configure` 引导你在界面更新密码,或直接到配置页修改。 |
|
|
235
|
+
| 工具显示「插件已禁用」 | 到 **设置 → DBHub 数据库工具** 点「启用」。 |
|
|
236
|
+
| 禁用后左侧栏入口不见了 | 预期行为(禁用中的插件不贡献入口)。点「启用」后入口立即回来。 |
|
|
237
|
+
| 开启只读后写操作报 `READONLY_VIOLATION` | 不是故障,是只读在生效。需要写入时在配置页取消该环境的「只读模式」(模型关不掉)。 |
|
|
238
|
+
| 只读 / SSH 控件是灰的,或提示 dbhub 版本过旧 | 两项都需要 dbhub 1.4.0+:升级 dbhub,或删除 `~/.dsh/storages/dsh-dbhub-live` 让插件自动装一个满足要求的版本。 |
|
|
239
|
+
| 连接测试失败,看不出原因 | 读提示的**末尾**(诊断给的是 dbhub 输出的尾部,会标出【SSH 层】/【数据库层】/【插件层】)。**报【数据库层】就说明隧道已经通了**;有隧道时先点「测试 SSH 隧道」把 SSH 层单独验一遍。 |
|
|
240
|
+
| 看不到配置页 | 确认插件已安装:web 端重启过 `dsh web`;桌面端在「插件」页该行点「配置」,或从左侧菜单的 DBHub 入口进。配置页只在 Web / 桌面界面显示,终端环境下不影响工具使用。 |
|
|
241
|
+
| 升级到新版本后仍是旧界面 | 新界面需要 **DSH 0.2.1+**(含其预发布版)且插件 5.2.0+;升级 dsh 后重启。DSH 0.1.x 上插件有意保持旧布局与行为,也不显示升级提示。 |
|
|
242
|
+
| 页头没有出现「升级到 …」提示 | 可能已是最新版本,或这次检查没连上 npm registry / GitHub Releases;点「检查更新」重试即可。检查失败只是提示,不影响插件功能。 |
|
|
243
|
+
| 桌面端怎么升级 | 在 **左侧菜单 → 插件** 页卸载后重装最新版本;连接与凭据在 `~/.dsh/storages/dsh-dbhub-live`,重装后仍在。 |
|
|
244
|
+
| 升级 dsh 后提示插件与本版不兼容 | 升级插件到 5.0.0+(同时支持 0.1.x 与 0.2.x);用 `allow-version` 强行豁免没有意义。 |
|
|
245
|
+
| 不希望自动更新 dbhub | 配置页「自动更新间隔(天)」填 `0` 保存,或设环境变量 `DSH_DBHUB_UPDATE_DAYS=0`。 |
|
|
246
|
+
|
|
247
|
+
**环境变量**(仅这两项,不在 UI 暴露):`DSH_DBHUB_PACKAGE`(自动安装的包名,默认 `@bytebase/dbhub`)、`DSH_DBHUB_UPDATE_DAYS`(自动更新间隔种子,默认 `7`,被配置页保存的值覆盖)。
|
|
248
|
+
|
|
249
|
+
## 许可证
|
|
250
|
+
|
|
251
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# 架构理由与踩坑记录(按需加载)
|
|
2
|
+
|
|
3
|
+
> AGENTS.md 只记**结论**;这里记**原因**——每条红线的由来、当时踩到的坑、以及"为什么不能那样改"的判断依据。
|
|
4
|
+
> 改红线或质疑红线前读对应章节。操作步骤见 [DEVELOPMENT.md](DEVELOPMENT.md) / [CLIENT.md](CLIENT.md) / [RELEASE.md](RELEASE.md)。
|
|
5
|
+
|
|
6
|
+
## 零知识边界
|
|
7
|
+
|
|
8
|
+
- 模型上下文就是泄露面:任何进 context 的字符串都会被摘要、日志和后续轮次复制。所以规则不是"少给"而是"永不给"——工具参数不接受 DSN(模型传 DSN 等于把密码写进对话),输出一律经 `connLabel` / `scrubSecrets`。
|
|
9
|
+
- dbhub 的启动横幅**只把密码掩成 `****`,用户名原样打印**;而连接测试的诊断文本正是这段输出。所以清洗必须折叠任意 URL 形态连接串的整个 userinfo(`://user:pass@`、`://user@` → `://****:****@`),只掩密码不够。
|
|
10
|
+
- 仓库被视为公开分发面:这些真实值曾随 npm 包与公开仓库分发过一次,公开即视为泄露、无法追回(详见 [DESENSITIZE.md](DESENSITIZE.md))。
|
|
11
|
+
|
|
12
|
+
## 一次性进程的理由
|
|
13
|
+
|
|
14
|
+
- 常驻 dbhub 服务器会带来一整套生命周期问题:端口/stdio 归属、配置变更重启、指纹与 mtime 监听、空闲回收、孤儿进程、多实例互杀——5.0 之前这些都真实出现过。最终选择"每次调用一条进程":天然并发隔离、一条进程挂只坏它自己、多实例无共享状态、没有常驻资源可泄漏。
|
|
15
|
+
- 代价是每次的启动延迟(spawn + initialize),换来的是可预测性。**不要以"优化启动"为名把常驻服务装回来。**
|
|
16
|
+
|
|
17
|
+
## 一次性TOML的由来
|
|
18
|
+
|
|
19
|
+
- `--dsn` 与 `--config` 互斥,而 dbhub 只有 TOML 方言能表达环境级只读(`[[tools]] readonly`)与 SSH 隧道(`ssh_*`)。所以只有"需要这两个特性"的环境走临时 TOML,其余**逐字节保持** `--dsn`(历史行为,密码在 argv)。
|
|
20
|
+
- 三个踩过的坑:
|
|
21
|
+
1. 秘密不能落文件 → 文件只写 `${DSH_DBHUB_SEC_*}` 占位符,真值走子进程 env。**dbhub 取不到变量时不报错**,会把 `${VAR}` 原样留在 DSN 里(于是连了一个字面量叫 `${VAR}` 的目标,或出现"神秘失败")——所以"文本引用的变量集合 == env 键集合"是硬断言。
|
|
22
|
+
2. `[[tools]]` 是**白名单不是补丁**:只要出现任意一条,dbhub 就不再自动挂 `execute_sql` + `search_objects`。只写一条 read_only 会让该环境的 `search_objects` 直接消失(`Tool search_objects not found`)。
|
|
23
|
+
3. source 级 `read_only` 写法会被**静默忽略**,比报错危险得多 → 绝不生成未实测的旧写法。1.4.0 是实测版本,`< 1.0.0` 明确拒绝。
|
|
24
|
+
- Windows 上 dbhub 会 watch 这个临时文件,删除可能 EBUSY → `finally` 尽力删 + boot 时 `sweepTempToml()` 清残留。
|
|
25
|
+
|
|
26
|
+
## 环境选项与执行预算
|
|
27
|
+
|
|
28
|
+
- 选项**没有版本号也没有迁移函数**是刻意的:`normalizeStore` 保留未知字段,给 env 条目加可选字段不需要迁移;旧文件读回来就是"无只读、无隧道"。别为了"规范"加迁移。
|
|
29
|
+
- `setWorkspaceEnv` 的 opts **缺省必须保留**既有选项:`dbhub_configure` 只更新 DSN 的场景下,若默认清空就会把用户的安全开关静默关掉——这是"只读/隧道莫名丢失"的根因。
|
|
30
|
+
- 安全不对称必须在**工具路径**拦截而不是 store:模型只能收窄不能放宽,但浏览器必须能自由关,否则用户永远关不掉自己开的只读。
|
|
31
|
+
- 预算与看门狗的三角关系:客户端看门狗必须**严格大于**宿主探测预算,否则客户端先放弃、界面报"测试超时",而宿主那条 dbhub 还在跑;宿主再加 +500ms 兜底,处理 spawn 本身永不 settle 的情况。三个数字必须一起改,`test/adhoc-toml.test.mjs` 会断言。
|
|
32
|
+
|
|
33
|
+
## envSlug事故
|
|
34
|
+
|
|
35
|
+
- 早期实现把非 ASCII 环境名统一 slug 化成一个值,`线上` 与 `测试` 拿到同一个 source 句柄 → 解析时**静默查错库**(模型以为在测环境,实际打线上)。现在:`default` 裸值、纯 ASCII 保留原 slug(既有 source 不变)、非 ASCII 取 `env-<shortHash>`。这是回归红线,不是风格问题。
|
|
36
|
+
|
|
37
|
+
## 状态通道与inject根因
|
|
38
|
+
|
|
39
|
+
> **为什么不能改回 inject(5.0 移植的根因)**:dsh 0.1.7 用 Config 表单取代了 settings 命名空间,0.2.x 又删掉浏览器侧 `settingsScope`。旧客户端 `inject: ['slots', 'settingsScope']` 在 0.2.x 上永远等不到服务 → 整个浏览器半区 pending、**无任何报错**,表现就是「配置页 / 侧边栏 / 插件页卡片静默消失」。所以这一层必须做成运行时探测 + 双实现(`legacyFace` / `httpFace`),而不是声明式依赖。
|
|
40
|
+
|
|
41
|
+
- 第二个静默失败面:**注入回调是独立作用域**。在回调里引用 `apply()` 的局部变量会 ReferenceError,而错误发生在发布链路上——页面只剩静态值、无任何可见报错。需要的服务必须 `ctx.get(...)` 就地获取。
|
|
42
|
+
- 两条通道都发布同一个 view 是刻意的:客户端只写一份渲染逻辑,通道差异收敛在 `legacyFace` / `httpFace` 两个适配器里。
|
|
43
|
+
|
|
44
|
+
## 诊断为什么取尾部
|
|
45
|
+
|
|
46
|
+
- dbhub 失败时致命行在输出末尾(前面是启动横幅与重试日志),取开头只能得到 `connecting…`。所以 `diagnosticTail` 取尾部 600 字符、丢 `at …` 栈帧与折行残片、按行边界截断(否则会截出半行 JSON/半个错误)。
|
|
47
|
+
- 分层前缀的意义:SSH 层与数据库层的可诊断性不同——只有**独立的 SSH 探测**能证明"隧道起来了";反向不成立,所以 db 层失败的措辞必须谨慎,不能写成"隧道断了"。
|
|
48
|
+
|
|
49
|
+
## 即时回推与扫描单飞
|
|
50
|
+
|
|
51
|
+
- `collectSources` 每次都要重扫全部工作区(无持久化 `default` 的工作区各起一次 `mise env`),一次写入原本要等 1–2s。更糟的是**并发扫描互相覆盖**:探针回执触发的扫描可能在写入前开始、写入后结束,把新行刷掉——表现就是"立刻出现又消失"。
|
|
52
|
+
- 结论:写入路径返回本次补丁并 `publishFast` 同步镜像;探针只镜像不扫描;扫描单飞并重放扫描期间到达的补丁;`rename` 补丁必须幂等(否则重放会丢行)。一次点击 = 1 次扫描 + 2 次纯内存镜像。
|
|
53
|
+
|
|
54
|
+
## 自动发现的代价与unref教训
|
|
55
|
+
|
|
56
|
+
- `mise env` 探测没有总超时会出事:曾有一个不退出的 `mise env` 让整轮扫描永不返回,而 `dbhub_list_sources` 正走这条同步路径,模型侧看到 `Interrupted`。所以每次探测必须有总超时(`graceMs` 只是 SIGTERM→SIGKILL 宽限,**不是超时**),结果按 TTL 缓存,工作区之间并发。
|
|
57
|
+
- **预算计时器绝不 `unref()`**:unref 计时器在"它是唯一待办"时不触发——事件循环排空、进程先退,超时形同虚设。该缺陷**只在 Linux CI 暴露**(整文件 cancelled),本地 Windows 会侥幸通过:本地全绿不能当发布依据。
|
|
58
|
+
|
|
59
|
+
## 布局取舍
|
|
60
|
+
|
|
61
|
+
- 行内只放图标、来源不做徽章、库名不截断,三条来自同一约束:每个连接只占一行,宽度是稀缺资源。文字按钮吃掉半行;"来源"徽章几乎每行相同却最抢眼;库名被截断是最高频的抱怨。
|
|
62
|
+
- 三面布局的两个阈值(700 / 720):实测出现过 710px 视口既不算手机(>= 700)也不够宽(< 720)的**空档**,布局判定落空。两阈值之间的窄桌面列必须走紧凑设置条,不能退回竖排。
|
|
63
|
+
- 现代面与 legacy 面必须能同时存在:`render-check` 的旧断言写死了 0.1.x 的 DOM,新界面只能挂在 `dshModern` 下;否则一次"统一界面"就会让旧线渲染异常。
|
|
64
|
+
|
|
65
|
+
## 覆盖门为什么先问
|
|
66
|
+
|
|
67
|
+
- `setWorkspaceEnv` 是**无条件写入**:新增或改名落到已存在的环境会静默替换原连接,而客户端无法回滚。所以写入前必须比较,命中就弹确认卡;比较前必须过 `normalizeEnvForCompare`(镜像宿主 `normalizeEnvName`),否则「prod 」与「prod」这种同键异写会漏判。取消 → 零 op 且保留表单内容。
|
|
68
|
+
|
|
69
|
+
## 脱敏的不可逆性
|
|
70
|
+
|
|
71
|
+
- 详见 [DESENSITIZE.md](DESENSITIZE.md)。一句话:真实值曾随 npm 包与公开仓库分发过一次,公开即无法追回;所以门禁是硬门,示例主机只用回环/RFC 5737,示例密码只用 `CHANGE_ME`,任何人不得把本机绝对路径写进受版本控制的文件。
|
|
72
|
+
|
|
73
|
+
## 版本门与更新检查
|
|
74
|
+
|
|
75
|
+
- `dshModern` 门(>= 0.2.1,含预发布)不是"只有新版本测过",而是"新界面依赖的 slot/DOM 能力在这个版本才稳定"。运行版本解析不到时回退为 desktop=true / web=false——桌面端总是新版、旧 web 线保持 legacy,比猜一个值安全。
|
|
76
|
+
- 更新检查**绝不抛错**:它只是页头一行提示,失败必须只体现为 `error` 字段,不得影响插件任何功能。
|
|
77
|
+
|
|
78
|
+
## 模块地图
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
lib/
|
|
82
|
+
index.mjs 入口:name/inject/apply、Config(volatile)、发布核心 createHostCore(walk/row-mirror/runOp/view)、
|
|
83
|
+
双通道接线(0.1.x settings 命名空间 / connection.fetch HTTP 桥)、settings 表单策略与选项写回、configOp 命令处理
|
|
84
|
+
meta.mjs 插件版本(读 package.json)、DSH 运行时版本探测、semver 比较、dshModern 门
|
|
85
|
+
update.mjs 最新版本检查:npm registry 为主 → GitHub Releases 兜底;内存节流、force、绝不抛错
|
|
86
|
+
bridge.mjs /api/dsh-dbhub-live 四条 exact Fetch 路由的请求/响应编解码(纯函数)
|
|
87
|
+
config.mjs 路径与原子持久化(store/runtime/prefs)、DSN 解析、工作区发现、环境选项(readOnly/ssh 归一化、默认密钥)、
|
|
88
|
+
零知识助手(describeConn/connLabel/scrubSecrets)、configure 决策纯函数(decideConfigureStep/argsChangedEndpoint/emptyUserDenied)
|
|
89
|
+
i18n.mjs 宿主侧文案表与插值
|
|
90
|
+
state.mjs 运行时状态机 enabled/phase/toolCount/lastError/mode + 订阅发布
|
|
91
|
+
options.mjs 部署开关:updateIntervalDays / showSidebarEntry / announceAccessPolicy;层叠 Config > prefs.json > 0.1.x 设置文档 > 环境变量 > 内置默认
|
|
92
|
+
runtime.mjs dbhub 可执行文件发现、按需自动安装、定期自动更新
|
|
93
|
+
capability.mjs 上游能力探测(文件级,不 spawn):[[tools]] readonly 与 ssh_* 需 1.4.0+
|
|
94
|
+
toml.mjs 一次性 TOML 生成器 buildDbhubToml/writeTempToml/removeTempToml/sweepTempToml(文本绝不含秘密)
|
|
95
|
+
mcp.mjs MCP JSON-RPC 客户端、工具注册表、来源发现 collectSources/resolveSource、摘要缓存、测试快路径 resolveTestDsn
|
|
96
|
+
adhoc.mjs 唯一执行引擎:一次性 dbhub 进程(描述符 {dsn,ro,ssh})、--dsn 或临时 TOML、stderr 清洗、
|
|
97
|
+
probeConnection、probeSshTunnel、diagnosticTail/DIAG_TAIL_MAX=600/failureLayerOf
|
|
98
|
+
collect.mjs 授权扫描项目配置文件(mise/.env)提取 DSN 候选,含 askUser 桥接
|
|
99
|
+
tools.mjs 4 个工具的定义与注册;configure 探连优先
|
|
100
|
+
policy.mjs 模型侧访问策略:唯一的 system prompt 段(order 8900)
|
|
101
|
+
client.js Web 半(手写 lazy-CJS bundle,无构建):settings.section + 侧边栏面板 + plugins.row.config(summary/page)
|
|
102
|
+
test/ node:test 单测(纯逻辑 + bridge/客户端契约 + 零知识泄漏门)
|
|
103
|
+
scripts/ desensitize.config.mjs(槽位表=单一事实源) / desensitize-check.mjs / docs-check.mjs / render-check.mjs / release-preflight.mjs
|
|
104
|
+
release-test/ 版本矩阵实测:matrix.json(唯一声明) / run.mjs / support-matrix.mjs / mise.toml
|
|
105
|
+
.github/ workflows/quality.yml(CI gate) + release-matrix.yml(定时 support:check)
|
|
106
|
+
doc/ REQUIREMENTS.md / ARCHITECTURE.md / DEVELOPMENT.md / CLIENT.md / RELEASE.md / DESENSITIZE.md / SUPPORT-MATRIX.md / images/
|
|
107
|
+
+ 本地文档(PLAN-*、implementation-notes、RELEASE-NOTES-*,均 gitignore 排除)
|
|
108
|
+
```
|