dsh-switchman 1.3.0
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/LICENSE +21 -0
- package/README.md +79 -0
- package/README.zh.md +79 -0
- package/client.js +2441 -0
- package/cordis.patch.yml +599 -0
- package/host/config.js +116 -0
- package/host/context-watch.js +620 -0
- package/host/data/capability-default.json +1097 -0
- package/host/dispatch.js +205 -0
- package/host/lang.js +182 -0
- package/host/lib/rank.js +187 -0
- package/host/routes.js +398 -0
- package/icon.svg +7 -0
- package/index.js +154 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +70 -0
- package/skills/db-query/SKILL.md +322 -0
- package/skills/db-query/package-lock.json +251 -0
- package/skills/db-query/package.json +20 -0
- package/skills/db-query/references/mysql.md +137 -0
- package/skills/db-query/references/redis.md +213 -0
- package/skills/db-query/references/security.md +176 -0
- package/skills/db-query/scripts/lib/cli.js +118 -0
- package/skills/db-query/scripts/lib/config.js +215 -0
- package/skills/db-query/scripts/lib/mysql-readonly.js +655 -0
- package/skills/db-query/scripts/lib/output.js +322 -0
- package/skills/db-query/scripts/lib/redis-readonly.js +362 -0
- package/skills/db-query/scripts/mysql-query.js +198 -0
- package/skills/db-query/scripts/redis-query.js +181 -0
- package/skills/db-query/scripts/setup.sh +51 -0
- package/skills/git-commit-message/SKILL.md +49 -0
- package/skills/requirement-docs/SKILL.md +38 -0
- package/skills/requirement-docs/references/templates.md +187 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: db-query
|
|
3
|
+
description: Read-only MySQL/Redis queries via built-in scripts — verify table records, cache keys/fields/TTL, and cross-store consistency. Trigger when the user asks to check or verify database data, including "run this SQL", "check the table", or any MySQL/Redis look-up. If the access address or credentials are unknown, stop and ask the user first; never search for them yourself. Refuse all writes, deletes, DDL, or TTL changes.
|
|
4
|
+
compatibility: Node.js 18+, npm, MySQL 5.7.8+/8.0, Redis 5+; dedicated read-only account required for shared/production environments
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- [2026-09-05]-[full English rewrite; top rule: unknown DB access info → stop and ask, never hunt/guess]-[blocks blind or self-sourced connections] -->
|
|
8
|
+
<!-- [2026-09-16]-[broaden the trigger sentence: run-this-SQL / check-the-table phrasings now match too]-[natural trigger rate rises alongside the [DB] hint surfaces] -->
|
|
9
|
+
<!-- [2026-10-01]-[Language section: honor a [SWITCHMAN:LANG] system-prompt line over the English default]-[aligns with the host-side language-preference layer] -->
|
|
10
|
+
# Database Query & Verification
|
|
11
|
+
|
|
12
|
+
Safely query MySQL and Redis via built-in read-only scripts for debugging, data verification, and cache inspection.
|
|
13
|
+
**Core red line: this skill is read-only; every write operation is refused.**
|
|
14
|
+
|
|
15
|
+
## Trigger guide
|
|
16
|
+
|
|
17
|
+
| Scenario | Trigger? | Notes |
|
|
18
|
+
|------|----------|------|
|
|
19
|
+
| Query MySQL records / verify field values | ✅ | Read-only verification |
|
|
20
|
+
| Query Redis keys/values/TTL/fields | ✅ | Read-only verification |
|
|
21
|
+
| Verify MySQL ↔ Redis consistency | ✅ | Read both sides, then compare |
|
|
22
|
+
| Inspect table structure, indexes, execution plans | ✅ | SHOW / DESCRIBE / EXPLAIN |
|
|
23
|
+
| Count rows, aggregate analysis | ✅ | SELECT COUNT/SUM etc. |
|
|
24
|
+
| Insert/update/delete records | ❌ | Refuse; suggest a read-only verification plan |
|
|
25
|
+
| Create/alter/migrate tables | ❌ | Refuse; out of scope |
|
|
26
|
+
| Change Redis TTL | ❌ | Refuse; EXPIRE is a write command |
|
|
27
|
+
| Flush cache / delete keys | ❌ | Refuse; offer a read-only inspection instead |
|
|
28
|
+
| Export data to files | ❌ | Refuse (INTO OUTFILE is blocked); query and let the user handle results |
|
|
29
|
+
|
|
30
|
+
## Mandatory read-only rules
|
|
31
|
+
|
|
32
|
+
> **Highest priority.** Violating any item counts as a security violation.
|
|
33
|
+
|
|
34
|
+
1. **Missing access info → stop and ask**: if the database address or credentials are unknown, stop immediately and ask the user. Never search config files/history on your own, guess, or probe hosts/ports. Without explicit access info, refuse to act rather than act blindly.
|
|
35
|
+
2. **Built-in scripts only**: queries must go through `scripts/mysql-query.js` or `scripts/redis-query.js`; no fallback to `mysql` / `redis-cli` or ad-hoc code.
|
|
36
|
+
3. **Never bypass the validator**: do not circumvent SQL/command safety checks by any means other than `--validate-only`.
|
|
37
|
+
4. **Refuse all write requests**: on any write/modify/delete/DDL request, tell the user "this skill supports read-only verification only" and proactively offer a read-only alternative (e.g. "I can query the current value and status; you apply the change after confirming").
|
|
38
|
+
5. **Confirm non-local targets first**: before connecting to any database other than `127.0.0.1` / `localhost`, confirm the target address and purpose with the user.
|
|
39
|
+
6. **Read-only account in production**: production or shared environments must use a dedicated read-only MySQL account and Redis ACL user; see [references/security.md](references/security.md).
|
|
40
|
+
|
|
41
|
+
## Environment setup
|
|
42
|
+
|
|
43
|
+
Script dependencies are installed **inside the skill directory** — the `skills/db-query` folder of the dsh-switchman bundle (reported as this skill's base directory), so they never touch your project. After the bundle is updated (e.g. its checkout is pulled or reinstalled), re-run the setup once.
|
|
44
|
+
Before first use, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# from the skill root
|
|
48
|
+
bash scripts/setup.sh
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This checks Node.js 18+, installs `mysql2`, `redis` (node-redis v4), `dotenv` into the skill directory, and runs the built-in tests.
|
|
52
|
+
|
|
53
|
+
## Connection config & password safety
|
|
54
|
+
|
|
55
|
+
### Precedence
|
|
56
|
+
|
|
57
|
+
**CLI options > explicit `--env-file` > process env vars > defaults**
|
|
58
|
+
|
|
59
|
+
`--max-value-bytes` shares the env var `DB_QUERY_MAX_VALUE_BYTES` (common to MySQL/Redis).
|
|
60
|
+
|
|
61
|
+
### MySQL env vars
|
|
62
|
+
|
|
63
|
+
| CLI option | Env var | Default | Notes |
|
|
64
|
+
|----------|----------|--------|------|
|
|
65
|
+
| `--host` | `DB_QUERY_MYSQL_HOST` | `127.0.0.1` | Host |
|
|
66
|
+
| `--port` | `DB_QUERY_MYSQL_PORT` | `3306` | Port |
|
|
67
|
+
| `--user` | `DB_QUERY_MYSQL_USER` | **(required)** | Username; no root default |
|
|
68
|
+
| `--password` | `DB_QUERY_MYSQL_PASSWORD` | empty | Password; prefer env vars |
|
|
69
|
+
| `--database` | `DB_QUERY_MYSQL_DATABASE` | empty | Database name; if empty, only queries without a default db work (`SHOW DATABASES`, `SELECT 1`, db-prefixed tables) |
|
|
70
|
+
| `--tls` | `DB_QUERY_MYSQL_TLS` | `false` | Enable TLS |
|
|
71
|
+
| `--ca-file` | `DB_QUERY_MYSQL_CA_FILE` | — | CA certificate path |
|
|
72
|
+
| `--connect-timeout-ms` | `DB_QUERY_MYSQL_CONNECT_TIMEOUT_MS` | `5000` | Connect timeout (max 30000) |
|
|
73
|
+
| `--timeout-ms` | `DB_QUERY_MYSQL_TIMEOUT_MS` | `10000` | Query timeout (max 60000) |
|
|
74
|
+
| `--max-rows` | `DB_QUERY_MYSQL_MAX_ROWS` | `200` | Max rows returned (max 5000) |
|
|
75
|
+
| `--max-value-bytes` | `DB_QUERY_MAX_VALUE_BYTES` | `4096` | Max bytes per field value |
|
|
76
|
+
| `--format` | `DB_QUERY_MYSQL_FORMAT` | `table` | Output format: `table` / `json` |
|
|
77
|
+
|
|
78
|
+
### Redis env vars
|
|
79
|
+
|
|
80
|
+
| CLI option | Env var | Default | Notes |
|
|
81
|
+
|----------|----------|--------|------|
|
|
82
|
+
| `--host` | `DB_QUERY_REDIS_HOST` | `127.0.0.1` | Host |
|
|
83
|
+
| `--port` | `DB_QUERY_REDIS_PORT` | `6379` | Port |
|
|
84
|
+
| `--user` | `DB_QUERY_REDIS_USER` | `default` | Username |
|
|
85
|
+
| `--password` | `DB_QUERY_REDIS_PASSWORD` | empty | Password; prefer env vars |
|
|
86
|
+
| `--db` | `DB_QUERY_REDIS_DB` | `0` | Database number (connect option only; SELECT command is not exposed) |
|
|
87
|
+
| `--tls` | `DB_QUERY_REDIS_TLS` | `false` | Enable TLS |
|
|
88
|
+
| `--ca-file` | `DB_QUERY_REDIS_CA_FILE` | — | CA certificate path |
|
|
89
|
+
| `--connect-timeout-ms` | `DB_QUERY_REDIS_CONNECT_TIMEOUT_MS` | `5000` | Connect timeout (max 30000) |
|
|
90
|
+
| `--timeout-ms` | `DB_QUERY_REDIS_TIMEOUT_MS` | `10000` | Command timeout (max 60000) |
|
|
91
|
+
| `--max-items` | `DB_QUERY_REDIS_MAX_ITEMS` | `200` | Max items returned (max 5000) |
|
|
92
|
+
| `--max-value-bytes` | `DB_QUERY_MAX_VALUE_BYTES` | `4096` | Max bytes per value |
|
|
93
|
+
| `--format` | `DB_QUERY_REDIS_FORMAT` | `table` | Output format: `table` / `json` |
|
|
94
|
+
|
|
95
|
+
### Password safety
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# ❌ Don't do this (password lands in shell history)
|
|
99
|
+
./scripts/mysql-query.js --user=admin --password=secret --database=mydb 'SELECT 1'
|
|
100
|
+
|
|
101
|
+
# ✅ Recommended: env var
|
|
102
|
+
export DB_QUERY_MYSQL_PASSWORD=secret
|
|
103
|
+
./scripts/mysql-query.js --user=admin --database=mydb 'SELECT 1'
|
|
104
|
+
|
|
105
|
+
# ✅ Recommended: env file (0600 permissions)
|
|
106
|
+
./scripts/mysql-query.js --env-file=~/.db-query.env --user=admin --database=mydb 'SELECT 1'
|
|
107
|
+
|
|
108
|
+
# ✅ Recommended: same for Redis
|
|
109
|
+
export DB_QUERY_REDIS_PASSWORD=secret
|
|
110
|
+
./scripts/redis-query.js --db=0 -- GET user:42
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Standard workflow
|
|
114
|
+
|
|
115
|
+
1. **Confirm target and verification goal**: what to query (which table, fields, key) and why (bug investigation, deployment check, consistency). If the access address or credentials are unknown, stop and ask the user; never hunt for them yourself.
|
|
116
|
+
2. **Write the narrowest query**: prefer primary-key lookups over full scans; add time ranges and LIMIT where possible.
|
|
117
|
+
3. **Offline validation**: validate SQL/command syntax with `--validate-only` first to confirm it passes safety checks.
|
|
118
|
+
4. **Confirm non-local targets**: any non-local database must be confirmed with the user first.
|
|
119
|
+
5. **Execute**: run the script and read the output.
|
|
120
|
+
6. **Check truncation/timeouts**: if results are truncated (`[TRUNCATED]` marker) or time out, refine the query and re-run.
|
|
121
|
+
7. **Summarize findings and uncertainties**: state conclusions and clearly flag uncertainties (e.g. time-window inconsistencies, possible misses from truncation).
|
|
122
|
+
|
|
123
|
+
## MySQL query guide
|
|
124
|
+
|
|
125
|
+
### Invocation
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
./scripts/mysql-query.js [connection options] 'SELECT ...'
|
|
129
|
+
./scripts/mysql-query.js [connection options] --file=/abs/path.sql
|
|
130
|
+
./scripts/mysql-query.js --validate-only 'SELECT ...'
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Allowed statements
|
|
134
|
+
|
|
135
|
+
| Type | Example |
|
|
136
|
+
|------|------|
|
|
137
|
+
| `SELECT` | `SELECT id, name FROM users WHERE id = 42` |
|
|
138
|
+
| `WITH...SELECT` (CTE) | `WITH cte AS (SELECT ...) SELECT * FROM cte` |
|
|
139
|
+
| `SHOW` | `SHOW TABLES`, `SHOW CREATE TABLE users` |
|
|
140
|
+
| `DESCRIBE` / `DESC` | `DESCRIBE users` |
|
|
141
|
+
| `EXPLAIN SELECT` | `EXPLAIN SELECT * FROM users WHERE id = 42` |
|
|
142
|
+
|
|
143
|
+
> Note: `EXPLAIN ANALYZE` is **not supported** (security trade-off).
|
|
144
|
+
|
|
145
|
+
### LIMIT & timeout rules
|
|
146
|
+
|
|
147
|
+
- Without `LIMIT` in the SQL, the script **auto-injects** a top-level `LIMIT` (default 200, adjustable via `--max-rows`, max 5000).
|
|
148
|
+
- With `LIMIT` present, only **plain integers** are accepted (`LIMIT ?`, `LIMIT @a`, `LIMIT 1+1` and other expressions/placeholders/variables are rejected), and the count is checked against `--max-rows`; `LIMIT offset, count` and `LIMIT n OFFSET m` validate the count part.
|
|
149
|
+
- Query timeout defaults to 10000ms (adjustable via `--timeout-ms`, max 60000ms).
|
|
150
|
+
|
|
151
|
+
### Practical read-only examples
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Connectivity test
|
|
155
|
+
./scripts/mysql-query.js --user=reader --database=mydb 'SELECT 1'
|
|
156
|
+
|
|
157
|
+
# Single record by primary key
|
|
158
|
+
./scripts/mysql-query.js --user=reader --database=mydb \
|
|
159
|
+
'SELECT id, username, status, created_at FROM users WHERE id = 42'
|
|
160
|
+
|
|
161
|
+
# Recent records by time range
|
|
162
|
+
./scripts/mysql-query.js --user=reader --database=mydb --max-rows=50 \
|
|
163
|
+
"SELECT id, status, updated_at FROM orders WHERE created_at >= '2024-01-01' ORDER BY created_at DESC"
|
|
164
|
+
|
|
165
|
+
# Aggregate counts
|
|
166
|
+
./scripts/mysql-query.js --user=reader --database=mydb \
|
|
167
|
+
'SELECT status, COUNT(*) AS cnt FROM users GROUP BY status'
|
|
168
|
+
|
|
169
|
+
# Table structure
|
|
170
|
+
./scripts/mysql-query.js --user=reader --database=mydb 'DESCRIBE users'
|
|
171
|
+
|
|
172
|
+
# Execution plan
|
|
173
|
+
./scripts/mysql-query.js --user=reader --database=mydb \
|
|
174
|
+
'EXPLAIN SELECT * FROM orders WHERE user_id = 100'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Redis query guide
|
|
178
|
+
|
|
179
|
+
### Invocation
|
|
180
|
+
|
|
181
|
+
Use `--` to separate connection options from the Redis command:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
./scripts/redis-query.js [connection options] -- <command> [args...]
|
|
185
|
+
./scripts/redis-query.js --validate-only -- <command> [args...]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Whitelist by category
|
|
189
|
+
|
|
190
|
+
| Category | Commands |
|
|
191
|
+
|------|------|
|
|
192
|
+
| **General** | PING, EXISTS, TYPE, TTL, PTTL, SCAN, KEYS†, DBSIZE, TIME |
|
|
193
|
+
| **String** | GET, MGET, STRLEN |
|
|
194
|
+
| **Hash** | HGET, HMGET, HEXISTS, HLEN, HGETALL†, HKEYS†, HVALS†, HSCAN |
|
|
195
|
+
| **List** | LINDEX, LLEN, LRANGE |
|
|
196
|
+
| **Set** | SISMEMBER, SCARD, SMEMBERS†, SSCAN |
|
|
197
|
+
| **ZSet** | ZCARD, ZCOUNT, ZSCORE, ZRANK, ZREVRANK, ZRANGE, ZRANGEBYSCORE, ZSCAN |
|
|
198
|
+
| **Server info** | INFO, CLIENT LIST, CONFIG GET† |
|
|
199
|
+
|
|
200
|
+
> † Requires `--allow-unbounded` (in production prefer SCAN over KEYS/HGETALL and other full-key commands).
|
|
201
|
+
|
|
202
|
+
### TYPE/TTL first, then query by type
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Step 1: confirm the key exists and check its type
|
|
206
|
+
./scripts/redis-query.js -- TTL user:42
|
|
207
|
+
./scripts/redis-query.js -- TYPE user:42
|
|
208
|
+
|
|
209
|
+
# Step 2: pick the command by type
|
|
210
|
+
# if it's a hash
|
|
211
|
+
./scripts/redis-query.js -- HGET user:42 name
|
|
212
|
+
./scripts/redis-query.js -- HGETALL user:42 # full dump (needs --allow-unbounded)
|
|
213
|
+
./scripts/redis-query.js -- HSCAN user:42 0 COUNT 100 # safe batching
|
|
214
|
+
|
|
215
|
+
# if it's a string
|
|
216
|
+
./scripts/redis-query.js -- GET user:42
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Safe per-type examples
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Connectivity test
|
|
223
|
+
./scripts/redis-query.js -- PING
|
|
224
|
+
|
|
225
|
+
# String: batch-check keys
|
|
226
|
+
./scripts/redis-query.js -- MGET config:timeout config:max-retry
|
|
227
|
+
|
|
228
|
+
# Hash: batched scan
|
|
229
|
+
./scripts/redis-query.js -- HSCAN session:abc123 0 COUNT 50
|
|
230
|
+
|
|
231
|
+
# List: head/tail of a queue
|
|
232
|
+
./scripts/redis-query.js -- LRANGE task:queue 0 9
|
|
233
|
+
./scripts/redis-query.js -- LLEN task:queue
|
|
234
|
+
|
|
235
|
+
# Set: membership check
|
|
236
|
+
./scripts/redis-query.js -- SISMEMBER online:users user:42
|
|
237
|
+
./scripts/redis-query.js -- SCARD online:users
|
|
238
|
+
|
|
239
|
+
# ZSet: score range
|
|
240
|
+
./scripts/redis-query.js -- ZRANGEBYSCORE leaderboard 100 999 WITHSCORES
|
|
241
|
+
./scripts/redis-query.js -- ZRANK leaderboard user:42
|
|
242
|
+
|
|
243
|
+
# TTL check
|
|
244
|
+
./scripts/redis-query.js -- TTL cache:homepage
|
|
245
|
+
# -1 = no expiry, -2 = key missing, >= 0 = seconds remaining
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Cross MySQL/Redis verification
|
|
249
|
+
|
|
250
|
+
Recommended steps for consistency checks:
|
|
251
|
+
|
|
252
|
+
1. **Get business keys from MySQL**: query the key set to verify first.
|
|
253
|
+
2. **Targeted Redis lookups**: check Redis per key (or batched); avoid `KEYS *` on the whole db.
|
|
254
|
+
3. **Compare differences**: compare values and TTL field by field.
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Step 1: recently updated user IDs from MySQL
|
|
258
|
+
./scripts/mysql-query.js --user=reader --database=mydb --max-rows=20 \
|
|
259
|
+
"SELECT id, username, updated_at FROM users WHERE updated_at > '2024-06-01'"
|
|
260
|
+
|
|
261
|
+
# Step 2: verify each in Redis
|
|
262
|
+
./scripts/redis-query.js -- GET user:1001
|
|
263
|
+
./scripts/redis-query.js -- TTL user:1001
|
|
264
|
+
./scripts/redis-query.js -- TYPE user:1001
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> ⚠️ **Time-window warning**: data may change between the two reads; you **cannot claim transactional consistency**. Always state "checked at <time>; concurrent changes possible in between" in conclusions.
|
|
268
|
+
|
|
269
|
+
## Output & error handling
|
|
270
|
+
|
|
271
|
+
### Output format
|
|
272
|
+
|
|
273
|
+
Controlled by `--format`:
|
|
274
|
+
|
|
275
|
+
| Format | Notes |
|
|
276
|
+
|------|------|
|
|
277
|
+
| `table` | Table output, human-readable |
|
|
278
|
+
| `json` | JSON output, machine-readable |
|
|
279
|
+
|
|
280
|
+
### Type conversion
|
|
281
|
+
|
|
282
|
+
- Date fields output as strings (`dateStrings` mode); no JS Date conversion.
|
|
283
|
+
- BIGINT / DECIMAL output as strings to avoid precision loss.
|
|
284
|
+
- JSON fields output as formatted strings.
|
|
285
|
+
- Buffer / binary fields rendered as base64 or hex strings.
|
|
286
|
+
|
|
287
|
+
### Truncation markers
|
|
288
|
+
|
|
289
|
+
- Field values exceeding `--max-value-bytes` (default 4096) are marked `[TRUNCATED]`.
|
|
290
|
+
- Row counts exceeding `--max-rows` (MySQL) / `--max-items` (Redis) truncate the result set with a trailing notice.
|
|
291
|
+
- On truncation, refine the query and re-run; **never draw conclusions from truncated results**.
|
|
292
|
+
|
|
293
|
+
### Error output
|
|
294
|
+
|
|
295
|
+
Errors are classified by code with sanitized messages, so failures are easy to tell apart:
|
|
296
|
+
|
|
297
|
+
- **Connection failure [code]: message** — connection/auth/handshake errors (`ECONNREFUSED`, `ER_ACCESS_DENIED_ERROR`, `WRONGPASS`, etc.) — couldn't connect.
|
|
298
|
+
- **Query execution error [code]: message** — SQL/command execution errors (`ER_BAD_FIELD_ERROR`(1054), `ER_PARSE_ERROR`, `WRONGTYPE`, etc.) — connected, but the SQL/command itself is wrong (bad column name, type mismatch, etc.).
|
|
299
|
+
- Messages are first-line only and truncated (≤200 chars), inline credentials masked (`://user:pass@host` → `://user:***@host`); passwords from connection strings or full stacks are never echoed.
|
|
300
|
+
- SQL/command validation failures print the concrete rejection reason (e.g. "contains comments", "contains a write operation").
|
|
301
|
+
|
|
302
|
+
> **"Connection failure" → investigate network/credentials; "query execution error" → fix the SQL/command from the message, don't chase connection issues.**
|
|
303
|
+
|
|
304
|
+
## Post-run self-check
|
|
305
|
+
|
|
306
|
+
After every execution, verify:
|
|
307
|
+
|
|
308
|
+
- [ ] **Read-only**: SQL is SELECT/SHOW/DESCRIBE/EXPLAIN; Redis command is whitelisted.
|
|
309
|
+
- [ ] **Right target**: connected to the intended db/instance, not production (unless explicitly confirmed).
|
|
310
|
+
- [ ] **No truncation**: results not truncated (`[TRUNCATED]` marker); re-query if they were.
|
|
311
|
+
- [ ] **Conclusions hold**: results actually support the claim; flag any uncertainties.
|
|
312
|
+
- [ ] **Sensitive data**: mask as needed when reporting (phone numbers, password hashes, etc.).
|
|
313
|
+
|
|
314
|
+
## Language
|
|
315
|
+
|
|
316
|
+
English by default; use the project's configured language when a `[SWITCHMAN:LANG]` line in the system prompt specifies one, or when the user explicitly requests it.
|
|
317
|
+
|
|
318
|
+
## References
|
|
319
|
+
|
|
320
|
+
- [references/security.md](references/security.md) — read-only security model in depth
|
|
321
|
+
- [references/mysql.md](references/mysql.md) — MySQL syntax matrix & query recipes
|
|
322
|
+
- [references/redis.md](references/redis.md) — Redis command whitelist & query patterns
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-query-skill",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "db-query-skill",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"dotenv": "16.6.1",
|
|
12
|
+
"mysql2": "3.12.0",
|
|
13
|
+
"redis": "4.7.1"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"node_modules/@redis/bloom": {
|
|
21
|
+
"version": "1.2.0",
|
|
22
|
+
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
|
23
|
+
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@redis/client": "^1.0.0"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"node_modules/@redis/client": {
|
|
30
|
+
"version": "1.6.1",
|
|
31
|
+
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
|
32
|
+
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"cluster-key-slot": "1.1.2",
|
|
36
|
+
"generic-pool": "3.9.0",
|
|
37
|
+
"yallist": "4.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=14"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"node_modules/@redis/graph": {
|
|
44
|
+
"version": "1.1.1",
|
|
45
|
+
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
|
46
|
+
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@redis/client": "^1.0.0"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"node_modules/@redis/json": {
|
|
53
|
+
"version": "1.0.7",
|
|
54
|
+
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
|
55
|
+
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@redis/client": "^1.0.0"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"node_modules/@redis/search": {
|
|
62
|
+
"version": "1.2.0",
|
|
63
|
+
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
|
|
64
|
+
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@redis/client": "^1.0.0"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"node_modules/@redis/time-series": {
|
|
71
|
+
"version": "1.1.0",
|
|
72
|
+
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
|
|
73
|
+
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
|
|
74
|
+
"license": "MIT",
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"@redis/client": "^1.0.0"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"node_modules/aws-ssl-profiles": {
|
|
80
|
+
"version": "1.1.2",
|
|
81
|
+
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
|
82
|
+
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
|
|
83
|
+
"license": "MIT",
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">= 6.0.0"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"node_modules/cluster-key-slot": {
|
|
89
|
+
"version": "1.1.2",
|
|
90
|
+
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
|
91
|
+
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
|
92
|
+
"license": "Apache-2.0",
|
|
93
|
+
"engines": {
|
|
94
|
+
"node": ">=0.10.0"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"node_modules/denque": {
|
|
98
|
+
"version": "2.1.0",
|
|
99
|
+
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
|
100
|
+
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
|
101
|
+
"license": "Apache-2.0",
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">=0.10"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"node_modules/dotenv": {
|
|
107
|
+
"version": "16.6.1",
|
|
108
|
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
|
109
|
+
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
|
110
|
+
"license": "BSD-2-Clause",
|
|
111
|
+
"engines": {
|
|
112
|
+
"node": ">=12"
|
|
113
|
+
},
|
|
114
|
+
"funding": {
|
|
115
|
+
"url": "https://dotenvx.com"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"node_modules/generate-function": {
|
|
119
|
+
"version": "2.3.1",
|
|
120
|
+
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
|
121
|
+
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
|
122
|
+
"license": "MIT",
|
|
123
|
+
"dependencies": {
|
|
124
|
+
"is-property": "^1.0.2"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"node_modules/generic-pool": {
|
|
128
|
+
"version": "3.9.0",
|
|
129
|
+
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
|
130
|
+
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
|
131
|
+
"license": "MIT",
|
|
132
|
+
"engines": {
|
|
133
|
+
"node": ">= 4"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"node_modules/iconv-lite": {
|
|
137
|
+
"version": "0.6.3",
|
|
138
|
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
139
|
+
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
140
|
+
"license": "MIT",
|
|
141
|
+
"dependencies": {
|
|
142
|
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
143
|
+
},
|
|
144
|
+
"engines": {
|
|
145
|
+
"node": ">=0.10.0"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"node_modules/is-property": {
|
|
149
|
+
"version": "1.0.2",
|
|
150
|
+
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
|
151
|
+
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
|
152
|
+
"license": "MIT"
|
|
153
|
+
},
|
|
154
|
+
"node_modules/long": {
|
|
155
|
+
"version": "5.3.2",
|
|
156
|
+
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
|
157
|
+
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
|
158
|
+
"license": "Apache-2.0"
|
|
159
|
+
},
|
|
160
|
+
"node_modules/lru.min": {
|
|
161
|
+
"version": "1.1.5",
|
|
162
|
+
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.5.tgz",
|
|
163
|
+
"integrity": "sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==",
|
|
164
|
+
"license": "MIT",
|
|
165
|
+
"engines": {
|
|
166
|
+
"bun": ">=1.0.0",
|
|
167
|
+
"deno": ">=1.30.0",
|
|
168
|
+
"node": ">=8.0.0"
|
|
169
|
+
},
|
|
170
|
+
"funding": {
|
|
171
|
+
"type": "github",
|
|
172
|
+
"url": "https://github.com/sponsors/wellwelwel"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"node_modules/mysql2": {
|
|
176
|
+
"version": "3.12.0",
|
|
177
|
+
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz",
|
|
178
|
+
"integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==",
|
|
179
|
+
"license": "MIT",
|
|
180
|
+
"dependencies": {
|
|
181
|
+
"aws-ssl-profiles": "^1.1.1",
|
|
182
|
+
"denque": "^2.1.0",
|
|
183
|
+
"generate-function": "^2.3.1",
|
|
184
|
+
"iconv-lite": "^0.6.3",
|
|
185
|
+
"long": "^5.2.1",
|
|
186
|
+
"lru.min": "^1.0.0",
|
|
187
|
+
"named-placeholders": "^1.1.3",
|
|
188
|
+
"seq-queue": "^0.0.5",
|
|
189
|
+
"sqlstring": "^2.3.2"
|
|
190
|
+
},
|
|
191
|
+
"engines": {
|
|
192
|
+
"node": ">= 8.0"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"node_modules/named-placeholders": {
|
|
196
|
+
"version": "1.1.6",
|
|
197
|
+
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
|
|
198
|
+
"integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
|
|
199
|
+
"license": "MIT",
|
|
200
|
+
"dependencies": {
|
|
201
|
+
"lru.min": "^1.1.0"
|
|
202
|
+
},
|
|
203
|
+
"engines": {
|
|
204
|
+
"node": ">=8.0.0"
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"node_modules/redis": {
|
|
208
|
+
"version": "4.7.1",
|
|
209
|
+
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz",
|
|
210
|
+
"integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==",
|
|
211
|
+
"license": "MIT",
|
|
212
|
+
"workspaces": [
|
|
213
|
+
"./packages/*"
|
|
214
|
+
],
|
|
215
|
+
"dependencies": {
|
|
216
|
+
"@redis/bloom": "1.2.0",
|
|
217
|
+
"@redis/client": "1.6.1",
|
|
218
|
+
"@redis/graph": "1.1.1",
|
|
219
|
+
"@redis/json": "1.0.7",
|
|
220
|
+
"@redis/search": "1.2.0",
|
|
221
|
+
"@redis/time-series": "1.1.0"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"node_modules/safer-buffer": {
|
|
225
|
+
"version": "2.1.2",
|
|
226
|
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
227
|
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
|
228
|
+
"license": "MIT"
|
|
229
|
+
},
|
|
230
|
+
"node_modules/seq-queue": {
|
|
231
|
+
"version": "0.0.5",
|
|
232
|
+
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
|
|
233
|
+
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
|
234
|
+
},
|
|
235
|
+
"node_modules/sqlstring": {
|
|
236
|
+
"version": "2.3.3",
|
|
237
|
+
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
|
238
|
+
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
|
|
239
|
+
"license": "MIT",
|
|
240
|
+
"engines": {
|
|
241
|
+
"node": ">= 0.6"
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"node_modules/yallist": {
|
|
245
|
+
"version": "4.0.0",
|
|
246
|
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
247
|
+
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
|
248
|
+
"license": "ISC"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-query-skill",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "pi 技能:数据库只读查询核验(MySQL / Redis)",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test tests/*.test.js",
|
|
12
|
+
"setup": "bash scripts/setup.sh"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"dotenv": "16.6.1",
|
|
16
|
+
"mysql2": "3.12.0",
|
|
17
|
+
"redis": "4.7.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {}
|
|
20
|
+
}
|