dsh-dbhub-live 5.2.0-dev.3 → 6.0.0-rc.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.
- package/README.en.md +36 -33
- package/README.md +37 -34
- package/lib/adhoc.mjs +108 -12
- package/lib/capability.mjs +97 -13
- package/lib/client.js +705 -315
- package/lib/config.mjs +490 -99
- package/lib/i18n.mjs +68 -2
- package/lib/index.mjs +386 -260
- package/lib/mcp.mjs +245 -49
- package/lib/meta.mjs +275 -267
- package/lib/options.mjs +11 -33
- package/lib/policy.mjs +92 -92
- package/lib/runtime.mjs +80 -11
- package/lib/state.mjs +112 -117
- package/lib/toml.mjs +10 -0
- package/lib/tools.mjs +118 -24
- package/lib/update.mjs +254 -244
- package/package.json +4 -20
- package/AGENTS.md +0 -116
- package/doc/ARCHITECTURE.md +0 -108
- package/doc/CLIENT.md +0 -124
- package/doc/DESENSITIZE.md +0 -56
- package/doc/DEVELOPMENT.md +0 -139
- package/doc/IMPACT-MATRIX.md +0 -36
- package/doc/RELEASE.md +0 -93
- package/doc/REQUIREMENTS.md +0 -177
- 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/scripts/desensitize-check.mjs +0 -331
- package/scripts/desensitize.config.mjs +0 -255
- package/scripts/docs-check.mjs +0 -214
- package/scripts/release-preflight.mjs +0 -180
- package/scripts/render-check.mjs +0 -1366
- package/test/adhoc-toml.test.mjs +0 -365
- package/test/auto-dsn.test.mjs +0 -126
- package/test/bridge.test.mjs +0 -227
- package/test/capability.test.mjs +0 -231
- package/test/client-format.test.mjs +0 -515
- package/test/configure-options.test.mjs +0 -244
- package/test/configure-ssh.test.mjs +0 -232
- package/test/copy-from.test.mjs +0 -155
- package/test/env-options.test.mjs +0 -456
- package/test/i18n.test.mjs +0 -37
- package/test/init.test.mjs +0 -163
- package/test/meta.test.mjs +0 -234
- package/test/options.test.mjs +0 -160
- package/test/policy.test.mjs +0 -132
- package/test/resolve-source.test.mjs +0 -82
- package/test/resolve-workspace.test.mjs +0 -115
- package/test/row-cache.test.mjs +0 -237
- package/test/ssh-probe.test.mjs +0 -834
- package/test/state.test.mjs +0 -85
- package/test/test-op-fastpath.test.mjs +0 -208
- package/test/toml.test.mjs +0 -352
- package/test/update.test.mjs +0 -270
- package/test/util.test.mjs +0 -275
- package/test/zero-knowledge.test.mjs +0 -121
package/README.en.md
CHANGED
|
@@ -15,7 +15,7 @@ Install it and you can simply tell the AI "show me the last 10 rows of the order
|
|
|
15
15
|
<details>
|
|
16
16
|
<summary>📷 Preview: Settings → DBHub Database Tools</summary>
|
|
17
17
|
|
|
18
|
-

|
|
18
|
+

