@yejiming/dsh-data-agent 0.0.6 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +142 -119
- package/README.md +138 -118
- package/cordis.patch.yml +8 -9
- package/lib/client.js +42423 -524
- package/lib/client.js.map +1 -1
- package/lib/command-LFgLb6el.js +875 -0
- package/lib/command.js +2 -0
- package/lib/connections-WmjuUrDj.js +1608 -0
- package/lib/defaults-DP4RyRh1.js +21 -0
- package/lib/index.js +265 -68
- package/lib/routes.js +94 -170
- package/lib/tool-Dka6RyEp.js +1128 -0
- package/lib/tool.js +1 -426
- package/lib/types/analysis.d.ts +1071 -0
- package/lib/types/client/AnalysisChart.d.ts +26 -0
- package/lib/types/client/AnalysisDashboard.d.ts +30 -0
- package/lib/types/client/DataAgentWorkbench.d.ts +2 -2
- package/lib/types/client/analysis-charts.d.ts +40 -0
- package/lib/types/client/analysis-view-model.d.ts +44 -0
- package/lib/types/client/index.d.ts +3 -4
- package/lib/types/client/locales.d.ts +66 -0
- package/lib/types/client/persistence.d.ts +6 -1
- package/lib/types/client-discovery.d.ts +45 -0
- package/lib/types/clients.d.ts +17 -10
- package/lib/types/command.d.ts +41 -0
- package/lib/types/connections.d.ts +115 -40
- package/lib/types/defaults.d.ts +2 -0
- package/lib/types/index.d.ts +109 -63
- package/lib/types/routes.d.ts +25 -91
- package/lib/types/sql.d.ts +1 -1
- package/lib/types/storage.d.ts +70 -0
- package/lib/types/structured-read.d.ts +50 -0
- package/lib/types/tool.d.ts +29 -20
- package/lib/types/tui-connection-form.d.ts +98 -0
- package/package.json +65 -4
- package/preset/data-agent/agent.cordis.yml +22 -25
- package/preset/data-agent/preset.yml +1 -1
- package/lib/defaults-Bac6QvNt.js +0 -911
- package/lib/query-CmhTFklw.js +0 -86
package/lib/tool.js
CHANGED
|
@@ -1,427 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as runClientQuery } from "./query-CmhTFklw.js";
|
|
3
|
-
import z from "schemastery";
|
|
4
|
-
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
5
|
-
//#region src/structured.ts
|
|
6
|
-
function normalizeNewlines(text) {
|
|
7
|
-
return text.replace(/\r\n?/g, "\n");
|
|
8
|
-
}
|
|
9
|
-
function splitLine(line, delimiter) {
|
|
10
|
-
return line.split(delimiter);
|
|
11
|
-
}
|
|
12
|
-
/** Make column names valid unique JSON object keys. */
|
|
13
|
-
function uniqueColumns(columns) {
|
|
14
|
-
const used = /* @__PURE__ */ new Set();
|
|
15
|
-
return columns.map((raw, index) => {
|
|
16
|
-
let name = raw.trim();
|
|
17
|
-
if (name.length === 0) name = `column_${index + 1}`;
|
|
18
|
-
if (used.has(name)) {
|
|
19
|
-
let suffix = 2;
|
|
20
|
-
while (used.has(`${name}_${suffix}`)) suffix += 1;
|
|
21
|
-
name = `${name}_${suffix}`;
|
|
22
|
-
}
|
|
23
|
-
used.add(name);
|
|
24
|
-
return name;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function rowObject(columns, fields) {
|
|
28
|
-
const row = {};
|
|
29
|
-
for (let index = 0; index < columns.length; index += 1) row[columns[index]] = fields[index] ?? null;
|
|
30
|
-
return row;
|
|
31
|
-
}
|
|
32
|
-
function emptyOutput() {
|
|
33
|
-
return {
|
|
34
|
-
columns: [],
|
|
35
|
-
rows: [],
|
|
36
|
-
rowLimitExceeded: false
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function skipLeadingBlank(lines) {
|
|
40
|
-
let index = 0;
|
|
41
|
-
while (index < lines.length && lines[index].trim().length === 0) index += 1;
|
|
42
|
-
return index;
|
|
43
|
-
}
|
|
44
|
-
/** PostgreSQL `-A` appends a `(N rows)` / `(N row)` footer after SELECT output. */
|
|
45
|
-
function isPostgresFooter(line) {
|
|
46
|
-
return /^\(\d+ rows?\)$/.test(line.trim());
|
|
47
|
-
}
|
|
48
|
-
function parseDelimited(stdout, delimiter, maxRows, skipFooter = false) {
|
|
49
|
-
const lines = normalizeNewlines(stdout).split("\n");
|
|
50
|
-
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
51
|
-
const headerIndex = skipLeadingBlank(lines);
|
|
52
|
-
if (headerIndex >= lines.length) return emptyOutput();
|
|
53
|
-
const columns = uniqueColumns(splitLine(lines[headerIndex], delimiter));
|
|
54
|
-
const rows = [];
|
|
55
|
-
let rowLimitExceeded = false;
|
|
56
|
-
for (let index = headerIndex + 1; index < lines.length; index += 1) {
|
|
57
|
-
const line = lines[index];
|
|
58
|
-
if (skipFooter && isPostgresFooter(line)) continue;
|
|
59
|
-
if (rows.length >= maxRows) {
|
|
60
|
-
rowLimitExceeded = true;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
rows.push(rowObject(columns, splitLine(line, delimiter)));
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
columns,
|
|
67
|
-
rows,
|
|
68
|
-
rowLimitExceeded
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
/** Minimal RFC-4180-style parser for sqlite3 `-csv` output. */
|
|
72
|
-
function parseCsv(text) {
|
|
73
|
-
const records = [];
|
|
74
|
-
let record = [];
|
|
75
|
-
let field = "";
|
|
76
|
-
let quoted = false;
|
|
77
|
-
let index = 0;
|
|
78
|
-
const pushField = () => {
|
|
79
|
-
record.push(field);
|
|
80
|
-
field = "";
|
|
81
|
-
};
|
|
82
|
-
const pushRecord = () => {
|
|
83
|
-
pushField();
|
|
84
|
-
records.push(record);
|
|
85
|
-
record = [];
|
|
86
|
-
};
|
|
87
|
-
while (index < text.length) {
|
|
88
|
-
const char = text[index];
|
|
89
|
-
if (quoted) {
|
|
90
|
-
if (char === "\"") {
|
|
91
|
-
if (text[index + 1] === "\"") {
|
|
92
|
-
field += "\"";
|
|
93
|
-
index += 2;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
quoted = false;
|
|
97
|
-
index += 1;
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
field += char;
|
|
101
|
-
index += 1;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (char === "\"" && field.length === 0) {
|
|
105
|
-
quoted = true;
|
|
106
|
-
index += 1;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (char === ",") {
|
|
110
|
-
pushField();
|
|
111
|
-
index += 1;
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (char === "\n") {
|
|
115
|
-
pushRecord();
|
|
116
|
-
index += 1;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (char === "\r") {
|
|
120
|
-
if (text[index + 1] === "\n") index += 1;
|
|
121
|
-
pushRecord();
|
|
122
|
-
index += 1;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
field += char;
|
|
126
|
-
index += 1;
|
|
127
|
-
}
|
|
128
|
-
if (field.length > 0 || record.length > 0) pushRecord();
|
|
129
|
-
return records;
|
|
130
|
-
}
|
|
131
|
-
function parseCsvOutput(stdout, maxRows) {
|
|
132
|
-
const records = parseCsv(normalizeNewlines(stdout)).filter((record) => !(record.length === 1 && record[0] === ""));
|
|
133
|
-
if (records.length === 0) return emptyOutput();
|
|
134
|
-
const columns = uniqueColumns(records[0]);
|
|
135
|
-
const rows = [];
|
|
136
|
-
let rowLimitExceeded = false;
|
|
137
|
-
for (let index = 1; index < records.length; index += 1) {
|
|
138
|
-
if (rows.length >= maxRows) {
|
|
139
|
-
rowLimitExceeded = true;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
rows.push(rowObject(columns, records[index]));
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
columns,
|
|
146
|
-
rows,
|
|
147
|
-
rowLimitExceeded
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Parse one database type's structured-query stdout. The matching template is
|
|
152
|
-
* `buildStructuredQueryTemplate`: mysql tab-separated with a header, postgres
|
|
153
|
-
* pipe-separated with a header and row-count footer, sqlite CSV with a header,
|
|
154
|
-
* oracle pipe-separated with heading on, hive/impala tsv with a header.
|
|
155
|
-
*/
|
|
156
|
-
function parseStructuredQueryOutput(type, stdout, maxRows) {
|
|
157
|
-
switch (type) {
|
|
158
|
-
case "mysql": return parseDelimited(stdout, " ", maxRows);
|
|
159
|
-
case "postgres": return parseDelimited(stdout, "|", maxRows, true);
|
|
160
|
-
case "sqlite": return parseCsvOutput(stdout, maxRows);
|
|
161
|
-
case "oracle": return parseDelimited(stdout, "|", maxRows);
|
|
162
|
-
case "hive":
|
|
163
|
-
case "impala": return parseDelimited(stdout, " ", maxRows);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/tool.ts
|
|
168
|
-
/** Cordis plugin name (diagnostics only). */
|
|
169
|
-
const name = "data-agent-tool";
|
|
170
|
-
/** Services required before the tool can register. */
|
|
171
|
-
const inject = [
|
|
172
|
-
"tools",
|
|
173
|
-
"subprocess",
|
|
174
|
-
"dataAgentConnections"
|
|
175
|
-
];
|
|
176
|
-
/** Loader schema with deployment defaults (no library defaults). */
|
|
177
|
-
const Config = z.object({
|
|
178
|
-
queryTimeoutMs: z.number().step(1).min(1e3).default(DEFAULT_QUERY_TIMEOUT_MS),
|
|
179
|
-
maxResultChars: z.number().step(1).min(1024).default(DEFAULT_MAX_RESULT_CHARS),
|
|
180
|
-
maxRows: z.number().step(1).min(1).default(100),
|
|
181
|
-
readonly: z.boolean().default(false),
|
|
182
|
-
clients: clientsSchema
|
|
183
|
-
});
|
|
184
|
-
/** One-line tool-call label (newlines collapsed). */
|
|
185
|
-
function oneLine(sql) {
|
|
186
|
-
const line = sql.replace(/\s+/g, " ").trim();
|
|
187
|
-
return line.length > 80 ? `${line.slice(0, 77)}...` : line;
|
|
188
|
-
}
|
|
189
|
-
/** Format the raw terminal result. */
|
|
190
|
-
function formatResult(value) {
|
|
191
|
-
const parts = [];
|
|
192
|
-
if (value.stdout.length > 0) parts.push(value.stdout);
|
|
193
|
-
if (value.stderr.length > 0) parts.push(`[stderr]\n${value.stderr}`);
|
|
194
|
-
if (value.truncated) parts.push("… 输出超过上限,已截断(可缩小查询或增加 maxResultChars)");
|
|
195
|
-
if (value.exitCode !== 0) parts.push(`[exit code: ${value.exitCode ?? "signal"}]`);
|
|
196
|
-
return parts.join("\n");
|
|
197
|
-
}
|
|
198
|
-
/** Format the structured result as JSON text (the canonical value stays JSON). */
|
|
199
|
-
function formatStructuredResult(value) {
|
|
200
|
-
return "```json\n" + JSON.stringify(value, null, 2) + "\n```";
|
|
201
|
-
}
|
|
202
|
-
/** Look up the session connection, failing with the same message for every tool. */
|
|
203
|
-
function requireToolConnection(ctx, exec, toolName) {
|
|
204
|
-
const sessionId = exec.agent?.id;
|
|
205
|
-
if (sessionId === void 0) throw new Error(`${toolName}: 缺少会话上下文(agent loop 未注入)`);
|
|
206
|
-
const connection = ctx.dataAgentConnections.getWithSecret(sessionId);
|
|
207
|
-
if (connection === void 0) throw new Error(`请先在「数据库」标签页连接数据库,再使用 ${toolName}(未找到当前会话的连接)`);
|
|
208
|
-
return connection;
|
|
209
|
-
}
|
|
210
|
-
/** Empty and multi-statement checks shared by all three tools. */
|
|
211
|
-
function validateSingleSql(sql, toolName) {
|
|
212
|
-
if (sql.trim().length === 0) throw new Error(`${toolName}: sql 不能为空`);
|
|
213
|
-
assertSingleStatement(sql, toolName);
|
|
214
|
-
}
|
|
215
|
-
/** Query runner options with the deployment overrides applied. */
|
|
216
|
-
function runnerOptions(resolved, mode) {
|
|
217
|
-
return {
|
|
218
|
-
clients: resolved.clients,
|
|
219
|
-
timeoutMs: resolved.queryTimeoutMs,
|
|
220
|
-
maxResultChars: resolved.maxResultChars,
|
|
221
|
-
...mode !== void 0 ? { mode } : {}
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Mount the data-agent database tools: `sql-query` (structured read-only),
|
|
226
|
-
* `sql-write` (explicit write semantics), and `sqlcmd` (raw compatibility).
|
|
227
|
-
* @param ctx - the preset-scoped agent context.
|
|
228
|
-
* @param config - validated loader configuration.
|
|
229
|
-
*/
|
|
230
|
-
function apply(ctx, config) {
|
|
231
|
-
const resolved = {
|
|
232
|
-
queryTimeoutMs: config.queryTimeoutMs,
|
|
233
|
-
maxResultChars: config.maxResultChars,
|
|
234
|
-
maxRows: config.maxRows,
|
|
235
|
-
readonly: config.readonly,
|
|
236
|
-
clients: config.clients
|
|
237
|
-
};
|
|
238
|
-
ctx.tools.register(defineTool({
|
|
239
|
-
name: "sql-query",
|
|
240
|
-
description: `在已连接数据库上执行一条只读 SQL(SELECT/SHOW/DESCRIBE/EXPLAIN,SQLite 还含查询型 PRAGMA),返回结构化 JSON:{ columns, rows, affectedRows, elapsedMs, truncated }。SELECT 未写 LIMIT 时会自动限制为最多 ${resolved.maxRows} 行;所有结果最多返回 ${resolved.maxRows} 行。只执行单条语句;写操作请使用 sql-write,原始客户端输出请使用 sqlcmd。`,
|
|
241
|
-
parameters: { sql: {
|
|
242
|
-
type: "string",
|
|
243
|
-
required: true,
|
|
244
|
-
description: "一条只读 SQL,如 \"SELECT * FROM orders LIMIT 5;\"、\"SHOW TABLES;\"、\"DESCRIBE users;\""
|
|
245
|
-
} },
|
|
246
|
-
output: {
|
|
247
|
-
schema: {
|
|
248
|
-
type: "object",
|
|
249
|
-
properties: {
|
|
250
|
-
columns: {
|
|
251
|
-
type: "array",
|
|
252
|
-
items: { type: "string" },
|
|
253
|
-
required: true
|
|
254
|
-
},
|
|
255
|
-
rows: {
|
|
256
|
-
type: "array",
|
|
257
|
-
items: {
|
|
258
|
-
type: "object",
|
|
259
|
-
properties: {},
|
|
260
|
-
additionalProperties: true
|
|
261
|
-
},
|
|
262
|
-
required: true
|
|
263
|
-
},
|
|
264
|
-
affectedRows: {
|
|
265
|
-
type: "integer",
|
|
266
|
-
required: true
|
|
267
|
-
},
|
|
268
|
-
elapsedMs: {
|
|
269
|
-
type: "integer",
|
|
270
|
-
required: true
|
|
271
|
-
},
|
|
272
|
-
truncated: {
|
|
273
|
-
type: "boolean",
|
|
274
|
-
required: true
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
additionalProperties: false
|
|
278
|
-
},
|
|
279
|
-
render: (_args, value) => [{
|
|
280
|
-
type: "text",
|
|
281
|
-
text: formatStructuredResult(value)
|
|
282
|
-
}]
|
|
283
|
-
},
|
|
284
|
-
presentCall: (args) => ({
|
|
285
|
-
card: "generic",
|
|
286
|
-
kind: "read",
|
|
287
|
-
title: `sql-query ${oneLine(args.sql)}`,
|
|
288
|
-
rawInput: args.sql
|
|
289
|
-
}),
|
|
290
|
-
presentResult: (args, result) => ({
|
|
291
|
-
card: "generic",
|
|
292
|
-
title: `sql-query ${oneLine(args.sql)}`,
|
|
293
|
-
content: result.content
|
|
294
|
-
}),
|
|
295
|
-
async execute(args, exec) {
|
|
296
|
-
const connection = requireToolConnection(ctx, exec, "sql-query");
|
|
297
|
-
validateSingleSql(args.sql, "sql-query");
|
|
298
|
-
if (classifyStatement(args.sql, connection.type) !== "read") throw new Error("sql-query 只执行读语句(SELECT/SHOW/DESCRIBE/EXPLAIN,SQLite 还含查询型 PRAGMA);写语句请使用 sql-write");
|
|
299
|
-
const limitedSql = enforceReadRowLimit(args.sql, connection.type, resolved.maxRows);
|
|
300
|
-
const startedAt = Date.now();
|
|
301
|
-
const result = await runClientQuery(ctx, connection, limitedSql, runnerOptions(resolved, "structured"), exec.signal);
|
|
302
|
-
const elapsedMs = Date.now() - startedAt;
|
|
303
|
-
if (result.exitCode !== 0) {
|
|
304
|
-
const detail = result.stderr.trim() !== "" ? result.stderr.trim() : result.stdout.trim();
|
|
305
|
-
throw new Error(`sql-query 执行失败(exit ${result.exitCode}):${detail}`);
|
|
306
|
-
}
|
|
307
|
-
const parsed = parseStructuredQueryOutput(connection.type, result.stdout, resolved.maxRows);
|
|
308
|
-
return {
|
|
309
|
-
columns: parsed.columns,
|
|
310
|
-
rows: parsed.rows,
|
|
311
|
-
affectedRows: 0,
|
|
312
|
-
elapsedMs,
|
|
313
|
-
truncated: result.truncated || parsed.rowLimitExceeded
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
}));
|
|
317
|
-
ctx.tools.register(defineTool({
|
|
318
|
-
name: "sql-write",
|
|
319
|
-
description: "在已连接数据库上执行一条写/管理语句(INSERT/UPDATE/DELETE/DDL 等)。每次调用都是独立客户端进程并自动提交,只接受单条语句,不支持跨调用的多语句事务;如需原子性,请改用单条 SQL(如 INSERT ... SELECT)或数据库端脚本/存储过程。只读查询请使用 sql-query。",
|
|
320
|
-
parameters: { sql: {
|
|
321
|
-
type: "string",
|
|
322
|
-
required: true,
|
|
323
|
-
description: "一条写/管理 SQL,如 \"INSERT INTO t VALUES (1);\"、\"UPDATE t SET x=1;\"、\"CREATE INDEX idx_t_x ON t(x);\""
|
|
324
|
-
} },
|
|
325
|
-
output: {
|
|
326
|
-
schema: {
|
|
327
|
-
type: "object",
|
|
328
|
-
properties: {
|
|
329
|
-
exitCode: {
|
|
330
|
-
oneOf: [{ type: "integer" }, { type: "null" }],
|
|
331
|
-
required: true
|
|
332
|
-
},
|
|
333
|
-
stdout: {
|
|
334
|
-
type: "string",
|
|
335
|
-
required: true
|
|
336
|
-
},
|
|
337
|
-
stderr: {
|
|
338
|
-
type: "string",
|
|
339
|
-
required: true
|
|
340
|
-
},
|
|
341
|
-
truncated: {
|
|
342
|
-
type: "boolean",
|
|
343
|
-
required: true
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
additionalProperties: false
|
|
347
|
-
},
|
|
348
|
-
render: (_args, value) => [{
|
|
349
|
-
type: "text",
|
|
350
|
-
text: formatResult(value)
|
|
351
|
-
}]
|
|
352
|
-
},
|
|
353
|
-
presentCall: (args) => ({
|
|
354
|
-
card: "terminal",
|
|
355
|
-
title: `sql-write ${oneLine(args.sql)}`,
|
|
356
|
-
description: "执行一条写/管理 SQL(自动提交)"
|
|
357
|
-
}),
|
|
358
|
-
presentResult: (args, result) => ({
|
|
359
|
-
card: "terminal",
|
|
360
|
-
title: `sql-write ${oneLine(args.sql)}`,
|
|
361
|
-
content: result.content
|
|
362
|
-
}),
|
|
363
|
-
async execute(args, exec) {
|
|
364
|
-
const connection = requireToolConnection(ctx, exec, "sql-write");
|
|
365
|
-
validateSingleSql(args.sql, "sql-write");
|
|
366
|
-
if (classifyStatement(args.sql, connection.type) === "read") throw new Error("sql-write 只执行写/管理语句;只读查询请使用 sql-query");
|
|
367
|
-
if (connection.readonly ?? resolved.readonly) throw new Error("当前连接为只读模式,sql-write 拒绝执行写/管理语句(仅放行 SELECT/SHOW/DESCRIBE/EXPLAIN/查询型 PRAGMA 等)");
|
|
368
|
-
return runClientQuery(ctx, connection, args.sql, runnerOptions(resolved), exec.signal);
|
|
369
|
-
}
|
|
370
|
-
}));
|
|
371
|
-
ctx.tools.register(defineTool({
|
|
372
|
-
name: "sqlcmd",
|
|
373
|
-
description: `在已连接数据库上执行一条 SQL 或客户端命令(如 SHOW TABLES、DESCRIBE users),返回原始 exitCode/stdout/stderr 文本。新调用优先使用 sql-query(结构化只读结果)和 sql-write(明确写语义)。一次只执行一条语句;读 SELECT 会自动限制最多 ${resolved.maxRows} 行;每次调用为独立客户端进程并自动提交。`,
|
|
374
|
-
parameters: { sql: {
|
|
375
|
-
type: "string",
|
|
376
|
-
required: true,
|
|
377
|
-
description: "一条 SQL 文本(或客户端命令),如 \"SHOW TABLES;\"、\"DESCRIBE users;\"、\"SELECT * FROM orders LIMIT 5;\""
|
|
378
|
-
} },
|
|
379
|
-
output: {
|
|
380
|
-
schema: {
|
|
381
|
-
type: "object",
|
|
382
|
-
properties: {
|
|
383
|
-
exitCode: {
|
|
384
|
-
oneOf: [{ type: "integer" }, { type: "null" }],
|
|
385
|
-
required: true
|
|
386
|
-
},
|
|
387
|
-
stdout: {
|
|
388
|
-
type: "string",
|
|
389
|
-
required: true
|
|
390
|
-
},
|
|
391
|
-
stderr: {
|
|
392
|
-
type: "string",
|
|
393
|
-
required: true
|
|
394
|
-
},
|
|
395
|
-
truncated: {
|
|
396
|
-
type: "boolean",
|
|
397
|
-
required: true
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
additionalProperties: false
|
|
401
|
-
},
|
|
402
|
-
render: (_args, value) => [{
|
|
403
|
-
type: "text",
|
|
404
|
-
text: formatResult(value)
|
|
405
|
-
}]
|
|
406
|
-
},
|
|
407
|
-
presentCall: (args) => ({
|
|
408
|
-
card: "terminal",
|
|
409
|
-
title: `sqlcmd ${oneLine(args.sql)}`,
|
|
410
|
-
description: "在数据库客户端执行一条 SQL"
|
|
411
|
-
}),
|
|
412
|
-
presentResult: (args, result) => ({
|
|
413
|
-
card: "terminal",
|
|
414
|
-
title: `sqlcmd ${oneLine(args.sql)}`,
|
|
415
|
-
content: result.content
|
|
416
|
-
}),
|
|
417
|
-
async execute(args, exec) {
|
|
418
|
-
const connection = requireToolConnection(ctx, exec, "sqlcmd");
|
|
419
|
-
validateSingleSql(args.sql, "sqlcmd");
|
|
420
|
-
if ((connection.readonly ?? resolved.readonly) && classifyStatement(args.sql, connection.type) === "write") throw new Error("当前连接为只读模式,sqlcmd 拒绝执行非读语句(仅放行 SELECT/SHOW/DESCRIBE/EXPLAIN/查询型 PRAGMA 等)");
|
|
421
|
-
const sql = classifyStatement(args.sql, connection.type) === "read" ? enforceReadRowLimit(args.sql, connection.type, resolved.maxRows) : args.sql;
|
|
422
|
-
return runClientQuery(ctx, connection, sql, runnerOptions(resolved), exec.signal);
|
|
423
|
-
}
|
|
424
|
-
}));
|
|
425
|
-
}
|
|
426
|
-
//#endregion
|
|
1
|
+
import { i as name, n as apply, r as inject, t as Config } from "./tool-Dka6RyEp.js";
|
|
427
2
|
export { Config, apply, inject, name };
|