Skip to content

dsh-plugin-db-explorer

Verified

@hanshu-ai/dsh-plugin-db-explorer · v0.2.0 · MIT · Web UI

DeepSeek Harness plugin for read-only database exploration: schema, table sizes, row counts, column distributions, and inferred table relationships (SQLite, PostgreSQL, MySQL, SQL Server, Oracle). Includes a local SQLite e-commerce demo and a Web GUI page

Install

dsh plugin add @hanshu-ai/dsh-plugin-db-explorer

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

@hanshu-ai/dsh-plugin-db-explorer

一个面向 DeepSeek Harness(DSH)的只读数据库探索插件。它把数据库目录、表结构、数据分布和表间关联整理成模型可直接推理的 Markdown 报告,并注册为一组 db_* 工具。

支持:

数据库 驱动 额外依赖 说明
SQLite Node 内置 node:sqlite 无 开发验证目标;支持 dbstat 精确到表和索引的空间统计
PostgreSQL pg pg 使用 pg_* 系统目录与 information_schema
MySQL / MariaDB mysql2 mysql2 使用 information_schema
SQL Server mssql mssql 使用 sys.* 目录视图
Oracle oracledb oracledb 使用 ALL_* 数据字典视图

安全边界:插件默认以只读方式打开数据库(SQLite 使用 readOnly,PG/MySQL 设置只读事务),db_query 只接受单条 SELECT/WITH 语句,并拒绝 DML/DDL、事务控制、SELECT ... INTO、FOR UPDATE 等。请仍然使用数据库侧的只读账号;SQL 关键字过滤只是纵深防御,不是权限系统。


1. 能力

  • 库/表总览:schema、表/视图、行数、数据大小、索引大小、列数。
  • 表结构:字段类型、可空性、默认值、主键、唯一键、索引、声明外键。
  • 数据画像:每张表的精确行数、空间占用;每个字段的空值率、去重数、最小/最大、数值均值与标准差、P25/P50/P75/P90、直方图、文本长度、Top-K 高频值。
  • 关联推测:同时给出
    • 数据库声明的外键(置信度 1.0),以及孤儿行比例、父表覆盖率;
    • 基于命名、类型、值重叠推测出的隐含关联(如 orders.customer_email → customers.email);
    • 可直接粘贴到文档中的 Mermaid ER 图。
  • 只读 SQL:在确认字段名后执行单条查询,自动截断超长结果。
  • 采样可控:小表全量扫描,大表自动采样;所有统计都会明确标记是否采样,避免把估算当成事实。

2. 注册的工具

工具 作用 关键参数
db_connections 列出配置的连接(凭据脱敏);test: true 逐个 ping test, url
db_overview 表清单 + 行数 + 空间 + 列数;探索起点 schema, tables, pattern, includeViews, exactCounts
db_schema 字段、主键、索引、声明外键 schema, tables, pattern, includeIndexes
db_profile 行数、空间、字段分布、Top-K、分位数、采样行 schema, tables, sampleSize, topValues, includeSamples
db_relationships 已声明 + 推测关联、覆盖率、Mermaid ER 图 schema, tables, deep, minConfidence, overlapSample, maxPairs
db_query 单条只读 SQL sql, limit

所有工具都接受:

  • connection:插件配置中命名的连接;
  • url:临时 DSN,例如 postgres://user:pass@host:5432/db、sqlite:demo/ecommerce.db。

推荐调用顺序:

db_overview → db_schema → db_profile → db_relationships → db_query

Web GUI 页面

插件同时声明了 dsh.client,会在 Web 侧边栏注册一个 数据库 入口,主面板提供五个页签:

  • 总览:表/视图、行数、空间、列数,点击行直接跳到该表结构;
  • 结构:字段、主键、唯一键、索引、声明外键;
  • 数据画像:每列空值率/去重数/min/max/均值/分位数/Top-K,带简单占比条;
  • 关联:声明外键、推测关联、覆盖率/孤儿率、Mermaid ER 图(可复制);
  • SQL:只读查询编辑器与结果表。

