dsh-sql 0.4.4 → 0.4.5

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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # 更新记录
2
+
3
+ [返回简介](README.md) · [使用说明](docs/USAGE.md) · [验证记录](docs/VALIDATION.md)
4
+
5
+ ## 0.4.5 (2026-10-05)
6
+
7
+ - 保留 SQLite 64 位整数与三种数据库的同名列;零行结果保留列名。同名列 JSON 导出使用 columns / rows,避免后列覆盖前列。
8
+
9
+ ## 0.4.4 (2026-09-28)
10
+
11
+ - 更新官方 Harness 0.2.0-rc.1 的兼容声明和共同加载验证;运行时代码未变。验证范围见[验证记录](docs/VALIDATION.md)。
12
+
13
+ ## 更早的改动
14
+
15
+ 完整历史可查阅 [GitHub 提交记录](https://github.com/STARDUSTLC666/dsh-sql/commits/master)。
package/README.en.md CHANGED
@@ -1,111 +1,46 @@
1
- [中文](README.md)
2
-
3
1
  # dsh-sql
4
2
 
5
- > **Your agent can query databases now**: SQLite / MySQL / PostgreSQL engines, read-only whitelist + write approval gate.
6
-
7
- DSH (DeepSeek Harness) engineer-grade database plugin: six tools covering connection management, read-only queries, write operations, schema introspection, database statistics, and health checks.
3
+ [中文](README.md)
8
4
 
9
- ![npm version](https://img.shields.io/npm/v/dsh-sql?label=npm&color=blue) ![npm downloads](https://img.shields.io/npm/dm/dsh-sql) ![license](https://img.shields.io/npm/l/dsh-sql) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-sql?style=social)
5
+ ![dsh-sql whale girl plugin cover](https://raw.githubusercontent.com/STARDUSTLC666/dsh-sql/master/assets/cover-whale-girl.png)
10
6
 
11
- [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
7
+ Connect SQLite, MySQL or PostgreSQL to query data and inspect schemas.
12
8
 
13
- ## Compatibility
9
+ [![npm](https://img.shields.io/npm/v/dsh-sql)](https://www.npmjs.com/package/dsh-sql) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-sql-downloads.svg)](https://www.npmjs.com/package/dsh-sql)
14
10
 
15
- Validation host: Harness `0.2.0-rc.1` built from official sources (commit `407e65c8`) with Node `24.16.0` on 2026-09-28. All 53 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 6 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
11
+ ## What it does
16
12
 
17
- ## Installation
13
+ - Manage connections and inspect schemas or database summaries.
14
+ - Limit rows and timeouts for read-only queries.
15
+ - Keep writes under host approval and check connections.
18
16
 
19
- ```bash
20
- dsh plugin --profile web add dsh-sql
21
- ```
17
+ ## Install
22
18
 
23
- ## Uninstall
19
+ In DSH Desktop, install `dsh-sql` from the Plugins panel. If the bundled dsh command is available:
24
20
 
25
21
  ```bash
26
- dsh plugin --profile web remove dsh-sql
22
+ dsh plugin --profile desktop add dsh-sql
27
23
  ```
28
24
 
29
- Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
30
-
31
-
32
- ## Configuration
33
-
34
- ```yaml
35
- - id: sql
36
- name: 'dsh-sql'
37
- config:
38
- connections:
39
- - name: local
40
- engine: sqlite
41
- file: E:\data\app.db # or :memory:
42
- - name: prod
43
- engine: postgres
44
- host: db.internal
45
- database: app
46
- # password: xxx # prefer env var DSH_SQL_PASSWORD_PROD
47
- - name: legacy
48
- engine: mysql
49
- host: 127.0.0.1
50
- port: 3306
51
- user: root
52
- database: legacy
53
- maxRows: 1000 # query row cap (1-10000)
54
- queryTimeoutMs: 60000 # per-query timeout (default 60s, 5s - 10min)
55
- execTimeoutMs: 120000 # per-write timeout (default 120s, 5s - 10min)
56
- readOnly: false # true disables sql_exec
57
- writeApproval: true # approve write operations first (default true)
58
- ```
25
+ For the web version, replace `desktop` with `web`. Restart DSH after installation.
59
26
 
60
- With no connection configuration, the plugin provides a `:memory:` SQLite connection. If configuration is present but invalid, the plugin fails to load with the validation error instead of silently falling back to the in-memory database.
61
-
62
- ## Tools
63
-
64
- | Tool | Purpose | Safety |
65
- | :-- | :-- | :-- |
66
- | `sql_list` | List connections + connectivity test | — |
67
- | `sql_query` | Read-only queries (SELECT/PRAGMA/EXPLAIN/SHOW/DESCRIBE/WITH) | Keyword whitelist + rejects multi-statement |
68
- | `sql_exec` | Writes / DDL (multi-statement scripts allowed) | readOnly lock + approval gate |
69
- | `sql_schema` | Table list / table structure | Identifier whitelist validation |
70
- | `sql_stats` | Table counts, row estimates, and database size | Quoted identifiers + isolated query failures |
71
- | `sql_health` | Connection and safety-configuration checks | Per-connection probe; passwords are never returned |
72
-
73
- ### Examples
74
-
75
- ```text
76
- sql_list {}
77
- sql_schema {} # list all tables
78
- sql_schema { table: users } # inspect the users table
79
- sql_stats {} # inspect the default connection's data size
80
- sql_health {} # check connections and safety settings
81
- sql_query { sql: SELECT * FROM orders WHERE status = 'pending' LIMIT 50 }
82
- sql_exec { sql: UPDATE orders SET status = 'paid' WHERE id = 42 }
83
- ```
27
+ ## Start using it
84
28
 
85
- ## Safety
29
+ Configure a connection, then ask to inspect a table schema and its ten latest rows using read-only queries.
86
30
 
87
- - **Lexer-grade read-only guard**: sql_query strips strings/comments before validation, then rejects data-modifying CTEs (WITH…DELETE/UPDATE), SELECT INTO, FOR UPDATE/FOR SHARE, PRAGMA assignment, and multi-statement input
88
- - **Write approval gate**: sql_exec asks for approval by default (mirroring dsh-email's send approval); headless environments without an approval channel are denied
89
- - **readOnly mode**: lock out writes entirely for production databases
90
- - **Streaming row cap**: SQLite iterators, MySQL Readables, and PostgreSQL Query row events collect at most maxRows+1 rows and flag overflow with truncated. MySQL and PostgreSQL close the query's dedicated connection at the cap; smaller results return the connection to the pool, without materializing the full result in memory
91
- - **Cancellation-aware execution**: queries and writes observe Harness `exec.signal`; cancellation stops waiting and destroys the active dedicated MySQL/PostgreSQL connection
92
- - **Lossless large integers**: bigint values within JavaScript's safe integer range are returned as numbers; larger values are returned as decimal strings instead of silently losing precision
93
- - **Identifier validation**: table names restricted to alphanumerics and underscores — no schema injection
94
- - **Secrets stay out of config**: passwords via `DSH_SQL_PASSWORD_<CONNECTION>` env vars
31
+ ## Requirements and configuration
95
32
 
96
- ## Engines
33
+ SQLite requires an accessible database file. MySQL and PostgreSQL require a reachable service and credentials.
97
34
 
98
- - **SQLite**: built-in `node:sqlite` (Node 22.13+), zero dependencies
99
- - **MySQL**: mysql2 pool
100
- - **PostgreSQL**: pg pool
35
+ Detailed configuration, tool arguments and troubleshooting are in the [usage guide](docs/USAGE.en.md). For standalone development, follow the Node requirement in [package.json](package.json).
101
36
 
102
- ## Development
37
+ ## Documentation
103
38
 
104
- ```bash
105
- pnpm install
106
- pnpm test # build + full test suite, including a real SQLite integration suite
107
- ```
39
+ - [Usage and troubleshooting](docs/USAGE.en.md)
40
+ - [Changelog](CHANGELOG.md)
41
+ - [Validation scope and history](docs/VALIDATION.md)
42
+ - [Report a problem or suggest a feature](https://github.com/STARDUSTLC666/dsh-sql/issues)
108
43
 
109
44
  ## License
110
45
 
111
- MIT
46
+ [MIT](LICENSE)
package/README.md CHANGED
@@ -1,111 +1,46 @@
1
- [English](README.en.md)
2
-
3
1
  # dsh-sql
4
2
 
5
- > **你的 agent 会查库了**:SQLite / MySQL / PostgreSQL 三引擎,只读白名单 + 写审批门。
3
+ [English](README.en.md)
6
4
 
7
- DSH(DeepSeek Harness)工程师级数据库插件:六个工具覆盖连接管理、只读查询、写操作、结构探查、统计概览与健康自检。
5
+ ![dsh-sql 鲸鱼娘插件封面](https://raw.githubusercontent.com/STARDUSTLC666/dsh-sql/master/assets/cover-whale-girl.png)
8
6
 
9
- ![npm version](https://img.shields.io/npm/v/dsh-sql?label=npm&color=blue) ![npm downloads](https://img.shields.io/npm/dm/dsh-sql) ![license](https://img.shields.io/npm/l/dsh-sql) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-sql?style=social)
7
+ 连接 SQLite、MySQL 或 PostgreSQL,查询数据并检查数据库结构。
10
8
 
11
- [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
9
+ [![npm](https://img.shields.io/npm/v/dsh-sql)](https://www.npmjs.com/package/dsh-sql) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-sql-downloads.svg)](https://www.npmjs.com/package/dsh-sql)
12
10
 
13
- ## 兼容性
11
+ ## 功能
14
12
 
15
- 验证宿主:官方源码构建的 Harness `0.2.0-rc.1`(commit `407e65c8`)+ Node `24.16.0`(2026-09-28)。53 项插件测试在隔离环境全部通过;同一个宿主里 18 个插件共同加载,注册 6 个工具,工具 schema 与健康检查契约通过。本轮未启用真实端口与外部服务。
13
+ - 管理连接,查询结构和数据概览。
14
+ - 只读查询带行数与超时限制。
15
+ - 写操作通过宿主审批,支持连接自检。
16
16
 
17
17
  ## 安装
18
18
 
19
- ```bash
20
- dsh plugin --profile web add dsh-sql
21
- ```
22
-
23
- ## 卸载
19
+ 桌面版可在「插件」面板按包名 `dsh-sql` 安装。已配置 dsh 命令时也可使用:
24
20
 
25
21
  ```bash
26
- dsh plugin --profile web remove dsh-sql
22
+ dsh plugin --profile desktop add dsh-sql
27
23
  ```
28
24
 
29
- 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
30
-
31
-
32
- ## 配置
33
-
34
- ```yaml
35
- - id: sql
36
- name: 'dsh-sql'
37
- config:
38
- connections:
39
- - name: local
40
- engine: sqlite
41
- file: E:\data\app.db # 或 :memory:
42
- - name: prod
43
- engine: postgres
44
- host: db.internal
45
- database: app
46
- # password: xxx # 推荐环境变量 DSH_SQL_PASSWORD_PROD
47
- - name: legacy
48
- engine: mysql
49
- host: 127.0.0.1
50
- port: 3306
51
- user: root
52
- database: legacy
53
- maxRows: 1000 # 查询返回行数上限(1-10000)
54
- queryTimeoutMs: 60000 # 单次查询超时(默认 60 秒,5 秒 - 10 分钟)
55
- execTimeoutMs: 120000 # 单次写操作超时(默认 120 秒,5 秒 - 10 分钟)
56
- readOnly: false # true 时禁用 sql_exec
57
- writeApproval: true # 写操作先弹审批(默认 true)
58
- ```
25
+ 网页版把命令中的 `desktop` 改为 `web`。安装后重启 DSH。
59
26
 
60
- 配置缺省时会提供一个 `:memory:` SQLite 连接;配置一旦给出但格式无效,插件会直接报错并停止加载,不会静默回退到内存库。
61
-
62
- ## 工具一览
63
-
64
- | 工具 | 作用 | 安全 |
65
- | :-- | :-- | :-- |
66
- | `sql_list` | 列出连接 + 连通性测试 | — |
67
- | `sql_query` | 只读查询(SELECT/PRAGMA/EXPLAIN/SHOW/DESCRIBE/WITH)| 关键字白名单 + 拒绝多语句 |
68
- | `sql_exec` | 写操作/DDL(可多语句脚本)| readOnly 禁用 + 审批门 |
69
- | `sql_schema` | 表清单 / 表结构 | 标识符白名单校验 |
70
- | `sql_stats` | 表数量、行数与库体积概览 | 表名引用 + 查询失败隔离 |
71
- | `sql_health` | 连接与安全配置自检 | 逐连接探活,不回显密码 |
72
-
73
- ### 示例
74
-
75
- ```text
76
- sql_list {}
77
- sql_schema {} # 列出所有表
78
- sql_schema { table: users } # 看 users 表结构
79
- sql_stats {} # 查看默认连接的数据规模
80
- sql_health {} # 检查连接和安全配置
81
- sql_query { sql: SELECT * FROM orders WHERE status = 'pending' LIMIT 50 }
82
- sql_exec { sql: UPDATE orders SET status = 'paid' WHERE id = 42 }
83
- ```
27
+ ## 开始使用
84
28
 
85
- ## 安全设计
29
+ 配置数据库连接后,可说:“查看这张表的结构和最近十条记录,先只读查询。”
86
30
 
87
- - **词法级只读保护**:sql_query 先剥离字符串/注释再校验,拒绝 data-modifying CTE(WITH…DELETE/UPDATE)、SELECT INTO、FOR UPDATE/FOR SHARE、PRAGMA 赋值与多语句
88
- - **写审批门**:sql_exec 默认弹审批(对齐 dsh-email 的发信审批),headless 环境无审批通道时拒绝执行
89
- - **readOnly 模式**:生产库可整体禁用写
90
- - **流式行数钳制**:SQLite 迭代器 / MySQL Readable / PostgreSQL Query 行事件最多收集 maxRows+1 行,超量标记 truncated;MySQL 和 PostgreSQL 在达到上限时关闭该查询的专用连接,未达上限则正常归还连接池,避免全量结果驻留内存
91
- - **可取消执行**:查询与写操作遵守 Harness 的 `exec.signal`;取消时会中止等待并销毁正在工作的 MySQL/PostgreSQL 专用连接
92
- - **大整数无损**:数据库返回的 bigint 在 JavaScript 安全整数范围内输出 number,超出范围则输出十进制字符串,避免静默丢精度
93
- - **标识符校验**:表名只允许字母/数字/下划线,杜绝 schema 注入
94
- - **密钥不落配置**:密码支持 `DSH_SQL_PASSWORD_<连接名>` 环境变量
31
+ ## 依赖与配置
95
32
 
96
- ## 引擎
33
+ SQLite 需可访问的数据库文件;MySQL / PostgreSQL 需对应服务与登录信息。
97
34
 
98
- - **SQLite**:Node 22.13+ 内置 `node:sqlite`,零依赖
99
- - **MySQL**:mysql2 连接池
100
- - **PostgreSQL**:pg 连接池
35
+ 详细配置、工具参数与排错见[使用说明](docs/USAGE.md)。从源码独立开发时,Node 要求以 [package.json](package.json) 为准。
101
36
 
102
- ## 开发
37
+ ## 文档
103
38
 
104
- ```bash
105
- pnpm install
106
- pnpm test # 构建 + 完整测试套件(含真实 SQLite 集成)
107
- ```
39
+ - [使用与排错](docs/USAGE.md)
40
+ - [更新记录](CHANGELOG.md)
41
+ - [验证范围与历史记录](docs/VALIDATION.md)
42
+ - [问题反馈与功能建议](https://github.com/STARDUSTLC666/dsh-sql/issues)
108
43
 
109
44
  ## License
110
45
 
111
- MIT
46
+ [MIT](LICENSE)
@@ -0,0 +1,102 @@
1
+ # dsh-sql usage guide
2
+
3
+ [Overview](../README.en.md) · [Changelog](../CHANGELOG.md) · [Validation](VALIDATION.md)
4
+
5
+ ## Current improvements
6
+
7
+ Safe integers remain numbers; larger integers are decimal strings. JSON with duplicate column names uses { columns, rows }; other JSON remains an object array. SQLite currently executes synchronously: row limits bound returned data but cannot interrupt an already-running expensive statement. Avoid unbounded complex queries on unknown data.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ dsh plugin --profile web add dsh-sql
13
+ ```
14
+
15
+ ## Uninstall
16
+
17
+ ```bash
18
+ dsh plugin --profile web remove dsh-sql
19
+ ```
20
+
21
+ Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
22
+
23
+ ## Configuration
24
+
25
+ ```yaml
26
+ - id: sql
27
+ name: 'dsh-sql'
28
+ config:
29
+ connections:
30
+ - name: local
31
+ engine: sqlite
32
+ file: E:\data\app.db # or :memory:
33
+ - name: prod
34
+ engine: postgres
35
+ host: db.internal
36
+ database: app
37
+ # password: xxx # prefer env var DSH_SQL_PASSWORD_PROD
38
+ - name: legacy
39
+ engine: mysql
40
+ host: 127.0.0.1
41
+ port: 3306
42
+ user: root
43
+ database: legacy
44
+ maxRows: 1000 # query row cap (1-10000)
45
+ queryTimeoutMs: 60000 # per-query timeout (default 60s, 5s - 10min)
46
+ execTimeoutMs: 120000 # per-write timeout (default 120s, 5s - 10min)
47
+ readOnly: false # true disables sql_exec
48
+ writeApproval: true # approve write operations first (default true)
49
+ ```
50
+
51
+ With no connection configuration, the plugin provides a `:memory:` SQLite connection. If configuration is present but invalid, the plugin fails to load with the validation error instead of silently falling back to the in-memory database.
52
+
53
+ ## Tools
54
+
55
+ | Tool | Purpose | Safety |
56
+ | :-- | :-- | :-- |
57
+ | `sql_list` | List connections + connectivity test | — |
58
+ | `sql_query` | Read-only queries (SELECT/PRAGMA/EXPLAIN/SHOW/DESCRIBE/WITH) | Keyword whitelist + rejects multi-statement |
59
+ | `sql_exec` | Writes / DDL (multi-statement scripts allowed) | readOnly lock + approval gate |
60
+ | `sql_schema` | Table list / table structure | Identifier whitelist validation |
61
+ | `sql_stats` | Table counts, row estimates, and database size | Quoted identifiers + isolated query failures |
62
+ | `sql_health` | Connection and safety-configuration checks | Per-connection probe; passwords are never returned |
63
+
64
+ ### Examples
65
+
66
+ ```text
67
+ sql_list {}
68
+ sql_schema {} # list all tables
69
+ sql_schema { table: users } # inspect the users table
70
+ sql_stats {} # inspect the default connection's data size
71
+ sql_health {} # check connections and safety settings
72
+ sql_query { sql: SELECT * FROM orders WHERE status = 'pending' LIMIT 50 }
73
+ sql_exec { sql: UPDATE orders SET status = 'paid' WHERE id = 42 }
74
+ ```
75
+
76
+ ## Safety
77
+
78
+ - **Lexer-grade read-only guard**: sql_query strips strings/comments before validation, then rejects data-modifying CTEs (WITH…DELETE/UPDATE), SELECT INTO, FOR UPDATE/FOR SHARE, PRAGMA assignment, and multi-statement input
79
+ - **Write approval gate**: sql_exec asks for approval by default (mirroring dsh-email's send approval); headless environments without an approval channel are denied
80
+ - **readOnly mode**: lock out writes entirely for production databases
81
+ - **Streaming row cap**: SQLite iterators, MySQL Readables, and PostgreSQL Query row events collect at most maxRows+1 rows and flag overflow with truncated. MySQL and PostgreSQL close the query's dedicated connection at the cap; smaller results return the connection to the pool, without materializing the full result in memory
82
+ - **Cancellation-aware execution**: queries and writes observe Harness `exec.signal`; cancellation stops waiting and destroys the active dedicated MySQL/PostgreSQL connection
83
+ - **Lossless large integers**: bigint values within JavaScript's safe integer range are returned as numbers; larger values are returned as decimal strings instead of silently losing precision
84
+ - **Identifier validation**: table names restricted to alphanumerics and underscores — no schema injection
85
+ - **Secrets stay out of config**: passwords via `DSH_SQL_PASSWORD_<CONNECTION>` env vars
86
+
87
+ ## Engines
88
+
89
+ - **SQLite**: built-in `node:sqlite` (Node 22.13+), zero dependencies
90
+ - **MySQL**: mysql2 pool
91
+ - **PostgreSQL**: pg pool
92
+
93
+ ## Development
94
+
95
+ ```bash
96
+ pnpm install
97
+ pnpm test # build + full test suite, including a real SQLite integration suite
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
package/docs/USAGE.md ADDED
@@ -0,0 +1,102 @@
1
+ # dsh-sql 使用说明
2
+
3
+ [返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证记录](VALIDATION.md)
4
+
5
+ ## 本次改进
6
+
7
+ 安全范围内整数保持 number,超出范围按十进制字符串输出。同名列 JSON 使用 { columns, rows },其他 JSON 仍为对象数组。SQLite 当前使用同步执行:行数上限限制返回数据,不能中断已运行的耗时 SQL;请避免对未知数据执行无界复杂查询。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ dsh plugin --profile web add dsh-sql
13
+ ```
14
+
15
+ ## 卸载
16
+
17
+ ```bash
18
+ dsh plugin --profile web remove dsh-sql
19
+ ```
20
+
21
+ 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
22
+
23
+ ## 配置
24
+
25
+ ```yaml
26
+ - id: sql
27
+ name: 'dsh-sql'
28
+ config:
29
+ connections:
30
+ - name: local
31
+ engine: sqlite
32
+ file: E:\data\app.db # 或 :memory:
33
+ - name: prod
34
+ engine: postgres
35
+ host: db.internal
36
+ database: app
37
+ # password: xxx # 推荐环境变量 DSH_SQL_PASSWORD_PROD
38
+ - name: legacy
39
+ engine: mysql
40
+ host: 127.0.0.1
41
+ port: 3306
42
+ user: root
43
+ database: legacy
44
+ maxRows: 1000 # 查询返回行数上限(1-10000)
45
+ queryTimeoutMs: 60000 # 单次查询超时(默认 60 秒,5 秒 - 10 分钟)
46
+ execTimeoutMs: 120000 # 单次写操作超时(默认 120 秒,5 秒 - 10 分钟)
47
+ readOnly: false # true 时禁用 sql_exec
48
+ writeApproval: true # 写操作先弹审批(默认 true)
49
+ ```
50
+
51
+ 配置缺省时会提供一个 `:memory:` SQLite 连接;配置一旦给出但格式无效,插件会直接报错并停止加载,不会静默回退到内存库。
52
+
53
+ ## 工具一览
54
+
55
+ | 工具 | 作用 | 安全 |
56
+ | :-- | :-- | :-- |
57
+ | `sql_list` | 列出连接 + 连通性测试 | — |
58
+ | `sql_query` | 只读查询(SELECT/PRAGMA/EXPLAIN/SHOW/DESCRIBE/WITH)| 关键字白名单 + 拒绝多语句 |
59
+ | `sql_exec` | 写操作/DDL(可多语句脚本)| readOnly 禁用 + 审批门 |
60
+ | `sql_schema` | 表清单 / 表结构 | 标识符白名单校验 |
61
+ | `sql_stats` | 表数量、行数与库体积概览 | 表名引用 + 查询失败隔离 |
62
+ | `sql_health` | 连接与安全配置自检 | 逐连接探活,不回显密码 |
63
+
64
+ ### 示例
65
+
66
+ ```text
67
+ sql_list {}
68
+ sql_schema {} # 列出所有表
69
+ sql_schema { table: users } # 看 users 表结构
70
+ sql_stats {} # 查看默认连接的数据规模
71
+ sql_health {} # 检查连接和安全配置
72
+ sql_query { sql: SELECT * FROM orders WHERE status = 'pending' LIMIT 50 }
73
+ sql_exec { sql: UPDATE orders SET status = 'paid' WHERE id = 42 }
74
+ ```
75
+
76
+ ## 安全设计
77
+
78
+ - **词法级只读保护**:sql_query 先剥离字符串/注释再校验,拒绝 data-modifying CTE(WITH…DELETE/UPDATE)、SELECT INTO、FOR UPDATE/FOR SHARE、PRAGMA 赋值与多语句
79
+ - **写审批门**:sql_exec 默认弹审批(对齐 dsh-email 的发信审批),headless 环境无审批通道时拒绝执行
80
+ - **readOnly 模式**:生产库可整体禁用写
81
+ - **流式行数钳制**:SQLite 迭代器 / MySQL Readable / PostgreSQL Query 行事件最多收集 maxRows+1 行,超量标记 truncated;MySQL 和 PostgreSQL 在达到上限时关闭该查询的专用连接,未达上限则正常归还连接池,避免全量结果驻留内存
82
+ - **可取消执行**:查询与写操作遵守 Harness 的 `exec.signal`;取消时会中止等待并销毁正在工作的 MySQL/PostgreSQL 专用连接
83
+ - **大整数无损**:数据库返回的 bigint 在 JavaScript 安全整数范围内输出 number,超出范围则输出十进制字符串,避免静默丢精度
84
+ - **标识符校验**:表名只允许字母/数字/下划线,杜绝 schema 注入
85
+ - **密钥不落配置**:密码支持 `DSH_SQL_PASSWORD_<连接名>` 环境变量
86
+
87
+ ## 引擎
88
+
89
+ - **SQLite**:Node 22.13+ 内置 `node:sqlite`,零依赖
90
+ - **MySQL**:mysql2 连接池
91
+ - **PostgreSQL**:pg 连接池
92
+
93
+ ## 开发
94
+
95
+ ```bash
96
+ pnpm install
97
+ pnpm test # 构建 + 完整测试套件(含真实 SQLite 集成)
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1,11 @@
1
+ # dsh-sql 验证记录
2
+
3
+ 本页整理原 README 的历史验证说明,保留当时的版本、日期与范围。自动测试、启动检查、浏览器操作和真实服务验收分别记录,不能相互替代。更详细的版本验收文件仍保留在仓库中。
4
+
5
+ ## 原中文记录
6
+
7
+ 验证宿主:官方源码构建的 Harness `0.2.0-rc.1`(commit `407e65c8`)+ Node `24.16.0`(2026-09-28)。53 项插件测试在隔离环境全部通过;同一个宿主里 18 个插件共同加载,注册 6 个工具,工具 schema 与健康检查契约通过。本轮未启用真实端口与外部服务。
8
+
9
+ ## Original English record
10
+
11
+ Validation host: Harness `0.2.0-rc.1` built from official sources (commit `407e65c8`) with Node `24.16.0` on 2026-09-28. All 53 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 6 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
package/lib/adapters.js CHANGED
@@ -43,7 +43,7 @@ function streamMysqlQuery(corePool, sql, limit, discard) {
43
43
  let settled = false;
44
44
  let columns = [];
45
45
  const rows = [];
46
- const stream = corePool.query(sql).stream({ highWaterMark: 64 });
46
+ const stream = corePool.query({ sql, rowsAsArray: true }).stream({ highWaterMark: 64 });
47
47
  const finish = () => {
48
48
  if (settled)
49
49
  return;
@@ -59,7 +59,7 @@ function streamMysqlQuery(corePool, sql, limit, discard) {
59
59
  return;
60
60
  if (columns.length === 0)
61
61
  columns = Object.keys(row);
62
- rows.push(columns.map((name) => toValue(row[name])));
62
+ rows.push(Array.isArray(row) ? row.map(toValue) : columns.map((name) => toValue(row[name])));
63
63
  if (rows.length >= limit) {
64
64
  finish();
65
65
  stream.destroy();
@@ -83,13 +83,14 @@ function streamPostgresQuery(client, sql, limit, discard) {
83
83
  let settled = false;
84
84
  let columns = [];
85
85
  const rows = [];
86
- const query = new pg.Query(sql);
86
+ const queryConfig = { text: sql, rowMode: 'array' };
87
+ const query = new pg.Query(queryConfig);
87
88
  query.on('row', (row, result) => {
88
89
  if (settled)
89
90
  return;
90
91
  if (columns.length === 0)
91
92
  columns = result?.fields.map((field) => field.name) ?? Object.keys(row);
92
- rows.push(columns.map((name) => toValue(row[name])));
93
+ rows.push(Array.isArray(row) ? row.map(toValue) : columns.map((name) => toValue(row[name])));
93
94
  if (rows.length >= limit) {
94
95
  // Closing this dedicated connection stops server work and prevents reuse.
95
96
  settled = true;
@@ -145,16 +146,17 @@ class SqliteAdapter {
145
146
  async query(sql, limit, signal) {
146
147
  signal?.throwIfAborted();
147
148
  const statement = this.db.prepare(sql);
149
+ statement.setReadBigInts(true);
150
+ statement.setReturnArrays(true);
151
+ const columns = statement.columns().map((column) => column.name);
148
152
  if (limit === undefined || limit <= 0) {
149
- const rows = statement.all();
153
+ const rows = statement.all().map(row => row.map(toValue));
150
154
  signal?.throwIfAborted();
151
- return rowsToColumns(rows);
155
+ return { columns, rows };
152
156
  }
153
- const columns = statement.columns().map((column) => column.name);
154
157
  const rows = [];
155
158
  for (const raw of statement.iterate()) {
156
- const row = raw;
157
- rows.push(columns.map((name) => toValue(row[name])));
159
+ rows.push(raw.map(toValue));
158
160
  signal?.throwIfAborted();
159
161
  if (rows.length >= limit)
160
162
  break;
@@ -195,6 +197,8 @@ class MysqlAdapter {
195
197
  database: connection.database ?? '',
196
198
  connectionLimit: 5,
197
199
  enableKeepAlive: true,
200
+ supportBigNumbers: true,
201
+ bigNumberStrings: true,
198
202
  });
199
203
  }
200
204
  async withSignalConnection(signal, work) {
@@ -226,8 +230,8 @@ class MysqlAdapter {
226
230
  }
227
231
  async queryRows(sql, signal) {
228
232
  if (signal === undefined)
229
- return await this.pool.query(sql);
230
- return await this.withSignalConnection(signal, async (connection) => await connection.query(sql));
233
+ return typeof sql === 'string' ? await this.pool.query(sql) : await this.pool.query(sql);
234
+ return await this.withSignalConnection(signal, async (connection) => typeof sql === 'string' ? await connection.query(sql) : await connection.query(sql));
231
235
  }
232
236
  async listTables(signal) {
233
237
  const [rows] = await this.queryRows('SHOW TABLES', signal);
@@ -245,7 +249,9 @@ class MysqlAdapter {
245
249
  }
246
250
  async query(sql, limit, signal) {
247
251
  if (limit === undefined || limit <= 0) {
248
- const [rows] = await this.queryRows(sql, signal);
252
+ const [rows, fields] = await this.queryRows({ sql, rowsAsArray: true }, signal);
253
+ if (fields?.length)
254
+ return { columns: fields.map(field => field.name), rows: rows.map(row => Array.isArray(row) ? row.map(toValue) : fields.map(field => toValue(row[field.name]))) };
249
255
  return rowsToColumns(rows);
250
256
  }
251
257
  return await this.withSignalConnection(signal, async (connection, discard) => {
@@ -344,7 +350,9 @@ class PostgresAdapter {
344
350
  }
345
351
  async query(sql, limit, signal) {
346
352
  if (limit === undefined || limit <= 0) {
347
- const result = await this.queryWithSignal(sql, undefined, signal);
353
+ const result = await this.queryWithSignal({ text: sql, rowMode: 'array' }, undefined, signal);
354
+ if (result.fields?.length)
355
+ return { columns: result.fields.map((field) => field.name), rows: result.rows.map((row) => row.map(toValue)) };
348
356
  const rows = result.rows;
349
357
  return rowsToColumns(rows);
350
358
  }
package/lib/tools.js CHANGED
@@ -395,6 +395,9 @@ export function buildSqlTools(config) {
395
395
  if (format === 'csv')
396
396
  return { ...base, format, formatted: toCsv(result.columns, rows) };
397
397
  if (format === 'json') {
398
+ if (new Set(result.columns).size !== result.columns.length) {
399
+ return { ...base, format, formatted: JSON.stringify({ columns: result.columns, rows }, null, 2) };
400
+ }
398
401
  const objects = rows.map((row) => Object.fromEntries(result.columns.map((column, i) => [column, row[i]])));
399
402
  return { ...base, format, formatted: JSON.stringify(objects, null, 2) };
400
403
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-sql",
3
- "version": "0.4.4",
4
- "description": "DSH 数据库插件:SQLite / MySQL / PostgreSQL 查询与管理,只读模式、写操作审批与流式行数钳制。",
3
+ "version": "0.4.5",
4
+ "description": "连接 SQLite、MySQL 或 PostgreSQL,查询数据并检查数据库结构。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -17,7 +17,9 @@
17
17
  "lib",
18
18
  "cordis.patch.yml",
19
19
  "README.md",
20
- "README.en.md"
20
+ "README.en.md",
21
+ "docs",
22
+ "CHANGELOG.md"
21
23
  ],
22
24
  "scripts": {
23
25
  "build": "tsc -p tsconfig.json",