|
|
19
19
|
|
|
20
20
|
</details>
|
|
21
21
|
|
|
@@ -33,26 +33,24 @@ Install it and you can simply tell the AI "show me the last 10 rows of the order
|
|
|
33
33
|
|
|
34
34
|
Supports **MySQL · PostgreSQL · MariaDB · SQLite · SQL Server**.
|
|
35
35
|
|
|
36
|
-
<!-- release-test:versions:begin -->
|
|
37
|
-
## Supported version range
|
|
38
|
-
|
|
39
|
-
**From DSH 0.1
|
|
40
|
-
|
|
41
|
-
<details>
|
|
42
|
-
<summary>Per-version verification details (expand)</summary>
|
|
43
|
-
|
|
44
|
-
| DSH version | UI | Boot | Hard checks | Result | Last verified |
|
|
45
|
-
| --- | --- | --- | --- | --- | --- |
|
|
46
|
-
| `0.1
|
|
47
|
-
| `0.1
|
|
48
|
-
| `0.
|
|
49
|
-
| `0.2.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Full evidence, review rules and known drift live in [doc/SUPPORT-MATRIX.md](doc/SUPPORT-MATRIX.md).
|
|
36
|
+
<!-- release-test:versions:begin -->
|
|
37
|
+
## Supported version range
|
|
38
|
+
|
|
39
|
+
**From DSH 0.2.1 (lines 0.2.1 / 0.2.2, prereleases included)** — 4 declared versions not all verified yet (last verified 2026-10-24).
|
|
40
|
+
|
|
41
|
+
<details>
|
|
42
|
+
<summary>Per-version verification details (expand)</summary>
|
|
43
|
+
|
|
44
|
+
| DSH version | UI | Boot | Hard checks | Result | Last verified |
|
|
45
|
+
| --- | --- | --- | --- | --- | --- |
|
|
46
|
+
| `0.2.1-rc.2` | modern | 16.9s | 20 | ⚠️ pass with notes · manual | 2026-10-24 · 91642fa2 |
|
|
47
|
+
| `0.2.1-rc.5` | modern | — | — | — | 未验证 |
|
|
48
|
+
| `0.2.2-rc.1` | modern | — | — | — | 未验证 |
|
|
49
|
+
| `0.2.2-rc.2` | modern | — | — | — | 未验证 |
|
|
50
|
+
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
Full evidence, review rules and known drift live in [doc/SUPPORT-MATRIX.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/SUPPORT-MATRIX.md).
|
|
56
54
|
<!-- release-test:versions:end -->
|
|
57
55
|
|
|
58
56
|
## Installation
|
|
@@ -73,7 +71,7 @@ On desktop you can also **install entirely through the UI**: **left menu → Plu
|
|
|
73
71
|
|
|
74
72
|
Once installed, **restart `dsh web`** and open **Settings → DBHub Database Tools** to see the configure page (there is also a sidebar shortcut, and the Configure control on the plugin's row on the Plugins page — all three entries lead to the same page).
|
|
75
73
|
|
|
76
|
-
> Requirements:
|
|
74
|
+
> Requirements: **DSH 0.2.1 or later** (0.2.1 prereleases included; **0.1.x and 0.2.0 are no longer supported**). On an older host the plugin does not go blank: the configure page shows one bilingual notice naming the required and the running version, and every tool, connection and setting keeps working — but upgrade DSH soon. Node.js ≥ 18 is recommended locally (it is used to install dbhub automatically). **Read-only mode and SSH tunnels need dbhub 1.4.0+** — the plugin detects this and gives you upgrade guidance. Per-version verification results live in [doc/SUPPORT-MATRIX.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/SUPPORT-MATRIX.md) (generated by the release test, which must pass before every release).
|
|
77
75
|
|
|
78
76
|
## Updating
|
|
79
77
|
|
|
@@ -87,9 +85,9 @@ Then **restart `dsh web`**.
|
|
|
87
85
|
|
|
88
86
|
**Desktop**: uninstall the plugin on the **left menu → Plugins** page and reinstall the latest version (desktop updates go through the visual Plugins page). **Connections and credentials survive the reinstall** — they live in `~/.dsh/storages/dsh-dbhub-live`, independent of the module directory.
|
|
89
87
|
|
|
90
|
-
**No need to remember version numbers**: the configure page header shows the plugin name and the current version; when a newer version exists a quiet "**↑ Upgrade to <latest>**" text link appears and **clicking it
|
|
88
|
+
**No need to remember version numbers**: the configure page header shows the plugin name and the current version; when a newer version exists a quiet "**↑ Upgrade to <latest>**" text link appears and **clicking it opens a panel where you copy** `dsh-dbhub-live@<latest>`; the "Check for updates" button next to it checks on demand. Opening the page also triggers one automatic check (throttled, so it does not hit the network on every open). Checks use the **npm registry first, with GitHub Releases as the fallback**; release notes live in [GitHub Releases](https://github.com/mr-mihu/dsh-dbhub-live/releases).
|
|
91
89
|
|
|
92
|
-
> The version/update hint
|
|
90
|
+
> The version/update hint needs plugin 5.2.0+ and DSH 0.2.1+ (see "Configure Page").
|
|
93
91
|
|
|
94
92
|
## Quick Start
|
|
95
93
|
|
|
@@ -105,7 +103,7 @@ AI: … (query results)
|
|
|
105
103
|
<details>
|
|
106
104
|
<summary>📷 In action: ask the AI, get the query results</summary>
|
|
107
105
|
|
|
108
|
-

|
|
106
|
+

|
|
109
107
|
|
|
110
108
|
</details>
|
|
111
109
|
|
|
@@ -149,11 +147,11 @@ While the plugin is enabled it contributes a **system-prompt section** (about 40
|
|
|
149
147
|
<details>
|
|
150
148
|
<summary>📷 Row editor: read-only mode and SSH tunnel</summary>
|
|
151
149
|
|
|
152
|
-

|
|
150
|
+

|
|
153
151
|
|
|
154
152
|
</details>
|
|
155
153
|
|
|
156
|
-
The same component offers **three layouts**, picked automatically from the host surface and the window width (
|
|
154
|
+
The same component offers **three layouts**, picked automatically from the host surface and the window width (needs plugin 5.2.0+ and **DSH 0.2.1+**, see the end of this section):
|
|
157
155
|
|
|
158
156
|
| Layout | How to reach it | Shape |
|
|
159
157
|
| --- | --- | --- |
|
|
@@ -163,16 +161,17 @@ The same component offers **three layouts**, picked automatically from the host
|
|
|
163
161
|
|
|
164
162
|
**Header: version and upgrade entry**: the title is the plugin name with the current version (small, muted) right next to it; when a newer version exists a quiet "**↑ Upgrade to <latest>**" text link appears on the right, and **clicking it expands** the update notes — the `dsh-dbhub-live@<latest>` spec (click to copy), the update command / steps for this deployment, and a link to the release notes; the "↻" button checks on demand. Opening the page also triggers an automatic check (throttled). Checks use the npm registry first, with GitHub Releases as the fallback. **An update is neutral news, never styled as an error.**
|
|
165
163
|
|
|
166
|
-
- **Status line**: it shows only "running / disabled · N connections". Implementation facts — tool count, one-shot execution mode, dbhub version — live in the collapsed "ⓘ Details
|
|
164
|
+
- **Status line**: it shows only "running / disabled · N connections". Implementation facts — tool count, one-shot execution mode, dbhub version — live in the collapsed "ⓘ Details" block below the status line.
|
|
167
165
|
- **Connection list**: organised by workspace. When every workspace has a single environment the rows render as flat cards; as soon as one workspace owns several environments they fold into per-workspace groups (click a group head to expand; the most recently used one is open by default). Each connection takes one line: environment name · type / host / port / **database** · **read-only switch** · the `source` handle (click it to copy) · test / edit / delete. In a narrow window the host and port are truncated first, while **the database name always stays visible**.
|
|
168
166
|
- **Settings (DSH-native style, deliberately quiet)**: on the wide page the four controls collapse into **one compact settings line** — `Plugin settings · Enable plugin [enabled] · Sidebar entry [shown] · Access policy [on] · Auto-update [7] days [save] · ⓘ details`; the modal and the phone use compact stacked rows. Fonts and switches are kept small so the **connection list** stays the main content. The controls are DSH's own switches (theme/skin aware, with state words); every explanatory paragraph lives in "ⓘ Details & diagnostics".
|
|
169
167
|
- **Read-only switch (outside the edit form in all three layouts)**: right next to the datasource on every connection row there is a small "RO" toggle — one click flips it (open lock + "Writable" = writable; closed lock + "RO" = read-only) without opening the edit form; the modal and the phone layout have it too. The tick-box in the edit form stays as well; both are the same state. **The connection string itself no longer carries a lock icon** — it used to read like "this row needs a password" and clashed with the read-only lock.
|
|
170
|
-
- **Add / edit / delete**: use ✏ on a row to change the connection string or the environment name; "+ Add connection" accepts Chinese environment names. **Saving onto an environment name that already exists asks "overwrite?" first** — cancel and not a single byte is written.
|
|
171
|
-
- **
|
|
168
|
+
- **Add / edit / delete**: use ✏ on a row to change the connection string or the environment name; "+ Add connection" accepts Chinese environment names. **Saving onto an environment name that already exists asks "overwrite?" first** — cancel and not a single byte is written. **Deleting asks first too** (it cannot be undone; cancelling writes nothing either).
|
|
169
|
+
- **A failed write is always visible**: when a save / switch / delete is refused (401, offline, a Host refusal) the page shows an error line and **never** claims "Saved ✓"; fixing it and clicking again is the retry. A failed plugin-state read shows its own error line plus "Retry" instead of silently turning into "no connections". "Auto-update interval" only accepts a whole number of days from 0 to 365 — an empty box or a fraction is rejected on the spot and no request is sent.
|
|
170
|
+
- **Connection test**: runs `SELECT 1` over **the effective connection that environment will really use** (read-only / tunnel included) and shows the elapsed time while it runs. The result is one-shot feedback: nothing is persisted and no connection state changes. When the command never reaches the Host (bridge 401 / offline) it says "the test command never reached the host" immediately instead of reporting a timeout 30 seconds later.
|
|
172
171
|
|
|
173
172
|
**"Show the sidebar entry" is not disabling the plugin**: switching it off merely hides the sidebar shortcut — the plugin keeps working and the model can still query; use "Disable" to stop the plugin as a whole (a disabled plugin always hides the entry). In either state, **the settings page and the Plugins row's Configure control always stay reachable**, so the change can be undone at any time.
|
|
174
173
|
|
|
175
|
-
**
|
|
174
|
+
**When DSH is too old**: the configure page gains one warning at the top (naming "DSH 0.2.1 or later" and the running version); the page, connections, read-only switches and tools all keep working — the plugin is not broken, the host just needs an upgrade.
|
|
176
175
|
|
|
177
176
|
## 🔒 Read-only Mode and SSH Tunnels
|
|
178
177
|
|
|
@@ -238,14 +237,18 @@ On desktop you can also uninstall directly on the **left menu → Plugins** page
|
|
|
238
237
|
| The read-only / SSH controls are greyed out, or dbhub is reported as too old | Both need dbhub 1.4.0+: upgrade dbhub, or delete `~/.dsh/storages/dsh-dbhub-live` so the plugin installs a version that satisfies it. |
|
|
239
238
|
| A connection test fails and you cannot tell why | Read the **end** of the message (the diagnostic is the tail of dbhub's output and marks the layer: [SSH layer] / [database layer] / [plugin layer]). **A [database layer] report means the tunnel is already up**; with a tunnel configured, press "Test SSH tunnel" first to verify the SSH layer on its own. |
|
|
240
239
|
| The configure page is not visible | Check the plugin is installed: restart `dsh web` on the web side; on desktop use the Configure control on the Plugins row or the DBHub entry in the left menu. The page only shows in the Web / desktop UI and does not affect tool usage in terminal environments. |
|
|
241
|
-
| The
|
|
240
|
+
| The configure page says the DSH version is too old | The plugin needs **DSH 0.2.1+** (prereleases included); upgrade dsh and restart. The tools and the page keep working until you do — the notice is only a reminder. |
|
|
242
241
|
| No "Upgrade to …" hint appears in the header | You may already be on the latest version, or this check could not reach the npm registry / GitHub Releases; press "Check for updates" to retry. A failed check is only a hint and does not affect plugin functionality. |
|
|
243
242
|
| How do I upgrade on desktop | Uninstall on the **left menu → Plugins** page, then reinstall the latest version; connections and credentials live in `~/.dsh/storages/dsh-dbhub-live` and are still there after the reinstall. |
|
|
244
|
-
| After a dsh upgrade the plugin is reported as incompatible |
|
|
243
|
+
| After a dsh upgrade the plugin is reported as incompatible | Check that dsh is 0.2.1 or later, then upgrade the plugin to the latest version; forcing it through with `allow-version` buys nothing. |
|
|
245
244
|
| Automatic dbhub updates are not wanted | Set "Auto-update interval (days)" to `0` on the configure page and save, or set `DSH_DBHUB_UPDATE_DAYS=0`. |
|
|
246
245
|
|
|
247
246
|
**Environment variables** (only these two, not exposed in the UI): `DSH_DBHUB_PACKAGE` (the package that gets auto-installed, default `@bytebase/dbhub`), `DSH_DBHUB_UPDATE_DAYS` (the seed for the auto-update interval, default `7`, overridden by the value saved on the configure page).
|
|
248
247
|
|
|
248
|
+
## Changelog
|
|
249
|
+
|
|
250
|
+
Highlights of every released version live in [doc/CHANGELOG.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/CHANGELOG.md); per-release notes are on [GitHub Releases](https://github.com/mr-mihu/dsh-dbhub-live/releases).
|
|
251
|
+
|
|
249
252
|
## License
|
|
250
253
|
|
|
251
254
|
[MIT](./LICENSE)
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<details>
|
|
16
16
|
<summary>📷 界面预览:设置 → DBHub 数据库工具</summary>
|
|
17
17
|
|
|
18
|
-

|
|
18
|
+

|
|
19
19
|
|
|
20
20
|
</details>
|
|
21
21
|
|
|
@@ -33,26 +33,24 @@
|
|
|
33
33
|
|
|
34
34
|
支持 **MySQL · PostgreSQL · MariaDB · SQLite · SQL Server**。
|
|
35
35
|
|
|
36
|
-
<!-- release-test:versions:begin -->
|
|
37
|
-
## 支持的版本范围
|
|
38
|
-
|
|
39
|
-
**DSH 0.1
|
|
40
|
-
|
|
41
|
-
<details>
|
|
42
|
-
<summary>逐版本实测明细(点开查看)</summary>
|
|
43
|
-
|
|
44
|
-
| DSH 版本 | 界面 | 启动 | 硬检查 | 结论 | 最近验证 |
|
|
45
|
-
| --- | --- | --- | --- | --- | --- |
|
|
46
|
-
| `0.1
|
|
47
|
-
| `0.1
|
|
48
|
-
| `0.
|
|
49
|
-
| `0.2.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
完整证据、判定规则与已知偏差见 [doc/SUPPORT-MATRIX.md](doc/SUPPORT-MATRIX.md)。
|
|
36
|
+
<!-- release-test:versions:begin -->
|
|
37
|
+
## 支持的版本范围
|
|
38
|
+
|
|
39
|
+
**DSH 0.2.1 起(0.2.1 / 0.2.2 共 2 条版本线,含预发布)** — 4 个声明版本尚未全部通过(最近验证 2026-10-24)。
|
|
40
|
+
|
|
41
|
+
<details>
|
|
42
|
+
<summary>逐版本实测明细(点开查看)</summary>
|
|
43
|
+
|
|
44
|
+
| DSH 版本 | 界面 | 启动 | 硬检查 | 结论 | 最近验证 |
|
|
45
|
+
| --- | --- | --- | --- | --- | --- |
|
|
46
|
+
| `0.2.1-rc.2` | 新版 | 16.9s | 20 | ⚠️ 通过(有告警) · 手工装配 | 2026-10-24 · 91642fa2 |
|
|
47
|
+
| `0.2.1-rc.5` | 新版 | — | — | — | 未验证 |
|
|
48
|
+
| `0.2.2-rc.1` | 新版 | — | — | — | 未验证 |
|
|
49
|
+
| `0.2.2-rc.2` | 新版 | — | — | — | 未验证 |
|
|
50
|
+
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
完整证据、判定规则与已知偏差见 [doc/SUPPORT-MATRIX.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/SUPPORT-MATRIX.md)。
|
|
56
54
|
<!-- release-test:versions:end -->
|
|
57
55
|
|
|
58
56
|
## 安装
|
|
@@ -73,7 +71,7 @@ npx @deepseek-ai/dsh plugin --profile desktop add dsh-dbhub-live
|
|
|
73
71
|
|
|
74
72
|
装完 web 端**重启 `dsh web`**,打开 **设置 → DBHub 数据库工具** 即可看到配置页(也有左侧栏快捷入口和插件页那行的「配置」,三个入口是同一个页面)。
|
|
75
73
|
|
|
76
|
-
>
|
|
74
|
+
> 环境要求:**DSH 0.2.1 或更高版本**(含 0.2.1 的预发布版;**0.1.x 与 0.2.0 不再支持**)。宿主版本过旧时插件不会变成空白:配置页顶部会显示一条中英双语提示(写明所需版本与当前版本),工具、连接与设置照常可用——但请尽快升级 DSH。建议本机有 Node.js ≥ 18(用于自动安装 dbhub)。**只读模式与 SSH 隧道需要 dbhub 1.4.0+**,插件会自动检测并给出升级指引。每个版本的实测记录见 [doc/SUPPORT-MATRIX.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/SUPPORT-MATRIX.md)(由发布测试自动生成,发版前必跑)。
|
|
77
75
|
|
|
78
76
|
## 更新
|
|
79
77
|
|
|
@@ -87,9 +85,9 @@ dsh plugin --profile web up dsh-dbhub-live@<最新版本号>
|
|
|
87
85
|
|
|
88
86
|
**桌面端**:在 **左侧菜单 → 插件** 页里卸载该插件,再重新安装最新版本(桌面端更新走可视化插件页)。**连接与凭据不会被重装清掉**——它们都在 `~/.dsh/storages/dsh-dbhub-live`,与模块目录无关。
|
|
89
87
|
|
|
90
|
-
**不用自己记版本号**:配置页页头显示插件名与当前版本,检测到更新时会出现一行低调的「**↑ 可升级到 <latest
|
|
88
|
+
**不用自己记版本号**:配置页页头显示插件名与当前版本,检测到更新时会出现一行低调的「**↑ 可升级到 <latest>**」文字链接,**点开后在面板里点一下即可复制** `dsh-dbhub-live@<latest>`;旁边的「检查更新」按钮可以随时手动检查。打开页面时也会自动检查一次(有节流,不会每次打开都打网络)。检查以 **npm registry 为主、GitHub Releases 为兜底**,发布说明见 [GitHub Releases](https://github.com/mr-mihu/dsh-dbhub-live/releases)。
|
|
91
89
|
|
|
92
|
-
>
|
|
90
|
+
> 版本与升级提示需要插件 5.2.0+ 且 DSH 0.2.1+(见「配置页」)。
|
|
93
91
|
|
|
94
92
|
## 快速开始
|
|
95
93
|
|
|
@@ -105,7 +103,7 @@ AI:…(返回查询结果)
|
|
|
105
103
|
<details>
|
|
106
104
|
<summary>📷 实际效果:向 AI 提问,直接拿到查询结果</summary>
|
|
107
105
|
|
|
108
|
-

|
|
106
|
+

|
|
109
107
|
|
|
110
108
|
</details>
|
|
111
109
|
|
|
@@ -149,11 +147,11 @@ AI:…(返回查询结果)
|
|
|
149
147
|
<details>
|
|
150
148
|
<summary>📷 连接行编辑器:只读模式与 SSH 隧道</summary>
|
|
151
149
|
|
|
152
|
-

|
|
150
|
+

|
|
153
151
|
|
|
154
152
|
</details>
|
|
155
153
|
|
|
156
|
-
|
|
154
|
+
同一套组件提供**三种布局**,按承载位与窗口宽度自动切换(需要插件 5.2.0+ 且 **DSH 0.2.1+**,见本节末尾):
|
|
157
155
|
|
|
158
156
|
| 布局 | 进入方式 | 形态 |
|
|
159
157
|
| --- | --- | --- |
|
|
@@ -163,16 +161,17 @@ AI:…(返回查询结果)
|
|
|
163
161
|
|
|
164
162
|
**页头:版本与升级入口**:标题是插件名,紧跟当前版本(灰色小字);有更新时右侧出现一行低调的「**↑ 可升级到 <latest>**」文字链接,**点开才展开**更新说明——里面是 `dsh-dbhub-live@<latest>`(点击复制)、按部署方式给的更新命令 / 步骤,以及更新说明链接;「↻」按钮随时手动检查一次。打开页面时也会自动检查(有节流)。检查以 npm registry 为主、GitHub Releases 为兜底。**更新是中性提示,不会用报错红。**
|
|
165
163
|
|
|
166
|
-
- **状态行**:只显示「运行中 /
|
|
164
|
+
- **状态行**:只显示「运行中 / 已禁用 · N 个连接」。工具数量、一次性执行模式、dbhub 版本这类实现细节收进状态行下方的「ⓘ 说明」折叠区,默认不展开。
|
|
167
165
|
- **连接列表**:按工作区组织。所有工作区都只有一个环境时用平铺卡片;任一工作区有多个环境时按工作区分组折叠(点组头展开,默认展开最近使用的那个)。每个连接只占一行:环境名 · 类型 / 主机 / 端口 / **库名** · **只读开关** · `source` 句柄(点它即复制) · 测试 / 修改 / 删除。窄窗口下先截断主机端口,**库名始终可见**。
|
|
168
|
-
- **设置区(DSH 主体样式,刻意弱化)**:宽屏把四项收成**一行紧凑设置条**——`插件设置 · 启用插件[启用] · 侧边栏入口[显示] · 访问策略[开启] · 自动更新[7]天[保存] · ⓘ说明`;弹窗与手机是紧凑设置行。字号与开关都收小,让**连接列表**成为页面主体。控件用 DSH 自己的开关(跟随主题 / 皮肤,带汉字状态词);解释性长文字都在「ⓘ
|
|
166
|
+
- **设置区(DSH 主体样式,刻意弱化)**:宽屏把四项收成**一行紧凑设置条**——`插件设置 · 启用插件[启用] · 侧边栏入口[显示] · 访问策略[开启] · 自动更新[7]天[保存] · ⓘ说明`;弹窗与手机是紧凑设置行。字号与开关都收小,让**连接列表**成为页面主体。控件用 DSH 自己的开关(跟随主题 / 皮肤,带汉字状态词);解释性长文字都在「ⓘ 说明」里。
|
|
169
167
|
- **只读开关(三种布局都在外面)**:每条连接行的数据源旁边就是该环境的「只读」小开关,点一下即改(开锁图标 + 「可写」= 可写;闭锁图标 + 「只读」= 已只读),不必打开编辑表单;弹窗 / 手机同样可用。编辑表单里的勾选也保留,两处是同一个状态。**连接串本身不再带锁形图标**——过去容易被误读成"需要密码",也和只读锁冲突。
|
|
170
|
-
- **增删改**:行内 ✏ 改连接串或环境名;「+
|
|
171
|
-
-
|
|
168
|
+
- **增删改**:行内 ✏ 改连接串或环境名;「+ 添加连接」支持中文环境名。**同名环境保存前会先问「是否覆盖」**,取消则一个字都不写。**删除也要先确认**(这条不可撤销,取消同样零写入)。
|
|
169
|
+
- **写失败一定看得见**:保存 / 开关 / 删除被拒(401、离线、宿主拒绝)时页面上会出现一行错误提示,**不会**再显示「已保存 ✓」,改好后再点一次就是重试;读取不到插件状态时也会给出错误行 + 「重试」,不会悄悄变成"没有连接"。「自动更新间隔」只接受 0–365 的整数天,留空或填小数会当场提示、不发请求。
|
|
170
|
+
- **连接测试**:按该环境**真正会被使用的有效连接**(含只读 / 隧道)执行 `SELECT 1`,测试中显示已耗时。结果是一次性反馈,不持久化、不改变连接状态。命令没能送到宿主(例如桥接 401 / 离线)时会立刻说「测试命令未能送达宿主」,而不是等 30 秒后报「测试超时」。
|
|
172
171
|
|
|
173
172
|
**「在侧边栏显示入口」不是禁用插件**:关掉它只是隐藏左侧栏的快捷入口,插件照常工作、模型照常可以查询;想整体停用才用「禁用」(禁用后入口一律隐藏)。无论哪种状态,**设置页与插件页那行的「配置」始终能进**,随时可以撤销。
|
|
174
173
|
|
|
175
|
-
**DSH
|
|
174
|
+
**DSH 版本过低时**:配置页顶部会多出一条警告(写明「需要 DSH 0.2.1 或更高版本」与本机版本),页面、连接、只读开关与工具全部照常工作——这不是"插件坏了",只需升级 DSH。
|
|
176
175
|
|
|
177
176
|
## 🔒 只读模式与 SSH 隧道
|
|
178
177
|
|
|
@@ -238,14 +237,18 @@ dsh plugin --profile desktop remove dsh-dbhub-live
|
|
|
238
237
|
| 只读 / SSH 控件是灰的,或提示 dbhub 版本过旧 | 两项都需要 dbhub 1.4.0+:升级 dbhub,或删除 `~/.dsh/storages/dsh-dbhub-live` 让插件自动装一个满足要求的版本。 |
|
|
239
238
|
| 连接测试失败,看不出原因 | 读提示的**末尾**(诊断给的是 dbhub 输出的尾部,会标出【SSH 层】/【数据库层】/【插件层】)。**报【数据库层】就说明隧道已经通了**;有隧道时先点「测试 SSH 隧道」把 SSH 层单独验一遍。 |
|
|
240
239
|
| 看不到配置页 | 确认插件已安装:web 端重启过 `dsh web`;桌面端在「插件」页该行点「配置」,或从左侧菜单的 DBHub 入口进。配置页只在 Web / 桌面界面显示,终端环境下不影响工具使用。 |
|
|
241
|
-
|
|
|
240
|
+
| 配置页顶部提示「DSH 版本过旧」 | 插件最低支持 **DSH 0.2.1**(含其预发布版);升级 dsh 后重启即可。升级前工具与页面仍可用,提示只是提醒。 |
|
|
242
241
|
| 页头没有出现「升级到 …」提示 | 可能已是最新版本,或这次检查没连上 npm registry / GitHub Releases;点「检查更新」重试即可。检查失败只是提示,不影响插件功能。 |
|
|
243
242
|
| 桌面端怎么升级 | 在 **左侧菜单 → 插件** 页卸载后重装最新版本;连接与凭据在 `~/.dsh/storages/dsh-dbhub-live`,重装后仍在。 |
|
|
244
|
-
| 升级 dsh 后提示插件与本版不兼容 |
|
|
243
|
+
| 升级 dsh 后提示插件与本版不兼容 | 先确认 dsh ≥ 0.2.1,再把插件升级到最新版;用 `allow-version` 强行豁免没有意义。 |
|
|
245
244
|
| 不希望自动更新 dbhub | 配置页「自动更新间隔(天)」填 `0` 保存,或设环境变量 `DSH_DBHUB_UPDATE_DAYS=0`。 |
|
|
246
245
|
|
|
247
246
|
**环境变量**(仅这两项,不在 UI 暴露):`DSH_DBHUB_PACKAGE`(自动安装的包名,默认 `@bytebase/dbhub`)、`DSH_DBHUB_UPDATE_DAYS`(自动更新间隔种子,默认 `7`,被配置页保存的值覆盖)。
|
|
248
247
|
|
|
248
|
+
## 版本历史
|
|
249
|
+
|
|
250
|
+
每个正式版本的变更摘要见 [doc/CHANGELOG.md](https://github.com/mr-mihu/dsh-dbhub-live/blob/dev/doc/CHANGELOG.md);逐版本的发布说明在 [GitHub Releases](https://github.com/mr-mihu/dsh-dbhub-live/releases)。
|
|
251
|
+
|
|
249
252
|
## 许可证
|
|
250
253
|
|
|
251
254
|
[MIT](./LICENSE)
|
package/lib/adhoc.mjs
CHANGED
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
// file holds placeholders only; the real secrets
|
|
19
19
|
// ride the CHILD's environment (see toml.mjs).
|
|
20
20
|
|
|
21
|
-
import { DATA_DIR, connLabel, scrubSecrets, normalizeSshOptions, expandHome, withDefaultSshKey, DEFAULT_SSH_KEY_PATH } from './config.mjs'
|
|
21
|
+
import { DATA_DIR, connLabel, scrubSecrets, normalizeSshOptions, expandHome, withDefaultSshKey, DEFAULT_SSH_KEY_PATH, warnOnce } from './config.mjs'
|
|
22
22
|
import { buildSpawnArgv, resolveDbhubExe } from './runtime.mjs'
|
|
23
|
-
import { createMcpClient, disabledMessage } from './mcp.mjs'
|
|
23
|
+
import { createMcpClient, disabledMessage, MCP_INIT_TIMEOUT_MS, MCP_CALL_TIMEOUT_MS } from './mcp.mjs'
|
|
24
24
|
import { buildDbhubToml, writeTempToml, removeTempToml } from './toml.mjs'
|
|
25
25
|
import { detectDbhubCapabilities } from './capability.mjs'
|
|
26
|
+
import { readPluginVersion } from './meta.mjs'
|
|
26
27
|
import { currentT } from './i18n.mjs'
|
|
28
|
+
import { StringDecoder } from 'node:string_decoder'
|
|
27
29
|
import { existsSync } from 'node:fs'
|
|
28
30
|
import { connect } from 'node:net'
|
|
29
31
|
import * as state from './state.mjs'
|
|
@@ -70,6 +72,14 @@ export function secretsOf(conn) {
|
|
|
70
72
|
export const PROBE_TIMEOUT_MS = 25000
|
|
71
73
|
export const PROBE_TIMEOUT_SSH_MS = 45000
|
|
72
74
|
|
|
75
|
+
/**
|
|
76
|
+
* How long a finished call waits for the child to actually exit before it stops
|
|
77
|
+
* waiting and simply warns. `waitForExit` never settles when a descendant escaped
|
|
78
|
+
* the managed process group (the host documents that as out of its control), and
|
|
79
|
+
* an unbounded await there would hang the call the timeouts above just bounded.
|
|
80
|
+
*/
|
|
81
|
+
export const EXIT_WAIT_MS = 5000
|
|
82
|
+
|
|
73
83
|
/** Host-side probe cap for one connection (tunnelled connections get more). */
|
|
74
84
|
export function hostTimeoutFor(conn) {
|
|
75
85
|
return needsToml(conn) && normalizeConn(conn).ssh ? PROBE_TIMEOUT_SSH_MS : PROBE_TIMEOUT_MS
|
|
@@ -383,11 +393,18 @@ export async function probeSshTunnel(subprocess, ssh, timeoutMs) {
|
|
|
383
393
|
|
|
384
394
|
/**
|
|
385
395
|
* Run ONE MCP tool call against a disposable dbhub process.
|
|
396
|
+
*
|
|
397
|
+
* Both requests carry a request-level budget (see lib/mcp.mjs): `exec.signal` only
|
|
398
|
+
* fires on a user cancel / turn interruption, so a dbhub that is alive but silent
|
|
399
|
+
* would otherwise hold this call — and the whole conversation round — forever.
|
|
400
|
+
* `exec.mcpTimeoutMs` shrinks the CALL budget (the robustness test proves the
|
|
401
|
+
* timeout path in milliseconds); production passes no value.
|
|
402
|
+
*
|
|
386
403
|
* @param subprocess - host subprocess service.
|
|
387
404
|
* @param conn - `{ dsn, ro, ssh }` (a bare DSN string means "no options").
|
|
388
405
|
* @param rawName - the MCP tool name (`execute_sql` / `search_objects`).
|
|
389
406
|
* @param mcpArgs - the tool arguments.
|
|
390
|
-
* @param exec - `{ signal, agent? }`.
|
|
407
|
+
* @param exec - `{ signal, agent?, mcpTimeoutMs? }`.
|
|
391
408
|
* @param label - optional pre-localized header line.
|
|
392
409
|
* @returns `{ ok, text }` (already scrubbed and metadata-labelled).
|
|
393
410
|
*/
|
|
@@ -422,7 +439,14 @@ export async function runAdhoc(subprocess, conn, rawName, mcpArgs, exec, label)
|
|
|
422
439
|
const built = buildDbhubToml({ dsn: c.dsn, ro: c.ro, ssh: c.ssh })
|
|
423
440
|
tomlPath = writeTempToml(built.text)
|
|
424
441
|
// Secrets travel in the CHILD's environment; the file only holds `${VAR}`.
|
|
425
|
-
|
|
442
|
+
// ONLY the generated names: spreading `process.env` here would hand the
|
|
443
|
+
// spawn a scrubbed-away ambient environment all over again (the host
|
|
444
|
+
// subprocess service strips `*KEY|PASSWORD|SECRET|TOKEN*` and `DSH_*` from
|
|
445
|
+
// the inherited env, and merges an explicit `env` AFTER that scrub), so the
|
|
446
|
+
// child — and every third-party package inside dbhub — could read the
|
|
447
|
+
// agent's own API keys. Absent names simply resolve from the scrubbed
|
|
448
|
+
// parent env the service provides.
|
|
449
|
+
childEnv = { ...built.env }
|
|
426
450
|
argv = ['--transport', 'stdio', '--config', tomlPath]
|
|
427
451
|
} catch (e) {
|
|
428
452
|
removeTempToml(tomlPath)
|
|
@@ -447,17 +471,30 @@ export async function runAdhoc(subprocess, conn, rawName, mcpArgs, exec, label)
|
|
|
447
471
|
return { ok: false, text: currentT('result.dbhubSpawnFailed', { msg: String((e && e.message) || e) }) }
|
|
448
472
|
}
|
|
449
473
|
let stderrTail = ''
|
|
474
|
+
const callTimeoutMs = Number.isFinite(exec && exec.mcpTimeoutMs) && exec.mcpTimeoutMs > 0
|
|
475
|
+
? exec.mcpTimeoutMs
|
|
476
|
+
: MCP_CALL_TIMEOUT_MS
|
|
450
477
|
try {
|
|
478
|
+
// Decode across chunk boundaries: dbhub's banner and connection errors are
|
|
479
|
+
// often non-ASCII, and a split multibyte character would surface as U+FFFD in
|
|
480
|
+
// the diagnostic tail the user has to read.
|
|
481
|
+
const stderrDecoder = new StringDecoder('utf8')
|
|
451
482
|
const client = createMcpClient(handle, (chunk) => {
|
|
452
|
-
|
|
483
|
+
const piece = typeof chunk === 'string' ? chunk : stderrDecoder.write(chunk)
|
|
484
|
+
stderrTail = (stderrTail + piece).slice(-2000)
|
|
453
485
|
})
|
|
454
486
|
await client.request('initialize', {
|
|
455
487
|
protocolVersion: '2025-03-26',
|
|
456
488
|
capabilities: {},
|
|
457
|
-
|
|
458
|
-
|
|
489
|
+
// dbhub logs this in its own diagnostics. The literal '3.0.0' that used to
|
|
490
|
+
// sit here named a version this plugin never was — a report from a user on
|
|
491
|
+
// 5.x pointed the maintainer at the wrong code. `meta` is a LEAF, so reading
|
|
492
|
+
// the installed version here cannot create a cycle; a detached copy of the
|
|
493
|
+
// package (no readable package.json) degrades to '0.0.0'.
|
|
494
|
+
clientInfo: { name: 'dsh-dbhub-adhoc', version: readPluginVersion() || '0.0.0' },
|
|
495
|
+
}, { timeoutMs: MCP_INIT_TIMEOUT_MS })
|
|
459
496
|
client.notify('notifications/initialized')
|
|
460
|
-
const res = await client.request('tools/call', { name: rawName, arguments: mcpArgs })
|
|
497
|
+
const res = await client.request('tools/call', { name: rawName, arguments: mcpArgs }, { timeoutMs: callTimeoutMs })
|
|
461
498
|
const textOf = (r) => {
|
|
462
499
|
if (r && Array.isArray(r.content)) {
|
|
463
500
|
const parts = []
|
|
@@ -469,16 +506,45 @@ export async function runAdhoc(subprocess, conn, rawName, mcpArgs, exec, label)
|
|
|
469
506
|
// Zero-knowledge surface: the header carries metadata only (host/port/db),
|
|
470
507
|
// and every line — including dbhub's own error text — is scrubbed before
|
|
471
508
|
// it can reach the model (the SSH password / passphrase included).
|
|
472
|
-
|
|
509
|
+
// The prefix itself goes through the same scrubber: it is built from
|
|
510
|
+
// `connLabel` AND from a caller-supplied `label`, and a caller that ever
|
|
511
|
+
// pastes a DSN into it must not bypass the gate the body passes through.
|
|
512
|
+
const header = label !== undefined && label !== null
|
|
473
513
|
? String(label)
|
|
474
|
-
: currentT('label.adhocConn', { dsn: connLabel(c.dsn) })
|
|
514
|
+
: currentT('label.adhocConn', { dsn: connLabel(c.dsn) })
|
|
515
|
+
const prefix = scrubSecrets(header, c.dsn, extraSecrets) + '\n'
|
|
475
516
|
if (res && res.isError) return { ok: false, text: prefix + scrubSecrets(textOf(res), c.dsn, extraSecrets) }
|
|
476
517
|
if (res && res.structuredContent !== undefined) {
|
|
477
518
|
return { ok: true, text: prefix + scrubSecrets(JSON.stringify(res.structuredContent, null, 2), c.dsn, extraSecrets) }
|
|
478
519
|
}
|
|
479
520
|
return { ok: true, text: prefix + scrubSecrets(textOf(res), c.dsn, extraSecrets) }
|
|
480
521
|
} catch (e) {
|
|
481
|
-
|
|
522
|
+
const code = e && e.code
|
|
523
|
+
let detail
|
|
524
|
+
if (code === 'MCP_TIMEOUT' || code === 'MCP_TOO_LARGE') {
|
|
525
|
+
// Kill the wedged process BEFORE the report is assembled: `finally` runs
|
|
526
|
+
// after this frame, and a dbhub that ignored one request will ignore the
|
|
527
|
+
// next. The temp TOML is still removed by `finally`.
|
|
528
|
+
try {
|
|
529
|
+
handle.terminate()
|
|
530
|
+
} catch (err) {
|
|
531
|
+
/* ignore */
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (code === 'MCP_TIMEOUT') {
|
|
535
|
+
const sec = Math.max(1, Math.round((Number(e.timeoutMs) || 0) / 1000))
|
|
536
|
+
detail = currentT('result.mcpTimeout', { sec: String(sec) })
|
|
537
|
+
} else if (code === 'MCP_TOO_LARGE') {
|
|
538
|
+
const limitBytes = Number(e.limitBytes) || 0
|
|
539
|
+
detail = currentT('result.mcpTooLarge', {
|
|
540
|
+
what: currentT(e.limitKind === 'line' ? 'result.mcpTooLargeLine' : 'result.mcpTooLargeTotal'),
|
|
541
|
+
limit: String(Math.round((limitBytes / (1024 * 1024)) * 10) / 10) + ' MiB',
|
|
542
|
+
})
|
|
543
|
+
} else {
|
|
544
|
+
detail = String((e && e.message) || e)
|
|
545
|
+
}
|
|
546
|
+
// Never echo a query/params/DSN: only the localized sentence plus (already
|
|
547
|
+
// length-capped) stderr, which the scrubber below rewrites.
|
|
482
548
|
if (stderrTail) detail += '\n[dbhub stderr] ' + stderrTail
|
|
483
549
|
return { ok: false, text: scrubSecrets(detail, c.dsn, extraSecrets) }
|
|
484
550
|
} finally {
|
|
@@ -487,10 +553,40 @@ export async function runAdhoc(subprocess, conn, rawName, mcpArgs, exec, label)
|
|
|
487
553
|
} catch (e) {
|
|
488
554
|
/* ignore */
|
|
489
555
|
}
|
|
556
|
+
let exited
|
|
557
|
+
// The host contract answers `false` when the signal was already aborted and
|
|
558
|
+
// the process is STILL alive. The return value used to be dropped, so the
|
|
559
|
+
// temp config was deleted as if the child had exited (Windows: EBUSY) and
|
|
560
|
+
// the only trace was a once-per-process warnOnce.
|
|
561
|
+
//
|
|
562
|
+
// The wait itself is bounded: a descendant that escaped the managed process
|
|
563
|
+
// group can make `waitForExit` never settle, which would hang the very call
|
|
564
|
+
// this batch set out to make bounded. A timeout here is treated exactly like
|
|
565
|
+
// `false` (try once more, then warn).
|
|
566
|
+
let exitTimer = null
|
|
567
|
+
const exitWaitMs = Number.isFinite(exec && exec.exitWaitMs) && exec.exitWaitMs > 0
|
|
568
|
+
? exec.exitWaitMs
|
|
569
|
+
: EXIT_WAIT_MS
|
|
490
570
|
try {
|
|
491
|
-
await
|
|
571
|
+
exited = await Promise.race([
|
|
572
|
+
Promise.resolve(handle.waitForExit(exec.signal)).catch(() => undefined),
|
|
573
|
+
new Promise((resolve) => {
|
|
574
|
+
exitTimer = setTimeout(() => resolve('timeout'), exitWaitMs)
|
|
575
|
+
}),
|
|
576
|
+
])
|
|
492
577
|
} catch (e) {
|
|
493
578
|
/* ignore */
|
|
579
|
+
} finally {
|
|
580
|
+
if (exitTimer) clearTimeout(exitTimer)
|
|
581
|
+
}
|
|
582
|
+
if (exited === false || exited === 'timeout') {
|
|
583
|
+
try {
|
|
584
|
+
handle.terminate()
|
|
585
|
+
} catch (e) {
|
|
586
|
+
/* ignore */
|
|
587
|
+
}
|
|
588
|
+
// No path, no DSN: just the fact (said once per process).
|
|
589
|
+
warnOnce('adhoc-exit-pending', currentT('result.processStillRunning'))
|
|
494
590
|
}
|
|
495
591
|
// Best effort: the temp file holds no secret, but it must not accumulate.
|
|
496
592
|
removeTempToml(tomlPath)
|