页面通过 Host 的 dbExplorer Remote 服务调用同一个 engine.js,因此和模型工具共享只读守卫、采样策略和连接配置。连接下拉框来自 db_connections,也可以直接在“临时 URL”里输入 DSN。

页面是浏览器插件:已经运行的 dsh web 需要重启一次(或重新启动桌面端),客户端模块元数据才会重新扫描并加载新的 dsh.client 声明。重启后在左侧边栏底部/面板列表中看到“数据库”。


3. 快速开始(本仓库的 SQLite 电商库)

仓库自带一个可复现的电商示例库:客户、地址、商品、分类、仓库、库存、订单、订单明细、交易、发货、评价。

# 进入本仓库目录(替换成你的实际路径)
cd /path/to/dsh-plugin-db-explorer

# 1) 重新生成 demo/ecommerce.db(确定性数据)
npm run seed

# 2) 安装到当前 DSH web profile(本地路径安装)
dsh plugin --profile web add "$PWD"

# 3) 在 profile 配置中登记连接
#    ~/.dsh/profiles/web/cordis.patch.yml

在 cordis.patch.yml 末尾加入:

- id: db-explorer
  config:
    defaultConnection: demo
    connections:
      demo:
        driver: sqlite
        # 绝对路径(推荐,替换成你的仓库路径)
        url: /path/to/dsh-plugin-db-explorer/demo/ecommerce.db
        # 如果 DSH 的 session workspace 就是本仓库根目录,也可以写相对路径:
        # url: demo/ecommerce.db

如果 DSH 启用了 HMR,配置会热加载;否则重启 dsh web。安装本身也可以直接在 Web 的 Plugins 页面完成:选择本地路径或包名,安装后启用 @hanshu-ai/dsh-plugin-db-explorer bundle,再在 profile patch 中加连接配置。

侧边栏的“数据库”页面属于浏览器插件:首次安装/升级后需要重启一次 dsh web,客户端模块才会重新扫描 dsh.client 并加载页面;模型工具和 Host Remote 不受影响。

然后可以直接对模型说:

用 db_overview 看一下 demo 库,列出最大的五张表;用 db_profile 分析 orders 的 status 和 total_amount;用 db_relationships 看看订单和客户、商品之间有哪些关联,并给出 Mermaid ER 图。

示例输出

db_overview:

| Schema | Table        | Type  | Rows  | Size     | Cols |
| main   | order_items  | table | 2,213 | 148 KiB  | 7    |
| main   | orders       | table |   900 | 156 KiB  | 9    |
| main   | transactions | table |   950 | 116 KiB  | 9    |

db_profile:

## main.products — 88 rows, 28.0 KiB (scanned all 88 rows)
| Column | Type | Nulls | Distinct | Min | Max | Mean ± SD | Top values |
| price  | NUMERIC | 0 (0%) | 88 (100%) | 14.01 | 2135.42 | 337.97 ± 465.39 | ... |
Numeric quantiles:
- price: p25=... p50=... p75=... p90=...

db_relationships:

## Declared foreign keys
- orders.customer_id → customers.customer_id (N:1) · orphan rows 0% · parent coverage 92.2%

## Inferred relationships
- orders.customer_email → customers.email (N:1) · score 0.99 (high) · child coverage 100% · parent coverage 92.2%
- order_items.product_sku → products.sku (N:1) · score 1 (high)
- inventory.warehouse_code → warehouses.code (N:1) · score 1 (high)
- transactions.customer_id → customers.customer_id (N:1) · score 0.99 (high)

## Mermaid ER diagram
```mermaid
erDiagram
  ORDERS ||--o{ ORDER_ITEMS : "order_id"
  CUSTOMERS ||--o{ ORDERS : "customer_email"
```

