agent-database-cli 0.2.7
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/AI_INSTALL.md +72 -0
- package/LICENSE +21 -0
- package/README.md +340 -0
- package/README_EN.md +341 -0
- package/SKILL.md +252 -0
- package/config/docker-test.json +40 -0
- package/dist/adapters/base-sql.d.ts +12 -0
- package/dist/adapters/base-sql.js +22 -0
- package/dist/adapters/base-sql.js.map +1 -0
- package/dist/adapters/factory.d.ts +2 -0
- package/dist/adapters/factory.js +26 -0
- package/dist/adapters/factory.js.map +1 -0
- package/dist/adapters/mongodb.d.ts +14 -0
- package/dist/adapters/mongodb.js +137 -0
- package/dist/adapters/mongodb.js.map +1 -0
- package/dist/adapters/mysql.d.ts +12 -0
- package/dist/adapters/mysql.js +38 -0
- package/dist/adapters/mysql.js.map +1 -0
- package/dist/adapters/oracle-sqlcl.d.ts +19 -0
- package/dist/adapters/oracle-sqlcl.js +334 -0
- package/dist/adapters/oracle-sqlcl.js.map +1 -0
- package/dist/adapters/oracle.d.ts +13 -0
- package/dist/adapters/oracle.js +47 -0
- package/dist/adapters/oracle.js.map +1 -0
- package/dist/adapters/postgres.d.ts +12 -0
- package/dist/adapters/postgres.js +40 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/adapters/redis.d.ts +15 -0
- package/dist/adapters/redis.js +91 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +118 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +147 -0
- package/dist/config.js.map +1 -0
- package/dist/connection-manager.d.ts +24 -0
- package/dist/connection-manager.js +91 -0
- package/dist/connection-manager.js.map +1 -0
- package/dist/daemon/client.d.ts +3 -0
- package/dist/daemon/client.js +33 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/config-manager.d.ts +15 -0
- package/dist/daemon/config-manager.js +44 -0
- package/dist/daemon/config-manager.js.map +1 -0
- package/dist/daemon/control.d.ts +9 -0
- package/dist/daemon/control.js +53 -0
- package/dist/daemon/control.js.map +1 -0
- package/dist/daemon/paths.d.ts +5 -0
- package/dist/daemon/paths.js +18 -0
- package/dist/daemon/paths.js.map +1 -0
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.js +119 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/output.d.ts +2 -0
- package/dist/output.js +39 -0
- package/dist/output.js.map +1 -0
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +25 -0
- package/dist/runtime.js.map +1 -0
- package/dist/security.d.ts +8 -0
- package/dist/security.js +239 -0
- package/dist/security.js.map +1 -0
- package/dist/ssh-tunnel.d.ts +8 -0
- package/dist/ssh-tunnel.js +231 -0
- package/dist/ssh-tunnel.js.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/masking.d.ts +2 -0
- package/dist/utils/masking.js +14 -0
- package/dist/utils/masking.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import oracledb from "oracledb";
|
|
2
|
+
import { BaseSqlAdapter } from "./base-sql.js";
|
|
3
|
+
export class OracleAdapter extends BaseSqlAdapter {
|
|
4
|
+
url;
|
|
5
|
+
connection;
|
|
6
|
+
constructor(url) {
|
|
7
|
+
super();
|
|
8
|
+
this.url = url;
|
|
9
|
+
}
|
|
10
|
+
async connect() {
|
|
11
|
+
if (this.connection) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const parsed = new URL(this.url);
|
|
15
|
+
this.connection = await oracledb.getConnection({
|
|
16
|
+
user: decodeURIComponent(parsed.username),
|
|
17
|
+
password: decodeURIComponent(parsed.password),
|
|
18
|
+
connectString: `${parsed.hostname}:${parsed.port || "1521"}${parsed.pathname}`
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async disconnect() {
|
|
22
|
+
if (this.connection) {
|
|
23
|
+
await this.connection.close();
|
|
24
|
+
this.connection = undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async execute(command) {
|
|
28
|
+
await this.connect();
|
|
29
|
+
const result = await this.connection.execute(command, [], { outFormat: oracledb.OUT_FORMAT_OBJECT, autoCommit: true });
|
|
30
|
+
return {
|
|
31
|
+
rows: (result.rows ?? []),
|
|
32
|
+
fields: result.metaData?.map((field) => field.name) ?? [],
|
|
33
|
+
rowCount: result.rowsAffected ?? result.rows?.length ?? 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
testQuery() {
|
|
37
|
+
return "select 1 from dual";
|
|
38
|
+
}
|
|
39
|
+
async listTables() {
|
|
40
|
+
return this.execute("select table_name from user_tables order by table_name");
|
|
41
|
+
}
|
|
42
|
+
async listColumns(table) {
|
|
43
|
+
const escaped = table.replace(/'/g, "''").toUpperCase();
|
|
44
|
+
return this.execute(`select table_name, column_name, data_type from user_tab_columns where table_name = '${escaped}' order by column_id`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=oracle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oracle.js","sourceRoot":"","sources":["../../src/adapters/oracle.ts"],"names":[],"mappings":"AAAA,OAAO,QAA6B,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,MAAM,OAAO,aAAc,SAAQ,cAAc;IAGlB;IAFrB,UAAU,CAAc;IAEhC,YAA6B,GAAW;QACtC,KAAK,EAAE,CAAC;QADmB,QAAG,GAAH,GAAG,CAAQ;IAExC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC;YAC7C,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;YACzC,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC7C,aAAa,EAAE,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE;SAC/E,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACxH,OAAO;YACL,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAc;YACtC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;YACzD,QAAQ,EAAE,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;SAC1D,CAAC;IACJ,CAAC;IAES,SAAS;QACjB,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAES,KAAK,CAAC,UAAU;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,wDAAwD,CAAC,CAAC;IAChF,CAAC;IAES,KAAK,CAAC,WAAW,CAAC,KAAa;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC,OAAO,CACjB,uFAAuF,OAAO,sBAAsB,CACrH,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseSqlAdapter } from "./base-sql.js";
|
|
2
|
+
import type { QueryResult } from "../types.js";
|
|
3
|
+
export declare class PostgresAdapter extends BaseSqlAdapter {
|
|
4
|
+
private readonly url;
|
|
5
|
+
private client?;
|
|
6
|
+
constructor(url: string);
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
execute(command: string): Promise<QueryResult>;
|
|
10
|
+
protected listTables(): Promise<QueryResult>;
|
|
11
|
+
protected listColumns(table: string): Promise<QueryResult>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import { BaseSqlAdapter } from "./base-sql.js";
|
|
3
|
+
const { Client } = pg;
|
|
4
|
+
export class PostgresAdapter extends BaseSqlAdapter {
|
|
5
|
+
url;
|
|
6
|
+
client;
|
|
7
|
+
constructor(url) {
|
|
8
|
+
super();
|
|
9
|
+
this.url = url;
|
|
10
|
+
}
|
|
11
|
+
async connect() {
|
|
12
|
+
if (!this.client) {
|
|
13
|
+
this.client = new Client({ connectionString: this.url });
|
|
14
|
+
await this.client.connect();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async disconnect() {
|
|
18
|
+
if (this.client) {
|
|
19
|
+
await this.client.end();
|
|
20
|
+
this.client = undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async execute(command) {
|
|
24
|
+
await this.connect();
|
|
25
|
+
const result = await this.client.query(command);
|
|
26
|
+
return {
|
|
27
|
+
rows: result.rows,
|
|
28
|
+
fields: result.fields.map((field) => field.name),
|
|
29
|
+
rowCount: result.rowCount ?? result.rows.length
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async listTables() {
|
|
33
|
+
return this.execute("select table_schema, table_name from information_schema.tables where table_type = 'BASE TABLE' and table_schema not in ('pg_catalog', 'information_schema') order by table_schema, table_name");
|
|
34
|
+
}
|
|
35
|
+
async listColumns(table) {
|
|
36
|
+
const escaped = table.replace(/'/g, "''");
|
|
37
|
+
return this.execute(`select table_schema, table_name, column_name, data_type from information_schema.columns where table_name = '${escaped}' order by ordinal_position`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/adapters/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAEtB,MAAM,OAAO,eAAgB,SAAQ,cAAc;IAGpB;IAFrB,MAAM,CAAa;IAE3B,YAA6B,GAAW;QACtC,KAAK,EAAE,CAAC;QADmB,QAAG,GAAH,GAAG,CAAQ;IAExC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACzD,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YAChD,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM;SAChD,CAAC;IACJ,CAAC;IAES,KAAK,CAAC,UAAU;QACxB,OAAO,IAAI,CAAC,OAAO,CACjB,+LAA+L,CAChM,CAAC;IACJ,CAAC;IAES,KAAK,CAAC,WAAW,CAAC,KAAa;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,OAAO,CACjB,+GAA+G,OAAO,6BAA6B,CACpJ,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DatabaseAdapter, MetadataRequest, QueryResult, RedisClusterConnectionConfig } from "../types.js";
|
|
2
|
+
export declare class RedisAdapter implements DatabaseAdapter {
|
|
3
|
+
private readonly url;
|
|
4
|
+
private readonly redisCluster?;
|
|
5
|
+
private standaloneClient?;
|
|
6
|
+
private clusterClient?;
|
|
7
|
+
constructor(url: string, redisCluster?: RedisClusterConnectionConfig | undefined);
|
|
8
|
+
connect(): Promise<void>;
|
|
9
|
+
disconnect(): Promise<void>;
|
|
10
|
+
test(): Promise<void>;
|
|
11
|
+
execute(command: string): Promise<QueryResult>;
|
|
12
|
+
metadata(request: MetadataRequest): Promise<QueryResult>;
|
|
13
|
+
private executeRawCommand;
|
|
14
|
+
private collectClusterKeys;
|
|
15
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createClient, createCluster } from "redis";
|
|
2
|
+
import { isReadOnlyCommand } from "../security.js";
|
|
3
|
+
export class RedisAdapter {
|
|
4
|
+
url;
|
|
5
|
+
redisCluster;
|
|
6
|
+
standaloneClient;
|
|
7
|
+
clusterClient;
|
|
8
|
+
constructor(url, redisCluster) {
|
|
9
|
+
this.url = url;
|
|
10
|
+
this.redisCluster = redisCluster;
|
|
11
|
+
}
|
|
12
|
+
async connect() {
|
|
13
|
+
if (this.redisCluster) {
|
|
14
|
+
if (!this.clusterClient) {
|
|
15
|
+
this.clusterClient = createCluster({
|
|
16
|
+
rootNodes: [{ url: this.url }],
|
|
17
|
+
nodeAddressMap: this.redisCluster.nodeAddressMap
|
|
18
|
+
});
|
|
19
|
+
await this.clusterClient.connect();
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!this.standaloneClient) {
|
|
24
|
+
this.standaloneClient = createClient({ url: this.url });
|
|
25
|
+
await this.standaloneClient.connect();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async disconnect() {
|
|
29
|
+
if (this.clusterClient) {
|
|
30
|
+
await this.clusterClient.quit();
|
|
31
|
+
this.clusterClient = undefined;
|
|
32
|
+
}
|
|
33
|
+
if (this.standaloneClient) {
|
|
34
|
+
await this.standaloneClient.quit();
|
|
35
|
+
this.standaloneClient = undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async test() {
|
|
39
|
+
await this.connect();
|
|
40
|
+
await this.executeRawCommand(["PING"], true);
|
|
41
|
+
}
|
|
42
|
+
async execute(command) {
|
|
43
|
+
await this.connect();
|
|
44
|
+
const parts = splitCommand(command);
|
|
45
|
+
const result = await this.executeRawCommand(parts, isReadOnlyCommand("redis", command));
|
|
46
|
+
return { rows: [{ result }], rowCount: 1 };
|
|
47
|
+
}
|
|
48
|
+
async metadata(request) {
|
|
49
|
+
if (request.type !== "keys") {
|
|
50
|
+
throw new Error(`Redis 不支持元信息类型: ${request.type}`);
|
|
51
|
+
}
|
|
52
|
+
await this.connect();
|
|
53
|
+
const pattern = request.pattern || "*";
|
|
54
|
+
const keys = this.clusterClient ? await this.collectClusterKeys(pattern) : await this.standaloneClient.keys(pattern);
|
|
55
|
+
return { rows: keys.map((key) => ({ key })), fields: ["key"], rowCount: keys.length };
|
|
56
|
+
}
|
|
57
|
+
async executeRawCommand(parts, isReadonly) {
|
|
58
|
+
if (this.clusterClient) {
|
|
59
|
+
const firstKey = getFirstKey(parts);
|
|
60
|
+
return this.clusterClient.sendCommand(firstKey, isReadonly, parts);
|
|
61
|
+
}
|
|
62
|
+
return this.standaloneClient.sendCommand(parts);
|
|
63
|
+
}
|
|
64
|
+
async collectClusterKeys(pattern) {
|
|
65
|
+
const keys = new Set();
|
|
66
|
+
for (const node of this.clusterClient.masters) {
|
|
67
|
+
const nodeClient = await this.clusterClient.nodeClient(node);
|
|
68
|
+
const nodeKeys = await nodeClient.keys(pattern);
|
|
69
|
+
for (const key of nodeKeys) {
|
|
70
|
+
keys.add(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [...keys];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function splitCommand(command) {
|
|
77
|
+
const matches = command.match(/"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|\S+/g);
|
|
78
|
+
if (!matches || matches.length === 0) {
|
|
79
|
+
throw new Error("Redis 命令不能为空");
|
|
80
|
+
}
|
|
81
|
+
return matches.map((part) => {
|
|
82
|
+
if ((part.startsWith('"') && part.endsWith('"')) || (part.startsWith("'") && part.endsWith("'"))) {
|
|
83
|
+
return part.slice(1, -1);
|
|
84
|
+
}
|
|
85
|
+
return part;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function getFirstKey(parts) {
|
|
89
|
+
return parts.length > 1 ? parts[1] : undefined;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/adapters/redis.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { loadConfig, listSupportedDatabases, resolveConfigPath } from "./config.js";
|
|
7
|
+
import { daemonStatus, startDaemon, stopDaemon } from "./daemon/control.js";
|
|
8
|
+
import { writeOutput } from "./output.js";
|
|
9
|
+
import { runExecute, runMetadata, runReset, runTest } from "./runtime.js";
|
|
10
|
+
import { toErrorMessage } from "./utils/masking.js";
|
|
11
|
+
const program = new Command();
|
|
12
|
+
const packageVersion = readPackageVersion();
|
|
13
|
+
program
|
|
14
|
+
.name("agent-database-cli")
|
|
15
|
+
.description("统一数据库命令行工具")
|
|
16
|
+
.version(packageVersion)
|
|
17
|
+
.option("--format <format>", "输出格式: json 或 table", "json");
|
|
18
|
+
program.command("list").description("展示支持的数据库类型").action(async () => {
|
|
19
|
+
await main(async () => {
|
|
20
|
+
const configPath = resolveConfigPath();
|
|
21
|
+
let configured = [];
|
|
22
|
+
try {
|
|
23
|
+
const config = await loadConfig(configPath);
|
|
24
|
+
configured = Object.keys(config.databases);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
configured = [];
|
|
28
|
+
}
|
|
29
|
+
writeOutput({
|
|
30
|
+
supported: listSupportedDatabases(),
|
|
31
|
+
configured,
|
|
32
|
+
configPath
|
|
33
|
+
}, getFormat());
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
program
|
|
37
|
+
.command("test")
|
|
38
|
+
.description("测试数据库连接")
|
|
39
|
+
.requiredOption("--db <name>", "数据库配置名")
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
await main(async () => {
|
|
42
|
+
writeOutput(await runTest(options.db), getFormat());
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
program
|
|
46
|
+
.command("exec")
|
|
47
|
+
.description("统一执行 SQL、Redis 命令或 MongoDB JSON 命令")
|
|
48
|
+
.requiredOption("--db <name>", "数据库配置名")
|
|
49
|
+
.requiredOption("--command <command>", "待执行命令")
|
|
50
|
+
.action(async (options) => {
|
|
51
|
+
await main(async () => {
|
|
52
|
+
writeOutput(await runExecute(options.db, options.command), getFormat());
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
program
|
|
56
|
+
.command("meta")
|
|
57
|
+
.description("查询数据库元信息")
|
|
58
|
+
.requiredOption("--db <name>", "数据库配置名")
|
|
59
|
+
.requiredOption("--type <type>", "元信息类型: tables, columns, collections, keys")
|
|
60
|
+
.option("--table <table>", "columns 查询的表名")
|
|
61
|
+
.option("--pattern <pattern>", "keys 查询的匹配模式")
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
await main(async () => {
|
|
64
|
+
writeOutput(await runMetadata(options.db, { type: options.type, table: options.table, pattern: options.pattern }), getFormat());
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
program
|
|
68
|
+
.command("reset")
|
|
69
|
+
.description("重置指定数据库连接")
|
|
70
|
+
.requiredOption("--db <name>", "数据库配置名")
|
|
71
|
+
.action(async (options) => {
|
|
72
|
+
await main(async () => {
|
|
73
|
+
writeOutput(await runReset(options.db), getFormat());
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
const daemon = program.command("daemon").description("管理本地连接守护进程");
|
|
77
|
+
daemon.command("start").description("启动 daemon").action(async () => {
|
|
78
|
+
await main(async () => {
|
|
79
|
+
writeOutput(await startDaemon(), getFormat());
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
daemon.command("stop").description("停止 daemon").action(async () => {
|
|
83
|
+
await main(async () => {
|
|
84
|
+
writeOutput(await stopDaemon(), getFormat());
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
daemon.command("status").description("查看 daemon 状态").action(async () => {
|
|
88
|
+
await main(async () => {
|
|
89
|
+
writeOutput(await daemonStatus(), getFormat());
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
function getFormat() {
|
|
93
|
+
const format = program.opts().format;
|
|
94
|
+
if (format !== "json" && format !== "table") {
|
|
95
|
+
throw new Error(`不支持的输出格式: ${format}`);
|
|
96
|
+
}
|
|
97
|
+
return format;
|
|
98
|
+
}
|
|
99
|
+
async function main(fn) {
|
|
100
|
+
try {
|
|
101
|
+
await fn();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
process.stderr.write(`${toErrorMessage(error)}\n`);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function readPackageVersion() {
|
|
109
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
110
|
+
const packageJsonPath = resolve(dirname(currentFile), "../package.json");
|
|
111
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
112
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
113
|
+
throw new Error("package.json 缺少有效的 version");
|
|
114
|
+
}
|
|
115
|
+
return packageJson.version;
|
|
116
|
+
}
|
|
117
|
+
await program.parseAsync(process.argv);
|
|
118
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppConfig, DatabaseConfig, DatabaseType } from "./types.js";
|
|
2
|
+
export declare const CONFIG_ENV = "AGENT_DATABASE_CLI_CONFIG";
|
|
3
|
+
export declare const DEFAULT_CONFIG_PATH: string;
|
|
4
|
+
export declare function resolveConfigPath(): string;
|
|
5
|
+
export declare function loadConfig(path?: string): Promise<AppConfig>;
|
|
6
|
+
export declare function validateConfig(config: AppConfig): void;
|
|
7
|
+
export declare function getDatabaseConfig(config: AppConfig, name: string): DatabaseConfig;
|
|
8
|
+
export declare function listSupportedDatabases(): DatabaseType[];
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export const CONFIG_ENV = "AGENT_DATABASE_CLI_CONFIG";
|
|
5
|
+
export const DEFAULT_CONFIG_PATH = join(homedir(), ".agent-database-cli", "config.json");
|
|
6
|
+
const SUPPORTED_TYPES = new Set(["mysql", "postgres", "redis", "oracle", "mongodb"]);
|
|
7
|
+
export function resolveConfigPath() {
|
|
8
|
+
return process.env[CONFIG_ENV] || DEFAULT_CONFIG_PATH;
|
|
9
|
+
}
|
|
10
|
+
export async function loadConfig(path = resolveConfigPath()) {
|
|
11
|
+
const raw = await readFile(path, "utf8");
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
validateConfig(parsed);
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
export function validateConfig(config) {
|
|
17
|
+
if (!config || typeof config !== "object" || !config.databases || typeof config.databases !== "object") {
|
|
18
|
+
throw new Error("配置文件必须包含 databases 对象");
|
|
19
|
+
}
|
|
20
|
+
for (const [name, db] of Object.entries(config.databases)) {
|
|
21
|
+
validateDatabaseConfig(name, db);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function validateDatabaseConfig(name, db) {
|
|
25
|
+
if (!db || typeof db !== "object") {
|
|
26
|
+
throw new Error(`数据库配置 ${name} 必须是对象`);
|
|
27
|
+
}
|
|
28
|
+
if (!SUPPORTED_TYPES.has(db.type)) {
|
|
29
|
+
throw new Error(`数据库配置 ${name} 的 type 不受支持: ${String(db.type)}`);
|
|
30
|
+
}
|
|
31
|
+
if (!db.url || typeof db.url !== "string") {
|
|
32
|
+
throw new Error(`数据库配置 ${name} 必须提供 url`);
|
|
33
|
+
}
|
|
34
|
+
if (db.redisCluster !== undefined) {
|
|
35
|
+
validateRedisClusterConfig(name, db);
|
|
36
|
+
}
|
|
37
|
+
if (db.blacklist && !Array.isArray(db.blacklist)) {
|
|
38
|
+
throw new Error(`数据库配置 ${name} 的 blacklist 必须是数组`);
|
|
39
|
+
}
|
|
40
|
+
if (db.keepAliveSeconds !== undefined && (!Number.isInteger(db.keepAliveSeconds) || db.keepAliveSeconds <= 0)) {
|
|
41
|
+
throw new Error(`数据库配置 ${name} 的 keepAliveSeconds 必须是正整数`);
|
|
42
|
+
}
|
|
43
|
+
if (db.sshTunnel !== undefined) {
|
|
44
|
+
validateSshTunnelConfig(name, db.sshTunnel);
|
|
45
|
+
}
|
|
46
|
+
if (db.oracleDriver !== undefined) {
|
|
47
|
+
if (db.type !== "oracle") {
|
|
48
|
+
throw new Error(`数据库配置 ${name} 只有 oracle 类型允许配置 oracleDriver`);
|
|
49
|
+
}
|
|
50
|
+
if (db.oracleDriver !== "oracledb" && db.oracleDriver !== "sqlcl") {
|
|
51
|
+
throw new Error(`数据库配置 ${name} 的 oracleDriver 只支持 oracledb 或 sqlcl`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (db.sqlclPath !== undefined && typeof db.sqlclPath !== "string") {
|
|
55
|
+
throw new Error(`数据库配置 ${name} 的 sqlclPath 必须是字符串`);
|
|
56
|
+
}
|
|
57
|
+
if (db.javaHome !== undefined && typeof db.javaHome !== "string") {
|
|
58
|
+
throw new Error(`数据库配置 ${name} 的 javaHome 必须是字符串`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function validateRedisClusterConfig(name, db) {
|
|
62
|
+
if (db.type !== "redis") {
|
|
63
|
+
throw new Error(`数据库配置 ${name} 只有 redis 类型允许配置 redisCluster`);
|
|
64
|
+
}
|
|
65
|
+
if (!db.redisCluster || typeof db.redisCluster !== "object") {
|
|
66
|
+
throw new Error(`数据库配置 ${name} 的 redisCluster 必须是对象`);
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(db.redisCluster.nodes) || db.redisCluster.nodes.length === 0) {
|
|
69
|
+
throw new Error(`数据库配置 ${name} 的 redisCluster.nodes 必须是非空数组`);
|
|
70
|
+
}
|
|
71
|
+
for (const [index, node] of db.redisCluster.nodes.entries()) {
|
|
72
|
+
if (typeof node !== "string" || node.trim() === "") {
|
|
73
|
+
throw new Error(`数据库配置 ${name} 的 redisCluster.nodes[${index}] 必须是非空字符串`);
|
|
74
|
+
}
|
|
75
|
+
validateRedisNodeUrl(name, node, index);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function validateRedisNodeUrl(name, url, index) {
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = new URL(url);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
throw new Error(`数据库配置 ${name} 的 redisCluster.nodes[${index}] 不是合法 URL`);
|
|
85
|
+
}
|
|
86
|
+
if (!["redis:", "rediss:"].includes(parsed.protocol)) {
|
|
87
|
+
throw new Error(`数据库配置 ${name} 的 redisCluster.nodes[${index}] 只支持 redis:// 或 rediss://`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function validateSshTunnelConfig(name, tunnel) {
|
|
91
|
+
if (!tunnel || typeof tunnel !== "object") {
|
|
92
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel 必须是对象`);
|
|
93
|
+
}
|
|
94
|
+
if (!tunnel.host || typeof tunnel.host !== "string") {
|
|
95
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.host 必须是非空字符串`);
|
|
96
|
+
}
|
|
97
|
+
if (tunnel.port !== undefined && (!Number.isInteger(tunnel.port) || tunnel.port <= 0 || tunnel.port > 65535)) {
|
|
98
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.port 必须是 1-65535 的整数`);
|
|
99
|
+
}
|
|
100
|
+
if (!tunnel.username || typeof tunnel.username !== "string") {
|
|
101
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.username 必须是非空字符串`);
|
|
102
|
+
}
|
|
103
|
+
if (tunnel.password !== undefined && typeof tunnel.password !== "string") {
|
|
104
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.password 必须是字符串`);
|
|
105
|
+
}
|
|
106
|
+
if (tunnel.password === "") {
|
|
107
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.password 不能为空字符串`);
|
|
108
|
+
}
|
|
109
|
+
if (tunnel.privateKeyPath !== undefined && typeof tunnel.privateKeyPath !== "string") {
|
|
110
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.privateKeyPath 必须是字符串`);
|
|
111
|
+
}
|
|
112
|
+
if (tunnel.privateKeyPath === "") {
|
|
113
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.privateKeyPath 不能为空字符串`);
|
|
114
|
+
}
|
|
115
|
+
if (tunnel.privateKey !== undefined && typeof tunnel.privateKey !== "string") {
|
|
116
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.privateKey 必须是字符串`);
|
|
117
|
+
}
|
|
118
|
+
if (tunnel.privateKey === "") {
|
|
119
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.privateKey 不能为空字符串`);
|
|
120
|
+
}
|
|
121
|
+
if (tunnel.privateKeyPath && tunnel.privateKey) {
|
|
122
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.privateKeyPath 和 privateKey 只能配置一个`);
|
|
123
|
+
}
|
|
124
|
+
if (!tunnel.password && !tunnel.privateKeyPath && !tunnel.privateKey) {
|
|
125
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel 必须配置 password、privateKeyPath 或 privateKey`);
|
|
126
|
+
}
|
|
127
|
+
if (tunnel.passphrase !== undefined && typeof tunnel.passphrase !== "string") {
|
|
128
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.passphrase 必须是字符串`);
|
|
129
|
+
}
|
|
130
|
+
if (tunnel.passphrase && !tunnel.privateKeyPath && !tunnel.privateKey) {
|
|
131
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.passphrase 只能和私钥一起使用`);
|
|
132
|
+
}
|
|
133
|
+
if (tunnel.readyTimeout !== undefined && (!Number.isInteger(tunnel.readyTimeout) || tunnel.readyTimeout <= 0)) {
|
|
134
|
+
throw new Error(`数据库配置 ${name} 的 sshTunnel.readyTimeout 必须是正整数`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function getDatabaseConfig(config, name) {
|
|
138
|
+
const db = config.databases[name];
|
|
139
|
+
if (!db) {
|
|
140
|
+
throw new Error(`未找到数据库配置: ${name}`);
|
|
141
|
+
}
|
|
142
|
+
return db;
|
|
143
|
+
}
|
|
144
|
+
export function listSupportedDatabases() {
|
|
145
|
+
return ["mysql", "postgres", "redis", "oracle", "mongodb"];
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AppConfig, MetadataRequest, QueryResult } from "./types.js";
|
|
2
|
+
export declare class ConnectionManager {
|
|
3
|
+
private readonly config;
|
|
4
|
+
private readonly entries;
|
|
5
|
+
constructor(config: AppConfig);
|
|
6
|
+
test(name: string): Promise<{
|
|
7
|
+
ok: true;
|
|
8
|
+
}>;
|
|
9
|
+
execute(name: string, command: string): Promise<QueryResult>;
|
|
10
|
+
metadata(name: string, request: MetadataRequest): Promise<QueryResult>;
|
|
11
|
+
reset(name: string): Promise<{
|
|
12
|
+
reset: string;
|
|
13
|
+
}>;
|
|
14
|
+
closeAll(): Promise<void>;
|
|
15
|
+
status(): {
|
|
16
|
+
connections: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
keepAliveSeconds: number;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
22
|
+
private getEntry;
|
|
23
|
+
private touch;
|
|
24
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { getDatabaseConfig } from "./config.js";
|
|
2
|
+
import { assertCommandAllowed } from "./security.js";
|
|
3
|
+
import { createAdapter } from "./adapters/factory.js";
|
|
4
|
+
import { startSshTunnel } from "./ssh-tunnel.js";
|
|
5
|
+
export class ConnectionManager {
|
|
6
|
+
config;
|
|
7
|
+
entries = new Map();
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async test(name) {
|
|
12
|
+
const entry = await this.getEntry(name);
|
|
13
|
+
await entry.adapter.test();
|
|
14
|
+
this.touch(name, entry);
|
|
15
|
+
return { ok: true };
|
|
16
|
+
}
|
|
17
|
+
async execute(name, command) {
|
|
18
|
+
const config = getDatabaseConfig(this.config, name);
|
|
19
|
+
assertCommandAllowed(config, command);
|
|
20
|
+
const entry = await this.getEntry(name);
|
|
21
|
+
const result = await entry.adapter.execute(command);
|
|
22
|
+
this.touch(name, entry);
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
async metadata(name, request) {
|
|
26
|
+
const entry = await this.getEntry(name);
|
|
27
|
+
const result = await entry.adapter.metadata(request);
|
|
28
|
+
this.touch(name, entry);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
async reset(name) {
|
|
32
|
+
const entry = this.entries.get(name);
|
|
33
|
+
if (entry) {
|
|
34
|
+
if (entry.timer) {
|
|
35
|
+
clearTimeout(entry.timer);
|
|
36
|
+
}
|
|
37
|
+
await entry.adapter.disconnect();
|
|
38
|
+
if (entry.tunnel) {
|
|
39
|
+
await entry.tunnel.close();
|
|
40
|
+
}
|
|
41
|
+
this.entries.delete(name);
|
|
42
|
+
}
|
|
43
|
+
return { reset: name };
|
|
44
|
+
}
|
|
45
|
+
async closeAll() {
|
|
46
|
+
await Promise.all([...this.entries.keys()].map((name) => this.reset(name)));
|
|
47
|
+
}
|
|
48
|
+
status() {
|
|
49
|
+
return {
|
|
50
|
+
connections: [...this.entries.entries()].map(([name, entry]) => ({
|
|
51
|
+
name,
|
|
52
|
+
type: entry.config.type,
|
|
53
|
+
keepAliveSeconds: entry.config.keepAliveSeconds ?? 180
|
|
54
|
+
}))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async getEntry(name) {
|
|
58
|
+
const existing = this.entries.get(name);
|
|
59
|
+
if (existing) {
|
|
60
|
+
return existing;
|
|
61
|
+
}
|
|
62
|
+
const config = getDatabaseConfig(this.config, name);
|
|
63
|
+
const tunnel = await startSshTunnel(config);
|
|
64
|
+
const adapterConfig = tunnel?.redisCluster ? { ...config, redisCluster: tunnel.redisCluster } : config;
|
|
65
|
+
const adapter = createAdapter(adapterConfig, tunnel?.url);
|
|
66
|
+
try {
|
|
67
|
+
await adapter.connect();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (tunnel) {
|
|
71
|
+
await tunnel.close();
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
const entry = { adapter, config, tunnel };
|
|
76
|
+
this.entries.set(name, entry);
|
|
77
|
+
this.touch(name, entry);
|
|
78
|
+
return entry;
|
|
79
|
+
}
|
|
80
|
+
touch(name, entry) {
|
|
81
|
+
if (entry.timer) {
|
|
82
|
+
clearTimeout(entry.timer);
|
|
83
|
+
}
|
|
84
|
+
const keepAliveSeconds = entry.config.keepAliveSeconds ?? 180;
|
|
85
|
+
entry.timer = setTimeout(() => {
|
|
86
|
+
void this.reset(name);
|
|
87
|
+
}, keepAliveSeconds * 1000);
|
|
88
|
+
entry.timer.unref();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=connection-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../src/connection-manager.ts"],"names":[],"mappings":"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"}
|