dsh-dbhub-live 3.0.0 → 3.1.2
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 +21 -10
- package/README.en.md +162 -0
- package/README.md +56 -28
- package/doc/REQUIREMENTS.md +23 -3
- package/lib/adhoc.mjs +96 -80
- package/lib/client.js +498 -71
- package/lib/config.mjs +142 -16
- package/lib/i18n.mjs +139 -0
- package/lib/index.mjs +165 -37
- package/lib/mcp.mjs +114 -128
- package/lib/options.mjs +94 -0
- package/lib/runtime.mjs +14 -16
- package/lib/tools.mjs +461 -303
- package/package.json +6 -5
- package/test/adhoc.test.mjs +29 -0
- package/test/client-format.test.mjs +49 -1
- package/test/i18n.test.mjs +27 -0
- package/test/init.test.mjs +162 -105
- package/test/options.test.mjs +41 -0
package/AGENTS.md
CHANGED
|
@@ -10,15 +10,16 @@ DSH host 插件(Node/E SM)+ Web client 插件(浏览器半):把 dbhub
|
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
lib/
|
|
13
|
-
index.mjs 入口:name/inject/apply、状态命名空间(schemastery
|
|
14
|
-
config.mjs 路径/持久化(store,runtime)/DSN
|
|
13
|
+
index.mjs 入口:name/inject/apply、状态命名空间(schemastery)接线、懒加载编排、配置变更应用
|
|
14
|
+
config.mjs 路径/持久化(store,runtime)/DSN 解析/工作区发现/store v2(environments)助手/小工具
|
|
15
15
|
state.mjs 运行时状态机:enabled/phase/toolCount/lastError/mode + 订阅发布
|
|
16
|
+
options.mjs 可配置参数:updateIntervalDays/idleMinutes(设置UI > 环境默认;dbhubPackage 仅内部环境knob)
|
|
16
17
|
runtime.mjs dbhub 可执行文件发现、按需自动安装、定期自动更新、孤儿进程清理
|
|
17
|
-
mcp.mjs MCP JSON-RPC 客户端 + 常驻多源 dbhub 服务生命周期 +
|
|
18
|
+
mcp.mjs MCP JSON-RPC 客户端 + 常驻多源 dbhub 服务生命周期 + collectSources(工作区×环境) + 摘要缓存
|
|
18
19
|
adhoc.mjs 临时连接(每次调用一条一次性 dbhub 进程)
|
|
19
20
|
collect.mjs 授权扫描项目配置文件并提取 DSN 候选(含 askUser 桥接)
|
|
20
|
-
tools.mjs host 自有工具定义与注册(dbhub_configure
|
|
21
|
-
client.js Web 半(手写 lazy-CJS bundle
|
|
21
|
+
tools.mjs host 自有工具定义与注册(dbhub_configure 支持 env 参数)
|
|
22
|
+
client.js Web 半(手写 lazy-CJS bundle):折叠分区卡片(状态/配置/工作区连接)
|
|
22
23
|
test/ node:test 单元测试(纯逻辑 + client bundle 格式契约)
|
|
23
24
|
doc/ 需求文档
|
|
24
25
|
cordis.patch.yml bundle patch:`name: dsh-dbhub-live` 挂载本包
|
|
@@ -27,8 +28,10 @@ cordis.patch.yml bundle patch:`name: dsh-dbhub-live` 挂载本包
|
|
|
27
28
|
## 关键架构事实
|
|
28
29
|
|
|
29
30
|
- **模块依赖只进不出**:`config ← state ← runtime ← mcp ← tools ← index`,`adhoc ← mcp`,`collect ← tools`;禁止反向 or 循环 import(HMR/装载顺序依赖它)。
|
|
30
|
-
-
|
|
31
|
-
-
|
|
31
|
+
- **状态命名空间**:Host 注册 `dsh-dbhub-live` 命名空间(`ctx.inject(['settings'], …)` + schema);Web 端 Plugins 选项卡按命名空间分发 `settings.plugin.item` 卡片。命名空间值 = 状态 `{enabled, phase, toolCount, lastError, mode}` + 配置 `{updateIntervalDays, idleMinutes}` + `{serverUp}` + `{workspaces(JSON 掩码摘要)}` + `{configOp}`(主机消费后自动清空)。**注入回调是独立作用域:内部需要的服务(如 `subprocess`)必须用 `ctx.get('subprocess')` 就地获取,绝不能引用 `apply()` 的局部变量——否则 ReferenceError 会静默杀死整条发布/配置链路(卡片只剩静态值、工作区连接不刷新)**。回调整体套 `wrap()` 防护,异常必须打日志。
|
|
32
|
+
- **可配置参数**:`options.mjs` 只此一处持有部署开关(`dbhubPackage` 仅环境变量/内部,不进设置);watch 把卡片写入的字段经 `options.applyPatch` 应用到运行时。**优先级:用户设置 > 进程环境变量 > 内置默认**。不要绕过 `applyPatch` 直接改 `options` 内部值。
|
|
33
|
+
- **工作区连接管理(configOp 通道)**:store v2 = `{ [wsPath]: { environments: { [env]: {dsn, source, updatedAt} } } }`(v1 单 dsn 条目自动迁移进 `environments.default`)。卡片只读**掩码**摘要(`collectSources` → `latestSummaries`,密码永不出 Host);增/改/删通过命名空间 `configOp` 单向命令下发,`index.mjs` 用 `setWorkspaceEnv`/`removeWorkspaceEnv` 落盘后重扫摘要并发布(configOp 随发布清空,天然防环)。自动发现(mise/.env)只提供未覆盖的 `default`,不持久化。
|
|
34
|
+
- **工具声明数量恒定(上下文预算红线)**:**绝不为每个工作区 × 环境注册独有工具**(否则 2N 个工具声明每轮进模型上下文)。常驻查询只有 `dbhub_execute_sql(source, …)` / `dbhub_search_objects(source, …)` 两个声明,调用时 `resolveSource` 按 source 值(`标题_hash[_环境]`,来自 `dbhub_list_sources`)解析到真实连接并调用 `dbhub_<base>_<sourceId>`。曾实现过的 per-source 注册(syncToolsNow/兜底/升级)已移除,勿回恢复。核心工具全集固定:configure / list_sources / execute_sql / search_objects / query / query_objects。
|
|
32
35
|
- **schema 弹性依赖**:优先用真实 `@deepseek-ai/schemastery` schema(`await import`);解析失败时降级为 `lib/index.mjs` 内建的最小 callable schema(`schema(v)` 合默认值 + `toJSON()`),保证**链路安装(`dsh plugin add <本地目录>`,Node ESM 按源码真实路径解析裸导入)下插件照样启动、卡片照常工作**。tarball/npm 安装(真实目录在 profile node_modules 下)走真实 schemastery 路径。不要把这个 import 改回静态顶层 import——会重新引入链路安装时启动失败。
|
|
33
36
|
- **懒加载**:`apply()` 只注册核心工具 + 后台异步初始化(`startLazyInit`);任何工具调用先 `ensureRunning`(共享 `server.starting`,并发调用自动排队等待);启动失败进入 `phase:'error'` 并记录 `lastError`,下次调用自动重试。**无工作区数据源时恒不拉起 dbhub 进程**(空 `[[sources]]` toml 对 dbhub 是致命的;空指纹若被当成“已同步”会在二次调用时绕过 toml 直接 spawn——`ensureRunning` 的 `sources.length === 0` 早退必须在指纹判断之外)。
|
|
34
37
|
- **启用/禁用**:`state.setEnabled` 持久化到 `credentials.json`(权威值);禁用时立即 `terminateServer()` 释放进程,所有工具 execute 首行返回「插件已禁用」;重新启用触发懒加载初始化。
|
|
@@ -37,8 +40,8 @@ cordis.patch.yml bundle patch:`name: dsh-dbhub-live` 挂载本包
|
|
|
37
40
|
## 存储与容错(初始化即处理)
|
|
38
41
|
|
|
39
42
|
- **实例隔离**:所有持久化都在 `$DSH_HOME/storages/dsh-dbhub-live/`(`credentials.json` 凭据+enabled、`runtime.json`、`dbhub.toml`、`dbhub-runtime/` 自动安装前缀)。隔离粒度 = `DSH_HOME`(同一 home 的多个 profile 共享,与 dsh 自身 workspace.json 约定一致);dbhub 进程、状态机、工具注册天然按进程隔离。**进程环境变量不参与连接解析**。
|
|
40
|
-
- **升级/手改遗留兼容**:`loadStore`/`loadRuntime` 先用纯函数 `normalizeStore`/`normalizeRuntime` 清洗:丢弃非布尔 `enabled`、非对象/空 dsn 条目、非法 `dbhubExe`/`dbhubInstallAt`;`dsn` 统一 trim
|
|
41
|
-
- **空值安全**:解析器对缺失/空值全部有兜底(`
|
|
43
|
+
- **升级/手改遗留兼容**:`loadStore`/`loadRuntime` 先用纯函数 `normalizeStore`/`normalizeRuntime` 清洗:丢弃非布尔 `enabled`、非对象/空 dsn 条目、非法 `dbhubExe`/`dbhubInstallAt`;`dsn` 统一 trim;v1 单 dsn 条目自动迁移为 `environments.default`;**未知字段保留**(向前兼容)。清洗结果与原文不同时**一次性回写迁移**,之后每次启动都是规范化文件。
|
|
44
|
+
- **空值安全**:解析器对缺失/空值全部有兜底(`resolveWorkspaceEnvs` 判空、`maskDsn` 对不可解析 DSN 正则兜底、状态 schema 默认值、settings 镜像的 `enabled` 只认布尔),清洗后不存在半吊子条目。
|
|
42
45
|
- **运行目录被删 / 写入被拦截**:每次 JSON/toml 写入前自动 `mkdirSync` 重建目录;写入失败**不抛致命**,`warnOnce` 一次性告警并继续内存态运行(凭据持久化失效但工具可用);`dbhub.toml` 写入失败按**初始化错误**记录(状态卡片 🔴 + `lastError`)并下次调用自动重试;npm 自动安装前同样重建目录。注意:以上全是 best-effort,被拦截时重启会丢「仅内存态」的修改,属预期。
|
|
43
46
|
|
|
44
47
|
## 调试方法(不影响正在运行的 Harness)
|
|
@@ -79,8 +82,16 @@ npm test # 单元测试(node:test;沙箱内请逐个文件跑:node
|
|
|
79
82
|
|
|
80
83
|
发布前:按「调试方法」第 3 步在隔离实例完整冷启动一遍,确认 Host 无报错、`/plugins/dsh-dbhub-live/client.js` 可访问。
|
|
81
84
|
|
|
85
|
+
## 版本号策略(内部调试 ≠ 发布)
|
|
86
|
+
|
|
87
|
+
- **每次内部调试迭代都升小版本**(如 `3.1.1-dev.1`、`3.1.1-dev.2`、`3.1.1-dev.3`…),`pnpm pack` 产物随之换名;
|
|
88
|
+
- 原因:pnpm 对 `dsh plugin add <同名同版本 tarball>` 会判「Already up to date」跳过、**不换包**;升版本号才能保证调试包真正部署(曾因同版本 tgz 导致连换两轮都没生效)。
|
|
89
|
+
- `dsh plugin add` 后务必用 `dsh plugin ls` 或核对 `node_modules/<pkg>/package.json` 的版本号确认已替换。
|
|
90
|
+
- **发布时**再升级发布版本(如调试到 `3.1.1-dev.N` 后,正式版打 `3.1.2`),tag/npm 一律用正式版本号。
|
|
91
|
+
|
|
82
92
|
## 约定
|
|
83
93
|
|
|
84
94
|
- 产品文案中文、代码注释英文;密码脱敏不可绕过;扫描必须经 `askUser` 授权。
|
|
85
95
|
- `state.*` 之外不要直接改 `store`/`runtime` 之外的持久化。
|
|
86
|
-
- 增加行为时同步更新本文件、README(用户侧)与 doc/REQUIREMENTS.md(业务侧)。
|
|
96
|
+
- 增加行为时同步更新本文件、README(用户侧)与 doc/REQUIREMENTS.md(业务侧)。
|
|
97
|
+
- README 双语同步:`README.md`(中文)为唯一真源,`README.en.md` 由 AI 从最新中文派生——改动任一侧必须同次更新另一侧,章节结构一一对应。
|
package/README.en.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# dsh-dbhub-live
|
|
2
|
+
|
|
3
|
+
[简体中文](README.md) | English
|
|
4
|
+
|
|
5
|
+
> Let [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/DeepSeek-Harness) operate databases directly and safely: persistent multi-source connections + per-workspace tools + ad-hoc dynamic connections + lazy loading and a browser status card.
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](#installation)
|
|
9
|
+
[](https://github.com/bytebase/dbhub)
|
|
10
|
+
[](https://www.npmjs.com/package/dsh-dbhub-live)
|
|
11
|
+
[](https://dsh-plugin.org/plugins/mr-mihu/dsh-dbhub-live)
|
|
12
|
+
|
|
13
|
+
`dsh-dbhub-live` is a DSH plugin built on [DBHub](https://dbhub.ai) (a database MCP server) that lets the model query databases directly: reuse persistent connections on configured workspaces, or make one-off connections to any database for ad-hoc investigation.
|
|
14
|
+
|
|
15
|
+
## ✨ Features
|
|
16
|
+
|
|
17
|
+
- **Persistent multi-source service** — a single background dbhub service connects to multiple sources; connections are reused and queries are faster.
|
|
18
|
+
- **Per-workspace tools** — `dbhub_execute_sql_<workspace>` / `dbhub_search_objects_<workspace>`; the tool name is the workspace, multiple workspaces never mix up, and connections are clearly labeled (passwords masked).
|
|
19
|
+
- **Ad-hoc dynamic connections** — `dbhub_query` / `dbhub_query_objects` connect to any database independently on each call and can query several databases in parallel, handy for cross-environment diagnosis.
|
|
20
|
+
- **Lazy-load startup** — plugin startup does not block the GUI and tools are available immediately; environment initialization is deferred to the first call (queries during initialization automatically wait until ready).
|
|
21
|
+
- **Enable / disable switch** — turning it off makes every dbhub tool return a friendly "plugin disabled" message immediately, without a restart; turning it back on takes effect right away.
|
|
22
|
+
- **Browser status card** — the Settings → Plugins → dsh-dbhub-live panel shows in real time: a status badge (🟢 running / 🟡 initializing / 🔴 error), registered tool count, operating mode, and the most recent error (in red), plus the enable/disable switch.
|
|
23
|
+
- **Out of the box** — if `dbhub` is not installed, the plugin installs it automatically on first use and keeps it updated afterwards; no manual steps needed.
|
|
24
|
+
- **Multiple configuration methods** — explicit DSN / fill-in fields / authorized scanning of project config files; credentials stay in your local user directory and passwords are masked end to end.
|
|
25
|
+
|
|
26
|
+
## Supported Data Sources
|
|
27
|
+
|
|
28
|
+
MySQL · PostgreSQL · MariaDB · SQLite · SQL Server
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- DeepSeek Harness's `dsh` CLI (`dsh web` runs the GUI)
|
|
33
|
+
- Node.js ≥ 18 with `npm` recommended — `dbhub` is auto-installed on first use
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Option 1: use a locally installed dsh
|
|
39
|
+
dsh plugin --profile web add dsh-dbhub-live
|
|
40
|
+
|
|
41
|
+
# Option 2: invoke dsh via npx (no global dsh installation required)
|
|
42
|
+
npx @deepseek-ai/dsh plugin --profile web add dsh-dbhub-live
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
After installing, **restart `dsh web`** for it to take effect (you can then see the status card at Settings → Plugins → dsh-dbhub-live).
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
The tools below are invoked automatically by DSH's AI — you don't run them by hand; just state your request in natural language (e.g., "look up the users table") and the AI configures and queries on demand:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
# 1) If the current workspace has no connection yet, the AI configures one first (three methods below under "Configuration Methods")
|
|
53
|
+
dbhub_configure
|
|
54
|
+
|
|
55
|
+
# 2) Run a query on a persistent connection of a configured workspace
|
|
56
|
+
dbhub_execute_sql_myapp SELECT * FROM users LIMIT 10;
|
|
57
|
+
|
|
58
|
+
# 3) Make a one-off connection to any database
|
|
59
|
+
dbhub_query dsn=mysql://user:pass@127.0.0.1:3306/mydb sql="SHOW TABLES;"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Tools
|
|
63
|
+
|
|
64
|
+
| Tool | Description |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `dbhub_configure(workspace?, env?, dsn?)` | Configure/persist a database connection for a workspace (environment name optional, default `default`). |
|
|
67
|
+
| `dbhub_list_sources()` | List all registered connection sources (workspace × environment, masked connection strings, origin, and the corresponding **source** value); **masked strings are for identification only and cannot be used for direct connections**. |
|
|
68
|
+
| `dbhub_execute_sql(source, sql)` | Execute SQL on a source's persistent connection; `source` comes from `dbhub_list_sources` (the default environment has no suffix; named environments look like `…_test`). |
|
|
69
|
+
| `dbhub_search_objects(source, object_type, ...)` | Search database objects (tables/views/columns/indexes, etc.) on a given source. |
|
|
70
|
+
| `dbhub_query(dsn, sql)` | Connect to any database temporarily and execute SQL (multiple statements separated by `;`); **a masked `****` password is detected and the call is rejected**, steering you to the persistent tools. |
|
|
71
|
+
| `dbhub_query_objects(dsn, ...)` | Connect to any database temporarily and search database objects; masked passwords are also rejected. |
|
|
72
|
+
|
|
73
|
+
> Persistent tools have a **constant declaration count** (it does not grow with the number of workspaces/environments): every workspace × environment is selected through the `source` parameter of `dbhub_execute_sql` / `dbhub_search_objects` — semantically equivalent to one tool per workspace × environment, but **without using extra context space**.
|
|
74
|
+
|
|
75
|
+
> Note: `search_objects` is only available for SQLite; for MySQL / PostgreSQL etc. use `dbhub_query` directly (e.g., `SHOW TABLES`).
|
|
76
|
+
|
|
77
|
+
### Configuration Methods
|
|
78
|
+
|
|
79
|
+
1. **Explicit DSN** — pass a full connection string, e.g. `mysql://user:pass@host:3306/db`.
|
|
80
|
+
2. **Fill in fields** — fill type / host / port / user / password / database name in order.
|
|
81
|
+
3. **Authorized scan** — after authorization, scan project config files (`.env`, `application*.yml`, `docker-compose`, `jdbc.properties`, etc.) and list candidates (passwords masked) for you to confirm.
|
|
82
|
+
|
|
83
|
+
If a workspace already has `mise env` or `.env` (`DSN` / `DB_*`), the plugin discovers it automatically — no manual configuration needed.
|
|
84
|
+
|
|
85
|
+
## Status Card
|
|
86
|
+
|
|
87
|
+
Settings → Plugins → dsh-dbhub-live: the plugin syncs its running state and configuration to the Web settings panel in real time (only visible on the `dsh web` side). The card uses the **single-row collapsible** style (consistent with the other plugin settings cards):
|
|
88
|
+
|
|
89
|
+
**Collapsed (default)**: one row shows the status badge (🟢 running / 🟡 initializing / 🔴 error / ⚪ disabled), environment count, and the **enable/disable switch** — you can check the status and toggle without expanding.
|
|
90
|
+
|
|
91
|
+
**Expanded** shows three blocks —
|
|
92
|
+
|
|
93
|
+
**Status** (supporting info; the tool declaration count is fixed and no longer the focus):
|
|
94
|
+
|
|
95
|
+
- Resident process: running / pending start (on demand) — shows "pending start" after idle reclamation.
|
|
96
|
+
- Operating mode: lazy loading (initializes on first call).
|
|
97
|
+
- Tool declarations: `6 (fixed)`; environments: `N · M saved`.
|
|
98
|
+
- Most recent error (shown in red on error).
|
|
99
|
+
|
|
100
|
+
**Configuration** (edit, then click "Save Configuration" to apply immediately and persist):
|
|
101
|
+
|
|
102
|
+
| Parameter | Description | Default |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| Auto-update interval (days) | How often dbhub is auto-updated; `0` disables | `7` |
|
|
105
|
+
| Idle reclamation (minutes) | Idle reclamation time of the resident dbhub process | `10` |
|
|
106
|
+
|
|
107
|
+
Precedence: **user settings > process environment variables (default seeds) > built-in defaults**. The auto-installed package is not in the UI (controlled separately by the `DSH_DBHUB_PACKAGE` environment variable, default `@bytebase/dbhub`).
|
|
108
|
+
|
|
109
|
+
**Workspace connections**:
|
|
110
|
+
|
|
111
|
+
- Lists every workspace × environment connection: workspace name, environment name, **source value** (what the model passes to `dbhub_execute_sql`, shown in monospace), **masked** connection string, and origin badge (`saved` / `auto`).
|
|
112
|
+
- `saved`: you configured it (`dbhub_configure` or added in the card).
|
|
113
|
+
- `auto`: not saved, discovered from `mise env` / `.env` — not persisted and follows the source files; if auto-discovery is wrong, use "Edit" to override it with a manual configuration.
|
|
114
|
+
- Each row can be **edited** (override the connection string; an auto item becomes saved) or **deleted** (saved items only).
|
|
115
|
+
- **Multiple environments per workspace**: fill "workspace (path or title, empty = default current workspace) + environment name + connection string" in the form and click "Add Connection". The default environment's source has no suffix; named environments look like `<workspace>_<environment>`.
|
|
116
|
+
|
|
117
|
+
## dbhub Environment Variables
|
|
118
|
+
|
|
119
|
+
| Environment variable | Description | Default |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `DSH_DBHUB_PACKAGE` | npm package name used for auto-install (environment variable only, not exposed in the UI) | `@bytebase/dbhub` |
|
|
122
|
+
| `DSH_DBHUB_UPDATE_DAYS` | Seed for the auto-update interval in days; `0` disables (overridden once saved in the settings card) | `7` |
|
|
123
|
+
|
|
124
|
+
## 🔄 Automatic Installation & Updates
|
|
125
|
+
|
|
126
|
+
- **Auto-install on first use** — when `dbhub` is missing locally, the plugin installs it on the first query; afterwards it also works offline.
|
|
127
|
+
- **Kept up to date automatically** — silently updates to the latest version in the background (interval under "Status Card → configurable parameters"); on failure the existing version is kept.
|
|
128
|
+
- **Never touches your configuration** — a `dbhub` you installed yourself via PATH / mise is left untouched.
|
|
129
|
+
|
|
130
|
+
> The default update interval can also be seeded by an environment variable, see "Status Card → Configuration"; once saved in the settings card, the saved value wins.
|
|
131
|
+
|
|
132
|
+
## Data Location
|
|
133
|
+
|
|
134
|
+
All configuration and credentials live outside the module directory (unaffected by pnpm packaging); deleting the directory clears everything:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
~/.dsh/storages/dsh-dbhub-live/
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Instances are isolated by `DSH_HOME`; multiple profiles of the same instance share it (same convention as dsh's own `workspace.json`). Workspace connections are stored per "workspace × environment" (`environments.default` is the default environment); legacy v1 single-connection entries migrate automatically at startup. The plugin cleans up and migrates legacy config left by upgrades or manual edits in one pass; it does not crash if the runtime directory is deleted or writes are blocked by the system — it recreates the directory, warns once and keeps running in memory when a write fails, and treats a `dbhub.toml` write failure as an initialization error with automatic retry.
|
|
141
|
+
|
|
142
|
+
## Uninstall
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
dsh plugin --profile web remove dsh-dbhub-live
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
| Symptom | Fix |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| "Cannot locate dbhub" on first use | Make sure npm is present and online; offline, install `dbhub` manually and add it to PATH. |
|
|
153
|
+
| Status card shows 🔴 error | Check "Most recent error" in the status card and the `dsh web` logs; a crashed process is auto-restarted on the next call. |
|
|
154
|
+
| Tools say "plugin disabled" | Open Settings → Plugins → dsh-dbhub-live and click "Enable". |
|
|
155
|
+
| Status card not visible | Confirm the plugin is installed and restart `dsh web`; the card only shows in the Web settings panel (`dsh web`) — on terminal environments without the panel, tool usage is unaffected. |
|
|
156
|
+
| No config files found by the scan | `node_modules` / `.git` / `target` / `dist` etc. are skipped by default; use "Enter DSN" or "Fill in fields" instead. |
|
|
157
|
+
| Need a custom dbhub version | Set the `DSH_DBHUB_PACKAGE` environment variable (e.g. `@bytebase/dbhub@1.2.1`) and restart; or delete `~/.dsh/storages/dsh-dbhub-live` and let it reinstall automatically. |
|
|
158
|
+
| Don't want automatic dbhub updates | Set "Auto-update interval (days)" to `0` in the status card and save; or set `DSH_DBHUB_UPDATE_DAYS=0`. |
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
[MIT](./LICENSE)
|
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# dsh-dbhub-live
|
|
2
2
|
|
|
3
|
+
**简体中文** · [English](README.en.md)
|
|
4
|
+
|
|
3
5
|
> 让 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/DeepSeek-Harness) 直接、安全地操作数据库:常驻多源连接 + 按工作区工具 + 临时动态连接 + 懒加载与浏览器状态卡片。
|
|
4
6
|
|
|
5
7
|
[](https://opensource.org/licenses/MIT)
|
|
6
8
|
[](#安装)
|
|
7
9
|
[](https://github.com/bytebase/dbhub)
|
|
10
|
+
[](https://www.npmjs.com/package/dsh-dbhub-live)
|
|
11
|
+
[](https://dsh-plugin.org/zh/plugins/mr-mihu/dsh-dbhub-live)
|
|
8
12
|
|
|
9
13
|
`dsh-dbhub-live` 是一个 DSH 插件,基于 [DBHub](https://dbhub.ai)(数据库 MCP 服务器)让模型直接查询数据库:既能在已配置的工作区上复用常驻连接,也能临时连任意库做一次性排查。
|
|
10
14
|
|
|
@@ -42,26 +46,31 @@ npx @deepseek-ai/dsh plugin --profile web add dsh-dbhub-live
|
|
|
42
46
|
|
|
43
47
|
## 快速开始
|
|
44
48
|
|
|
49
|
+
以下工具由 DSH 的 AI 自动调用,你不用手动执行——直接用自然语言提出需求即可(如「查一下 users 表」),AI 会按需完成配置与查询:
|
|
50
|
+
|
|
45
51
|
```text
|
|
46
|
-
# 1)
|
|
52
|
+
# 1) 若当前工作区还没有连接,AI 先为其配置数据库连接(三种方式见下方「配置方式」)
|
|
47
53
|
dbhub_configure
|
|
48
54
|
|
|
49
55
|
# 2) 在已配置工作区的常驻连接上执行查询
|
|
50
56
|
dbhub_execute_sql_myapp SELECT * FROM users LIMIT 10;
|
|
51
57
|
|
|
52
58
|
# 3) 临时连任意库做一次性排查
|
|
53
|
-
dbhub_query dsn=mysql://
|
|
59
|
+
dbhub_query dsn=mysql://user:pass@127.0.0.1:3306/mydb sql="SHOW TABLES;"
|
|
54
60
|
```
|
|
55
61
|
|
|
56
62
|
## 工具
|
|
57
63
|
|
|
58
64
|
| 工具 | 说明 |
|
|
59
65
|
| --- | --- |
|
|
60
|
-
| `dbhub_configure(workspace?, dsn?)` |
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
66
|
+
| `dbhub_configure(workspace?, env?, dsn?)` | 为工作区配置/持久化数据库连接(可指定环境名,默认 `default`)。 |
|
|
67
|
+
| `dbhub_list_sources()` | 列出当前已注册的全部连接源(工作区 × 环境、打码连接串、来源与对应 **source 值**);**打码连接串仅供识别,不可用于直连**。 |
|
|
68
|
+
| `dbhub_execute_sql(source, sql)` | 在指定数据源的常驻连接上执行 SQL;`source` 见 `dbhub_list_sources`(默认环境不带后缀,命名环境如 `…_test`)。 |
|
|
69
|
+
| `dbhub_search_objects(source, object_type, ...)` | 在指定数据源搜索数据库对象(表/视图/列/索引等)。 |
|
|
70
|
+
| `dbhub_query(dsn, sql)` | 临时连接任意库执行 SQL(多语句用 `;` 分隔);**检测到 `****` 脱敏密码会直接拒绝**并引导使用常驻工具。 |
|
|
71
|
+
| `dbhub_query_objects(dsn, ...)` | 临时连接任意库搜索数据库对象;同样拒绝脱敏密码。 |
|
|
72
|
+
|
|
73
|
+
> 常驻工具**声明数量恒定**(不随工作区/环境数量增长):全部工作区 × 环境通过 `dbhub_execute_sql` / `dbhub_search_objects` 的 `source` 参数选择,语义等价于每个工作区×环境一个工具,但**不占据多余上下文空间**。
|
|
65
74
|
|
|
66
75
|
> 注:`search_objects` 仅对 SQLite 开放;MySQL / PostgreSQL 等请用 `dbhub_query` 直接查(如 `SHOW TABLES`)。
|
|
67
76
|
|
|
@@ -75,23 +84,50 @@ dbhub_query dsn=mysql://root:pass@192.168.77.6:3306/tx_sd_jinengshu sql="SHOW
|
|
|
75
84
|
|
|
76
85
|
## 状态卡片
|
|
77
86
|
|
|
78
|
-
设置 → 插件 → dsh-dbhub-live
|
|
87
|
+
设置 → 插件 → dsh-dbhub-live:插件会把运行状态与配置实时同步到 Web 设置面板(仅 `dsh web` 端可见)。卡片为**整行折叠**样式(与其它插件设置卡一致):
|
|
88
|
+
|
|
89
|
+
**折叠态(默认)**:一行显示 运行状态徽章(🟢 运行中 / 🟡 初始化中 / 🔴 异常 / ⚪ 已禁用)、环境数量、**启用/禁用开关**——不展开也能看状态、切开关。
|
|
90
|
+
|
|
91
|
+
**展开后**分三块——
|
|
92
|
+
|
|
93
|
+
**状态**(辅助信息,工具声明数固定已非重点):
|
|
79
94
|
|
|
80
|
-
-
|
|
81
|
-
- 已注册工具数(随工作区配置 / 服务同步实时变化)。
|
|
95
|
+
- 常驻进程:运行中 / 待启动(按需)——空闲回收后显示「待启动」。
|
|
82
96
|
- 工作模式:懒加载(首次调用时初始化)。
|
|
83
|
-
-
|
|
97
|
+
- 工具声明:`6 个(固定)`;环境:`N 个 · 已保存 M`。
|
|
98
|
+
- 最近错误(异常时红色展示)。
|
|
99
|
+
|
|
100
|
+
**配置**(编辑后点「保存配置」即时生效并持久化):
|
|
101
|
+
|
|
102
|
+
| 参数 | 说明 | 默认 |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| 自动更新间隔(天) | 自动更新 dbhub 的间隔天数,`0` 关闭 | `7` |
|
|
105
|
+
| 空闲回收(分钟) | 常驻 dbhub 进程空闲回收时长 | `10` |
|
|
106
|
+
|
|
107
|
+
优先级:**用户设置 > 进程环境变量(默认值种子)> 内置默认**。自动安装包不在 UI 中(`DSH_DBHUB_PACKAGE` 环境变量单独控制,默认 `@bytebase/dbhub`)。
|
|
108
|
+
|
|
109
|
+
**工作区连接**:
|
|
110
|
+
|
|
111
|
+
- 列出每个工作区 × 环境的连接:工作区名、环境名、**source 值**(模型调用 `dbhub_execute_sql` 时填这个,等宽字体显示)、**打码**连接串、来源徽章(`已保存` / `自动`)。
|
|
112
|
+
- `已保存`:你配置过(`dbhub_configure` 或卡片添加)。
|
|
113
|
+
- `自动`:未保存,来自 `mise env` / `.env` 自动发现——不持久化,随源文件变化;自动发现不对时可直接「修改」为手动配置覆盖。
|
|
114
|
+
- 每行可「修改」(覆盖连接串,自动项会转成已保存项)或「删除」(仅已保存项)。
|
|
115
|
+
- **同一工作区可添加多个环境**:表单填「工作区(路径或标题,留空=默认当前工作区)+ 环境名 + 连接串」点「添加连接」。默认环境 source 不带后缀,命名环境如 `<工作区>_<环境>`。
|
|
116
|
+
|
|
117
|
+
## dbhub 环境变量
|
|
118
|
+
|
|
119
|
+
| 环境变量 | 说明 | 默认 |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `DSH_DBHUB_PACKAGE` | 自动安装使用的 npm 包名(仅环境变量,不在 UI 暴露) | `@bytebase/dbhub` |
|
|
122
|
+
| `DSH_DBHUB_UPDATE_DAYS` | 自动更新间隔天数种子,`0` 关闭(被设置卡片保存过的值覆盖) | `7` |
|
|
84
123
|
|
|
85
124
|
## 🔄 dbhub 自动安装与更新
|
|
86
125
|
|
|
87
126
|
- **首次使用自动安装**:本机没有 `dbhub` 时,插件会在第一次查询时自动安装,之后离线也可用。
|
|
88
|
-
-
|
|
127
|
+
- **自动保持更新**:后台静默更新到最新版(间隔见「状态卡片 → 可配置参数」),失败则沿用现有版本。
|
|
89
128
|
- **不碰你的配置**:通过 PATH / mise 自行安装的 `dbhub` 不会被插件改动。
|
|
90
129
|
|
|
91
|
-
|
|
92
|
-
| --- | --- | --- |
|
|
93
|
-
| `DSH_DBHUB_PACKAGE` | 自动安装使用的 npm 包名 | `@bytebase/dbhub` |
|
|
94
|
-
| `DSH_DBHUB_UPDATE_DAYS` | 自动更新间隔天数,`0` 关闭 | `7` |
|
|
130
|
+
> 更新间隔默认值也可用环境变量播种,见「状态卡片 → 配置」;一旦在设置卡片保存过,即以设置值为准。
|
|
95
131
|
|
|
96
132
|
## 数据位置
|
|
97
133
|
|
|
@@ -101,7 +137,7 @@ dbhub_query dsn=mysql://root:pass@192.168.77.6:3306/tx_sd_jinengshu sql="SHOW
|
|
|
101
137
|
~/.dsh/storages/dsh-dbhub-live/
|
|
102
138
|
```
|
|
103
139
|
|
|
104
|
-
按 `DSH_HOME` 实例隔离,同一实例的多个 profile 共享(与 dsh 自身 `workspace.json`
|
|
140
|
+
按 `DSH_HOME` 实例隔离,同一实例的多个 profile 共享(与 dsh 自身 `workspace.json` 同一约定)。工作区连接按「工作区 × 环境」存储(`environments.default` 为默认环境);v1 旧格式单连接条目启动时自动迁移。插件对升级/手改遗留的旧格式配置自动清洗并一次性迁移;运行目录被误删或写入被系统拦截时不会崩溃——自动重建目录、写入失败仅告警并继续内存态运行,`dbhub.toml` 写入失败则显示为初始化错误并自动重试。
|
|
105
141
|
|
|
106
142
|
## 卸载
|
|
107
143
|
|
|
@@ -116,18 +152,10 @@ dsh plugin --profile web remove dsh-dbhub-live
|
|
|
116
152
|
| 首次使用报「无法获取 dbhub」 | 确认本机有 npm 且能联网;离线可手动安装 `dbhub` 并加入 PATH。 |
|
|
117
153
|
| 状态卡片显示 🔴 异常 | 查看状态卡片中的「最近错误」与 `dsh web` 日志;进程异常退出时插件会在下次调用自动重启。 |
|
|
118
154
|
| 工具显示「插件已禁用」 | 打开 设置 → 插件 → dsh-dbhub-live 卡片,点击「启用」。 |
|
|
119
|
-
| 看不到状态卡片 | 确认插件已安装并重启 `dsh web
|
|
155
|
+
| 看不到状态卡片 | 确认插件已安装并重启 `dsh web`;状态卡片只在 Web 设置面板(`dsh web`)显示,在无设置面板的终端环境下不影响工具使用。 |
|
|
120
156
|
| 扫描不到配置文件 | 默认跳过 `node_modules` / `.git` / `target` / `dist` 等目录,可改用「输入 DSN」或「填写分项」。 |
|
|
121
|
-
| 需要自定义 dbhub 版本 |
|
|
122
|
-
| 不希望自动更新 dbhub |
|
|
123
|
-
|
|
124
|
-
## 开发与测试(不影响主进程)
|
|
125
|
-
|
|
126
|
-
开发说明见 [AGENTS.md](./AGENTS.md)。推荐的调试路径(详见测试方案 `/插件开发文档/DSH插件测试方案.md`):
|
|
127
|
-
|
|
128
|
-
- **日常改代码** → 静态校验(`npm run check`)+ 单元测试(`npm test`),零风险;
|
|
129
|
-
- **会话内跑通逻辑** → 动态插件 `cordis_define/run` 快速迭代,不重启 Harness;
|
|
130
|
-
- **冷启动验证** → 隔离 `DSH_HOME` 起一个测试实例(如 `dsh web --port 3081`)安装本插件验证启动与客户端 bundle,主实例零影响。
|
|
157
|
+
| 需要自定义 dbhub 版本 | 设置环境变量 `DSH_DBHUB_PACKAGE`(如 `@bytebase/dbhub@1.2.1`)后重启;或删除 `~/.dsh/storages/dsh-dbhub-live` 重新自动安装。 |
|
|
158
|
+
| 不希望自动更新 dbhub | 状态卡片「自动更新间隔(天)」填 `0` 并保存;或设置环境变量 `DSH_DBHUB_UPDATE_DAYS=0`。 |
|
|
131
159
|
|
|
132
160
|
## 许可证
|
|
133
161
|
|
package/doc/REQUIREMENTS.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
### 1. 数据库连接管理
|
|
12
12
|
- **支持的数据库**:MySQL、PostgreSQL、MariaDB、SQLite、SQL Server。
|
|
13
13
|
- **按工作区配置**:每个工作区可配置并持久化专属连接,一次配置长期复用。
|
|
14
|
+
- **同一工作区多环境**:可为一个工作区添加多个环境连接(默认环境 `default` 与命名环境如 prod / dev / test),各环境有独立连接标识与 `source` 值,互不混淆;可在设置卡片中直接查看、新增、修改、删除。
|
|
14
15
|
- **三种配置方式**:
|
|
15
16
|
1. 直接填写完整连接串(DSN);
|
|
16
17
|
2. 按分项填写(类型 / 主机 / 端口 / 账号 / 密码 / 库名);
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
- **安全约束**:凭据仅存本机用户目录;任何展示/结果中的密码一律打码;扫描敏感文件必须先经用户授权。
|
|
20
21
|
|
|
21
22
|
### 2. 查询与对象搜索
|
|
22
|
-
-
|
|
23
|
+
- **按数据源查询**:通过 `dbhub_execute_sql(source, …)` / `dbhub_search_objects(source, …)` 选择工作区与环境(`source` 值见 `dbhub_list_sources`),默认环境不带后缀、命名环境带 `_<环境>` 后缀。**工具声明数量恒定**:不随工作区/环境数量增长,避免占用模型上下文。
|
|
23
24
|
- **临时连接查询**:对任意未配置的库传入连接串即可查询/搜索(多语句用 `;` 分隔),每次调用独立连接,可同时连多个不同库,便于跨环境对比排查。
|
|
24
25
|
- **环境提示**:工具描述和结果中标注目标连接环境(主机/端口/库,密码打码),避免误操作生产库。
|
|
25
26
|
|
|
@@ -41,5 +42,24 @@
|
|
|
41
42
|
- **错误摘要**:最近一次初始化或运行错误简要信息,异常时展示。
|
|
42
43
|
|
|
43
44
|
### 3. 浏览器状态卡片(设置在插件的状态面板)
|
|
44
|
-
-
|
|
45
|
-
-
|
|
45
|
+
- **整行折叠样式**(与其余插件设置卡一致):折叠态一行展示运行状态徽章(🟢 / 🟡 / 🔴 / ⚪)、环境数量与 **启用 / 禁用开关**(不展开即可看状态、切开关);展开后分「状态 / 配置 / 工作区连接」三块。
|
|
46
|
+
- 状态块为辅助信息:常驻进程状态、工作模式(懒加载)、**工具声明数(固定 6,非重点)**、环境计数(N 个 · 已保存 M)、最近错误(红色展示)。
|
|
47
|
+
- 工作区连接块逐行展示:工作区名、环境名、**source 值**(等宽字体,供 `dbhub_execute_sql` 使用)、打码连接串、来源徽章(已保存 / 自动),支持修改 / 删除 / 新增。
|
|
48
|
+
- 启用/禁用开关即时生效。
|
|
49
|
+
|
|
50
|
+
### 4. 可配置参数(插件配置面板内直接编辑)
|
|
51
|
+
- 自动更新间隔天数(0 关闭)、空闲回收时长(分钟):在「配置」分区编辑后点保存,即时生效并持久化。
|
|
52
|
+
- 优先级:用户设置 > 进程环境变量(默认值种子)> 内置默认。
|
|
53
|
+
- 自动安装包不进入 UI:由环境变量 `DSH_DBHUB_PACKAGE` 单独控制(默认 `@bytebase/dbhub`);`DSH_DBHUB_UPDATE_DAYS` 继续作为更新间隔默认值种子。
|
|
54
|
+
|
|
55
|
+
### 5. 工作区连接管理(插件配置面板内查看与增删改)
|
|
56
|
+
- 「工作区连接」分区列出每个工作区 × 环境的连接:工作区名、环境名、打码连接串、来源徽章(已保存 / 自动)。
|
|
57
|
+
- 已保存:经 `dbhub_configure` 或卡片添加持久化于本机。
|
|
58
|
+
- 自动:来自 mise env / .env 自动发现,不持久化、随源文件变化。
|
|
59
|
+
- 支持:新增连接(工作区 + 环境名 + 连接串)、修改连接串(对自动项执行修改即转为已保存的手动覆盖)、删除已保存项。
|
|
60
|
+
- **同一工作区可添加多个环境**:默认环境 `default` 与命名环境(prod / dev / test 等)并存,均通过 `dbhub_execute_sql` / `dbhub_search_objects` 的 `source` 参数选择(source 值见 `dbhub_list_sources`),互不混淆。
|
|
61
|
+
- **上下文占用恒定**:**不为每个工作区 × 环境单独注册工具**——常驻工具声明固定为 6 个,环境再多也不膨胀模型上下文;`source` 在调用时由 Host 解析到真实连接。
|
|
62
|
+
- **变更即时生效**:卡片新增/修改/删除连接后,常驻 dbhub 服务立即重启,下一次查询即可使用新环境的 `source` 值,无需重启实例。
|
|
63
|
+
- **连接源枚举工具**:`dbhub_list_sources` 列出全部已注册连接源(工作区 × 环境、打码连接串、来源与对应 source 值),避免把「测试环境」误当成默认(生产)库。
|
|
64
|
+
- **脱敏连接串不可直连**:`dbhub_list_sources` 中展示的连接串密码为 `****`(仅识别用途);`dbhub_query` / `dbhub_query_objects` 检测到脱敏密码时会直接拒绝并引导使用常驻工具(内置真实凭据),避免用 `****` 白跑一次失败连接。
|
|
65
|
+
- 自动配置与已保存配置并存展示;自动发现与实际不符时可手动修改覆盖。
|
package/lib/adhoc.mjs
CHANGED
|
@@ -1,81 +1,97 @@
|
|
|
1
|
-
// dsh-dbhub-live: ad-hoc temporary connections.
|
|
2
|
-
//
|
|
3
|
-
// Each call spawns a fresh throwaway `dbhub --transport stdio --dsn <dsn>`,
|
|
4
|
-
// runs one MCP tools/call against an ad-hoc target (ip/account/password/db
|
|
5
|
-
// supplied by the model per request), then kills it. Independent per call, so
|
|
6
|
-
// two parallel calls can query two different databases at once. Nothing is
|
|
7
|
-
// persisted and the persistent multi-source server is untouched.
|
|
8
|
-
|
|
9
|
-
import { DATA_DIR, maskDsn } from './config.mjs'
|
|
10
|
-
import { buildSpawnArgv, resolveDbhubExe } from './runtime.mjs'
|
|
11
|
-
import { createMcpClient, disabledMessage } from './mcp.mjs'
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
1
|
+
// dsh-dbhub-live: ad-hoc temporary connections.
|
|
2
|
+
//
|
|
3
|
+
// Each call spawns a fresh throwaway `dbhub --transport stdio --dsn <dsn>`,
|
|
4
|
+
// runs one MCP tools/call against an ad-hoc target (ip/account/password/db
|
|
5
|
+
// supplied by the model per request), then kills it. Independent per call, so
|
|
6
|
+
// two parallel calls can query two different databases at once. Nothing is
|
|
7
|
+
// persisted and the persistent multi-source server is untouched.
|
|
8
|
+
|
|
9
|
+
import { DATA_DIR, maskDsn } from './config.mjs'
|
|
10
|
+
import { buildSpawnArgv, resolveDbhubExe } from './runtime.mjs'
|
|
11
|
+
import { createMcpClient, disabledMessage } from './mcp.mjs'
|
|
12
|
+
import { currentT } from './i18n.mjs'
|
|
13
|
+
import * as state from './state.mjs'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect a surfaced DSN whose password was masked (as in the settings-card
|
|
17
|
+
* summaries). The model sometimes copies a masked DSN into `dbhub_query`;
|
|
18
|
+
* connecting with `****` always fails, so refuse loudly with the right path
|
|
19
|
+
* instead of burning an auth round-trip.
|
|
20
|
+
* @param dsn - the ad-hoc DSN string.
|
|
21
|
+
* @returns whether the password position looks masked (`:**…` before @).
|
|
22
|
+
*/
|
|
23
|
+
export function isMaskedDsn(dsn) {
|
|
24
|
+
return typeof dsn === 'string' && /\/\/[^/@]*:\*{2,}@/.test(dsn)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function runAdhoc(subprocess, dsn, rawName, mcpArgs, exec) {
|
|
28
|
+
if (!state.isEnabled()) {
|
|
29
|
+
return { ok: false, text: disabledMessage() }
|
|
30
|
+
}
|
|
31
|
+
if (!dsn || typeof dsn !== 'string' || !dsn.trim()) {
|
|
32
|
+
return { ok: false, text: currentT('result.dsnMissing') }
|
|
33
|
+
}
|
|
34
|
+
if (isMaskedDsn(dsn)) {
|
|
35
|
+
return { ok: false, text: currentT('result.masked') }
|
|
36
|
+
}
|
|
37
|
+
let exe
|
|
38
|
+
try {
|
|
39
|
+
exe = await resolveDbhubExe(subprocess, exec.signal)
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return { ok: false, text: '无法获取 dbhub: ' + String((e && e.message) || e) }
|
|
42
|
+
}
|
|
43
|
+
let handle
|
|
44
|
+
try {
|
|
45
|
+
handle = subprocess.spawn({
|
|
46
|
+
argv: buildSpawnArgv(subprocess, exe, ['--transport', 'stdio', '--dsn', dsn.trim()]),
|
|
47
|
+
cwd: DATA_DIR,
|
|
48
|
+
stdio: { stdin: 'pipe', stdout: 'pipe', stderr: 'pipe' },
|
|
49
|
+
graceMs: 3000,
|
|
50
|
+
signal: exec.signal,
|
|
51
|
+
})
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return { ok: false, text: '启动临时 dbhub 失败: ' + String((e && e.message) || e) }
|
|
54
|
+
}
|
|
55
|
+
let stderrTail = ''
|
|
56
|
+
try {
|
|
57
|
+
const client = createMcpClient(handle, (c) => {
|
|
58
|
+
stderrTail = (stderrTail + String(c)).slice(-2000)
|
|
59
|
+
})
|
|
60
|
+
await client.request('initialize', {
|
|
61
|
+
protocolVersion: '2025-03-26',
|
|
62
|
+
capabilities: {},
|
|
63
|
+
clientInfo: { name: 'dsh-dbhub-adhoc', version: '3.0.0' },
|
|
64
|
+
})
|
|
65
|
+
client.notify('notifications/initialized')
|
|
66
|
+
const res = await client.request('tools/call', { name: rawName, arguments: mcpArgs })
|
|
67
|
+
const textOf = (r) => {
|
|
68
|
+
if (r && Array.isArray(r.content)) {
|
|
69
|
+
const parts = []
|
|
70
|
+
for (const b of r.content) if (b && typeof b.text === 'string') parts.push(b.text)
|
|
71
|
+
return parts.join('\n')
|
|
72
|
+
}
|
|
73
|
+
return JSON.stringify(r)
|
|
74
|
+
}
|
|
75
|
+
const prefix = currentT('label.adhocConn', { dsn: maskDsn(dsn) }) + '\n'
|
|
76
|
+
if (res && res.isError) return { ok: false, text: textOf(res) }
|
|
77
|
+
if (res && res.structuredContent !== undefined) {
|
|
78
|
+
return { ok: true, text: prefix + JSON.stringify(res.structuredContent, null, 2) }
|
|
79
|
+
}
|
|
80
|
+
return { ok: true, text: prefix + textOf(res) }
|
|
81
|
+
} catch (e) {
|
|
82
|
+
let detail = String((e && e.message) || e)
|
|
83
|
+
if (stderrTail) detail += '\n[dbhub stderr] ' + stderrTail
|
|
84
|
+
return { ok: false, text: detail }
|
|
85
|
+
} finally {
|
|
86
|
+
try {
|
|
87
|
+
handle.terminate()
|
|
88
|
+
} catch (e) {
|
|
89
|
+
/* ignore */
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await handle.waitForExit(exec.signal)
|
|
93
|
+
} catch (e) {
|
|
94
|
+
/* ignore */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
81
97
|
}
|