完整示例报告见仓库中的 demo/exploration-report.md。npm 发布包不包含这份生成报告;本地执行 npm run seed && npm run smoke 可以重新生成。


4. 安装与配置

4.1 安装插件

# 本地路径(开发;替换成你的实际仓库路径)
dsh plugin --profile web add /path/to/dsh-plugin-db-explorer

# 或发布到 registry 后
dsh plugin --profile web add @hanshu-ai/dsh-plugin-db-explorer

安装会写入 profile 的 package.json,并把本插件 bundle 追加到 dsh.profile.bundles。插件声明了 dsh.bundle.patch,因此安装后会自动插入 db-explorer 这一行。

4.2 安装非 SQLite 驱动

SQLite 使用 Node 22.5+ 内置的 node:sqlite,无需安装任何包。其他数据库按需安装:

dsh plugin --profile web add pg        # PostgreSQL
dsh plugin --profile web add mysql2    # MySQL / MariaDB
dsh plugin --profile web add mssql     # SQL Server
dsh plugin --profile web add oracledb  # Oracle

这些包被插件在真正用到对应驱动时才动态 import();没有安装时只有该驱动报出明确的安装提示,不影响 SQLite 使用。

4.3 配置参考

- id: db-explorer
  config:
    # 工具调用省略 connection/url 时使用哪个命名连接
    defaultConnection: shop

    connections:
      # SQLite:路径可以是绝对路径,或相对当前 session workspace 的路径
      shop:
        driver: sqlite
        url: /data/shop.db

      analytics:
        driver: postgres
        url: postgres://reader:${PG_PASSWORD}@db.internal:5432/analytics?sslmode=require&schema=public

      legacy:
        driver: mysql
        host: mysql.internal
        port: 3306
        database: legacy
        user: readonly
        passwordEnv: MYSQL_PASSWORD
        ssl: false

      warehouse:
        driver: sqlserver
        host: mssql.internal
        port: 1433
        database: warehouse
        user: readonly
        passwordEnv: MSSQL_PASSWORD
        ssl: true

      erp:
        driver: oracle
        host: oracle.internal
        port: 1521
        database: FREEPDB1       # 作为 service name 拼成 host:port/service
        user: READONLY
        passwordEnv: ORACLE_PASSWORD
        schema: ERP

    # 资源与安全上限
    maxRows: 500               # db_query / 显式 sample 的行数上限
    maxTables: 200             # 多表工具最多处理多少张表
    maxColumnsPerTable: 80     # 单表画像最多分析多少列
    profileRows: 20000         # 小表全量扫描阈值
    autoSampleRows: 5000       # 超过阈值时的默认采样行数
    topValues: 10              # 每列 Top-K
    relationshipPairs: 150     # 关系推测最多评估多少对候选
    overlapRows: 5000          # 值重叠检查每侧最多读取多少行
    minConfidence: 0.45        # 推测关联的最低分
    allowQuery: true           # 是否允许 db_query
    sampleRows: 5              # includeSamples=true 时返回的样本行数
    readOnly: true             # 尽可能以只读句柄打开

4.4 连接 URL 格式

驱动 示例
SQLite sqlite:/abs/path/shop.db、sqlite:demo/ecommerce.db、sqlite::memory:
PostgreSQL postgres://user:pass@host:5432/db?sslmode=require&schema=public
MySQL mysql://user:pass@host:3306/db
SQL Server mssql://user:pass@host:1433/db
Oracle oracle://user:pass@host:1521/FREEPDB1

字符串里的 ${ENV_VAR} 会在连接时展开;也可以对密码使用 passwordEnv 指向环境变量。错误信息中会替换掉密码和 URL 中的凭据。


5. 关系推测是怎么做的

db_relationships 同时做两件事:

5.1 已声明外键

从各数据库的目录读取外键,置信度固定为 1。对单列外键还会计算:

  • orphanRate:子表中找不到父记录的行占比;
  • childCoverage / parentCoverage:子表值命中的比例、父表键被引用的比例;
  • cardinality:子表列是主键/唯一键时为 1:1,否则为 N:1。

这些数字能直接暴露“数据质量”问题,例如订单引用了不存在的客户。

5.2 隐含关联

对每个“子表列 × 父表单列主键/唯一键”候选:

  1. 命名证据(0 ~ 0.5)
    • orders.customer_id → customers.customer_id:前缀 customer 匹配 customers 的单数形式,且目标是主键;
    • orders.customer_email → customers.email:前缀匹配表名,后缀匹配目标列;
    • order_items.product_sku → products.sku;
    • inventory.warehouse_code → warehouses.code;
    • 同一列名也只给较低权重,避免把每个 id 都连到每个 id。
  2. 类型兼容(0.1):数值 / 字符串 / 日期 / 布尔 / 二进制 家族必须一致。
  3. 值重叠(0.4):对两侧各采样最多 overlapRows 行,计算子表去重值覆盖率、父表键覆盖率。
score = nameScore + 0.1 (类型) + 0.3 × 子表去重覆盖率 + 0.1 × 父表覆盖率

score ≥ minConfidence 的候选进入结果,并标为 low / medium / high。

  • 默认只评估有命名证据的候选,避免在大库上做 N×N 全量两两比较;
  • deep: true 会评估所有类型兼容的组合,仍受 maxPairs 限制;
  • 大表上 overlapRows 是采样,父表覆盖率是近似值;
  • 推断结果不是数据库约束,只是探索线索,写业务逻辑前应人工确认。

6. Demo 数据库

demo/schema.sql + demo/seed.mjs 会生成确定性的电商库:

表 行数 说明
customers 320 客户与会员等级
addresses 496 客户地址
categories 32 两级分类(自关联)
products 88 商品、SKU、价格、成本、状态
warehouses 4 仓库
inventory 258 商品 × 仓库库存
orders 900 订单(7 种状态、4 种渠道)
order_items 2,213 订单明细(折扣、数量、单价)
transactions 950 支付/退款/拒付
shipments 643 发货与签收
reviews 248 商品评价

数据库中特意放了四条没有声明外键的冗余列,用来验证关系推测:

  • orders.customer_email → customers.email
  • order_items.product_sku → products.sku
  • inventory.warehouse_code → warehouses.code
  • transactions.customer_id → customers.customer_id

重新生成:

npm run seed
# 或指定目标文件
node demo/seed.mjs /tmp/shop.db

7. 开发与验证

# 进入本仓库目录(替换成你的实际路径)
cd /path/to/dsh-plugin-db-explorer

npm run setup:dev   # 仅为脱离 DSH profile 运行 test/smoke 建立最小依赖符号链接
npm test            # 26 个单元 + 集成 + 客户端/Remote 测试(会先重新 seed demo 库)
npm run smoke       # 直接调用 engine 探索 demo 库,生成 demo/exploration-report.md
npm run verify      # seed + test + smoke

测试重点:

  • 五种 URL 解析、环境变量、密码脱敏、连接选择错误提示;
  • SQL 只读守卫(多语句、DML/DDL、SELECT INTO、FOR UPDATE、字符串里的关键字);
  • 标识符引用、类型家族、值归一化、分布统计与分位数;
  • 命名证据和单数化规则;
  • 通过伪造 Cordis context 注册真实 defineTool 工具并逐个执行;
  • 总览行数/空间、表结构、画像采样、声明外键、四条隐含关联、Mermaid、查询行数上限;
  • 客户端 bundle 的模块注册、Remote 贡献、main/sidebar.panellist 槽位和 React 渲染冒烟。

本仓库只在 SQLite 上做了端到端验证。其他四个驱动按各数据库系统目录实现,未在本机连接真实服务器执行;首次接入时建议先用 db_connections({test:true}) 和 db_overview 验证权限与系统目录版本。建议版本:PostgreSQL 12+、MySQL 8.0+ / MariaDB 10.5+、SQL Server 2017+(STRING_AGG)、Oracle 12c+(FETCH FIRST)。


8. 目录结构

.
├── lib/
│   ├── index.js          # DSH 插件入口:Config、defineTool 注册、system prompt
│   ├── remote.js         # Host Remote 服务 dbExplorer(Web 页面调用入口)
│   ├── client.js         # Web 客户端:侧边栏入口 + 数据库探索页面
│   ├── engine.js         # 无 DSH 依赖的探索引擎(总览/结构/画像/关系/查询)
│   ├── config.js         # 配置归一化、URL 解析、连接选择、凭据脱敏
│   ├── sql.js            # 标识符/字面量引用、只读 SQL 守卫、LIMIT 包装
│   ├── stats.js          # 分布、分位数、直方图、字节/数字格式化
│   ├── values.js         # 类型分类与 JSON 安全的值归一化
│   ├── render.js         # 面向模型的 Markdown 渲染
│   └── drivers/
│       ├── index.js      # 懒加载驱动
│       ├── sqlite.js     # node:sqlite + dbstat
│       ├── postgres.js   # pg
│       ├── mysql.js      # mysql2/promise
│       ├── sqlserver.js  # mssql
│       └── oracle.js     # oracledb
├── demo/
│   ├── schema.sql        # 电商 schema
│   ├── seed.mjs          # 确定性造数
│   ├── ecommerce.db      # 生成物
│   └── exploration-report.md
├── scripts/
│   ├── setup-dev.mjs     # 测试用最小依赖链接
│   └── smoke.mjs         # 独立 engine 冒烟与报告
├── test/
│   ├── unit.test.mjs
│   ├── integration.test.mjs
│   ├── client.test.mjs
│   └── helpers.mjs
├── examples/
│   └── cordis.patch.example.yml
├── cordis.patch.yml      # bundle patch(插入 db-explorer 行)
└── package.json

9. 安全与限制

  • 只读:SQLite readOnly 句柄;PostgreSQL 会话 default_transaction_read_only=on;MySQL 会话只读事务;db_query 拒绝写语句。SQL Server / Oracle 没有通用会话级只读开关,依赖 SQL 守卫 + 只读账号。
  • Web 页面权限:页面通过 DSH 已认证的 Remote 通道访问 Host,不额外开放端口;能打开该 DSH Web UI 的用户即可对已配置连接执行同样的只读探索。
  • 行数上限:db_query 默认 500 行;db_profile 小表全量、大表采样;关系值重叠默认每侧 5000 行。
  • 标识符引用:所有 schema/table/column 都按方言引用("..." / `...` / [...]),避免目录名中的特殊字符破坏 SQL。
  • 错误脱敏:报错信息会移除密码和 URL 凭据。
  • 不生成写操作:插件没有写入工具,也不会自动创建索引或统计信息。
  • 采样不是精确值:sampled: true / scannedRows 会明确展示;distinct 在采样表上是下界估计。
  • Oracle/SQL Server 权限:部分系统目录(如 ALL_SEGMENTS、sys.dm_db_partition_stats)可能因账号权限不可见;插件会回退到 ALL_TABLES.BLOCKS * 8192 等估算并给出 sizeSource。
  • Node 版本:SQLite 依赖 node:sqlite,需要 Node 22.5+;Node 22 会打印 ExperimentalWarning: SQLite is an experimental feature,不影响功能。
  • SQL Server:STRING_AGG 需要 2017+;更早版本索引列会为空但不影响其他功能。
  • Oracle:分位数/关系查询使用 12c+ 语法;db_query 直接执行用户 SQL 并设置 maxRows,不包裹内联视图。

10. License

MIT。非 SQLite 驱动通过动态 import() 使用 pg、mysql2、mssql、oracledb,它们各自遵循自己的许可证;本包不把它们作为强制依赖打包。