sql-chatbot-agent 1.0.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/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/client.d.ts +13 -0
- package/dist/llm/client.d.ts.map +1 -0
- package/dist/llm/client.js +50 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/middleware.d.ts +6 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +112 -0
- package/dist/middleware.js.map +1 -0
- package/dist/prompts/answer.d.ts +22 -0
- package/dist/prompts/answer.d.ts.map +1 -0
- package/dist/prompts/answer.js +141 -0
- package/dist/prompts/answer.js.map +1 -0
- package/dist/prompts/classify.d.ts +13 -0
- package/dist/prompts/classify.d.ts.map +1 -0
- package/dist/prompts/classify.js +43 -0
- package/dist/prompts/classify.js.map +1 -0
- package/dist/prompts/generate-sql.d.ts +10 -0
- package/dist/prompts/generate-sql.d.ts.map +1 -0
- package/dist/prompts/generate-sql.js +35 -0
- package/dist/prompts/generate-sql.js.map +1 -0
- package/dist/services/code-indexer.d.ts +35 -0
- package/dist/services/code-indexer.d.ts.map +1 -0
- package/dist/services/code-indexer.js +319 -0
- package/dist/services/code-indexer.js.map +1 -0
- package/dist/services/orchestrator.d.ts +33 -0
- package/dist/services/orchestrator.d.ts.map +1 -0
- package/dist/services/orchestrator.js +196 -0
- package/dist/services/orchestrator.js.map +1 -0
- package/dist/services/schema.d.ts +9 -0
- package/dist/services/schema.d.ts.map +1 -0
- package/dist/services/schema.js +122 -0
- package/dist/services/schema.js.map +1 -0
- package/dist/services/sql-executor.d.ts +13 -0
- package/dist/services/sql-executor.d.ts.map +1 -0
- package/dist/services/sql-executor.js +104 -0
- package/dist/services/sql-executor.js.map +1 -0
- package/package.json +40 -0
- package/widget/widget.js +50 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaService = void 0;
|
|
4
|
+
const pg_1 = require("pg");
|
|
5
|
+
const SENSITIVE_PATTERNS = [
|
|
6
|
+
'password', 'passwd', 'secret', 'token', 'ssn', 'social_security',
|
|
7
|
+
'credit_card', 'card_number', 'cvv', 'pin', 'encrypted', 'hash',
|
|
8
|
+
'salt', 'private_key', 'api_key', 'auth_key', 'access_key',
|
|
9
|
+
];
|
|
10
|
+
const TYPE_MAP = {
|
|
11
|
+
'character varying': 'VARCHAR',
|
|
12
|
+
'integer': 'INT',
|
|
13
|
+
'bigint': 'BIGINT',
|
|
14
|
+
'smallint': 'SMALLINT',
|
|
15
|
+
'timestamp without time zone': 'TIMESTAMP',
|
|
16
|
+
'timestamp with time zone': 'TIMESTAMPTZ',
|
|
17
|
+
'numeric': 'DECIMAL',
|
|
18
|
+
'boolean': 'BOOL',
|
|
19
|
+
'text': 'TEXT',
|
|
20
|
+
'date': 'DATE',
|
|
21
|
+
'double precision': 'DOUBLE',
|
|
22
|
+
'real': 'REAL',
|
|
23
|
+
'uuid': 'UUID',
|
|
24
|
+
'jsonb': 'JSONB',
|
|
25
|
+
'json': 'JSON',
|
|
26
|
+
};
|
|
27
|
+
function mapType(pgType) {
|
|
28
|
+
return TYPE_MAP[pgType] || pgType.toUpperCase();
|
|
29
|
+
}
|
|
30
|
+
function isSensitive(columnName) {
|
|
31
|
+
const lower = columnName.toLowerCase();
|
|
32
|
+
return SENSITIVE_PATTERNS.some((pattern) => {
|
|
33
|
+
// Word-boundary matching: pattern must appear as a whole word segment
|
|
34
|
+
// This avoids false positives like "pinned_at" matching "pin"
|
|
35
|
+
const regex = new RegExp('(^|_)' + pattern + '($|_)');
|
|
36
|
+
return regex.test(lower);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
class SchemaService {
|
|
40
|
+
summary = '';
|
|
41
|
+
tables = [];
|
|
42
|
+
async discover(databaseUrl) {
|
|
43
|
+
const pool = new pg_1.Pool({ connectionString: databaseUrl });
|
|
44
|
+
try {
|
|
45
|
+
const [tablesRes, columnsRes, pksRes, fksRes] = await Promise.all([
|
|
46
|
+
pool.query(`SELECT table_name FROM information_schema.tables
|
|
47
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
48
|
+
ORDER BY table_name`),
|
|
49
|
+
pool.query(`SELECT table_name, column_name, data_type, is_nullable, column_default
|
|
50
|
+
FROM information_schema.columns
|
|
51
|
+
WHERE table_schema = 'public'
|
|
52
|
+
ORDER BY table_name, ordinal_position`),
|
|
53
|
+
pool.query(`SELECT kcu.table_name, kcu.column_name
|
|
54
|
+
FROM information_schema.table_constraints tc
|
|
55
|
+
JOIN information_schema.key_column_usage kcu
|
|
56
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
57
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
|
58
|
+
WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_schema = 'public'`),
|
|
59
|
+
pool.query(`SELECT
|
|
60
|
+
kcu.table_name AS from_table,
|
|
61
|
+
kcu.column_name AS from_column,
|
|
62
|
+
ccu.table_name AS to_table,
|
|
63
|
+
ccu.column_name AS to_column
|
|
64
|
+
FROM information_schema.table_constraints tc
|
|
65
|
+
JOIN information_schema.key_column_usage kcu
|
|
66
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
67
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
|
68
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
69
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
70
|
+
AND tc.constraint_schema = ccu.constraint_schema
|
|
71
|
+
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = 'public'`),
|
|
72
|
+
]);
|
|
73
|
+
const tableNames = tablesRes.rows.map((r) => r.table_name);
|
|
74
|
+
// Index primary keys: Set of "table.column"
|
|
75
|
+
const pkSet = new Set(pksRes.rows.map((r) => `${r.table_name}.${r.column_name}`));
|
|
76
|
+
// Index foreign keys: Map of "table.column" → "target_table.target_column"
|
|
77
|
+
const fkMap = new Map(fksRes.rows.map((r) => [`${r.from_table}.${r.from_column}`, `${r.to_table}.${r.to_column}`]));
|
|
78
|
+
// Group columns by table
|
|
79
|
+
const columnsByTable = new Map();
|
|
80
|
+
for (const col of columnsRes.rows) {
|
|
81
|
+
if (!columnsByTable.has(col.table_name)) {
|
|
82
|
+
columnsByTable.set(col.table_name, []);
|
|
83
|
+
}
|
|
84
|
+
columnsByTable.get(col.table_name).push(col);
|
|
85
|
+
}
|
|
86
|
+
// Build summary lines
|
|
87
|
+
const lines = [];
|
|
88
|
+
for (const table of tableNames) {
|
|
89
|
+
const columns = columnsByTable.get(table) || [];
|
|
90
|
+
const colParts = [];
|
|
91
|
+
for (const col of columns) {
|
|
92
|
+
if (isSensitive(col.column_name))
|
|
93
|
+
continue;
|
|
94
|
+
const key = `${table}.${col.column_name}`;
|
|
95
|
+
let part = `${col.column_name} ${mapType(col.data_type)}`;
|
|
96
|
+
if (pkSet.has(key))
|
|
97
|
+
part += ' PK';
|
|
98
|
+
if (fkMap.has(key))
|
|
99
|
+
part += ` FK→${fkMap.get(key)}`;
|
|
100
|
+
colParts.push(part);
|
|
101
|
+
}
|
|
102
|
+
lines.push(`TABLE ${table} (${colParts.join(', ')})`);
|
|
103
|
+
}
|
|
104
|
+
this.tables = tableNames;
|
|
105
|
+
this.summary = lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
await pool.end();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
getSummary() {
|
|
112
|
+
return this.summary;
|
|
113
|
+
}
|
|
114
|
+
tableCount() {
|
|
115
|
+
return this.tables.length;
|
|
116
|
+
}
|
|
117
|
+
async refresh(databaseUrl) {
|
|
118
|
+
await this.discover(databaseUrl);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.SchemaService = SchemaService;
|
|
122
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/services/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
sql?: string;
|
|
4
|
+
reason?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SqlResult {
|
|
7
|
+
columns: string[];
|
|
8
|
+
rows: Record<string, any>[];
|
|
9
|
+
rowCount: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function validateSql(sql: string): ValidationResult;
|
|
12
|
+
export declare function executeSql(databaseUrl: string, sql: string): Promise<SqlResult>;
|
|
13
|
+
//# sourceMappingURL=sql-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-executor.d.ts","sourceRoot":"","sources":["../../src/services/sql-executor.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AA4BD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAsDzD;AAMD,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA6BrF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateSql = validateSql;
|
|
4
|
+
exports.executeSql = executeSql;
|
|
5
|
+
const pg_1 = require("pg");
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Blocklists (word-boundary matched)
|
|
8
|
+
// ============================================================
|
|
9
|
+
const KEYWORD_BLOCKLIST = [
|
|
10
|
+
'INSERT', 'UPDATE', 'DELETE', 'DROP', 'ALTER', 'CREATE',
|
|
11
|
+
'GRANT', 'TRUNCATE', 'EXECUTE', 'REVOKE', 'COPY', 'INTO',
|
|
12
|
+
];
|
|
13
|
+
const FUNCTION_BLOCKLIST = [
|
|
14
|
+
'pg_read_file', 'pg_read_binary_file', 'dblink',
|
|
15
|
+
'pg_terminate_backend', 'lo_import', 'lo_export',
|
|
16
|
+
'pg_sleep', 'set_config', 'current_setting',
|
|
17
|
+
];
|
|
18
|
+
const CATALOG_BLOCKLIST = [
|
|
19
|
+
'pg_shadow', 'pg_roles', 'pg_authid', 'pg_user',
|
|
20
|
+
'information_schema',
|
|
21
|
+
];
|
|
22
|
+
const AGGREGATE_PATTERN = /\b(COUNT|SUM|AVG|MIN|MAX)\s*\(/i;
|
|
23
|
+
// ============================================================
|
|
24
|
+
// validateSql
|
|
25
|
+
// ============================================================
|
|
26
|
+
function validateSql(sql) {
|
|
27
|
+
// Strip leading whitespace and SQL comments
|
|
28
|
+
let trimmed = sql.replace(/^\s+/, '');
|
|
29
|
+
trimmed = trimmed.replace(/--[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
30
|
+
trimmed = trimmed.trim();
|
|
31
|
+
// 1. Must be a single statement: reject if contains `;` followed by another statement
|
|
32
|
+
const parts = trimmed.split(';').filter((p) => p.trim().length > 0);
|
|
33
|
+
if (parts.length > 1) {
|
|
34
|
+
return { valid: false, reason: 'Only a single statement is allowed' };
|
|
35
|
+
}
|
|
36
|
+
// Remove trailing semicolon for the working copy
|
|
37
|
+
let workingSql = trimmed.replace(/;\s*$/, '').trim();
|
|
38
|
+
// 2. Must start with SELECT
|
|
39
|
+
if (!/^SELECT\b/i.test(workingSql)) {
|
|
40
|
+
return { valid: false, reason: 'Only SELECT queries are allowed' };
|
|
41
|
+
}
|
|
42
|
+
// 3. Keyword blocklist (word-boundary matching)
|
|
43
|
+
for (const keyword of KEYWORD_BLOCKLIST) {
|
|
44
|
+
const regex = new RegExp('\\b' + keyword + '\\b', 'i');
|
|
45
|
+
if (regex.test(workingSql)) {
|
|
46
|
+
return { valid: false, reason: `Blocked keyword: ${keyword}` };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// 4. Function blocklist (word-boundary matching)
|
|
50
|
+
for (const fn of FUNCTION_BLOCKLIST) {
|
|
51
|
+
const regex = new RegExp('\\b' + fn + '\\b', 'i');
|
|
52
|
+
if (regex.test(workingSql)) {
|
|
53
|
+
return { valid: false, reason: `Blocked function: ${fn}` };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// 5. System catalog blocklist (word-boundary matching)
|
|
57
|
+
for (const catalog of CATALOG_BLOCKLIST) {
|
|
58
|
+
const regex = new RegExp('\\b' + catalog + '\\b', 'i');
|
|
59
|
+
if (regex.test(workingSql)) {
|
|
60
|
+
return { valid: false, reason: `Blocked system catalog: ${catalog}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 6. Auto-add LIMIT 500 if no LIMIT clause present
|
|
64
|
+
// Skip for aggregate queries (COUNT/SUM/AVG/MIN/MAX) without GROUP BY having a LIMIT
|
|
65
|
+
const hasLimit = /\bLIMIT\b/i.test(workingSql);
|
|
66
|
+
const isAggregate = AGGREGATE_PATTERN.test(workingSql);
|
|
67
|
+
if (!hasLimit && !isAggregate) {
|
|
68
|
+
workingSql = workingSql + ' LIMIT 500';
|
|
69
|
+
}
|
|
70
|
+
return { valid: true, sql: workingSql };
|
|
71
|
+
}
|
|
72
|
+
// ============================================================
|
|
73
|
+
// executeSql
|
|
74
|
+
// ============================================================
|
|
75
|
+
async function executeSql(databaseUrl, sql) {
|
|
76
|
+
const pool = new pg_1.Pool({ connectionString: databaseUrl });
|
|
77
|
+
const client = await pool.connect();
|
|
78
|
+
try {
|
|
79
|
+
await client.query("SET statement_timeout = '10s'");
|
|
80
|
+
await client.query('BEGIN');
|
|
81
|
+
await client.query('SET TRANSACTION READ ONLY');
|
|
82
|
+
const result = await client.query(sql);
|
|
83
|
+
await client.query('COMMIT');
|
|
84
|
+
return {
|
|
85
|
+
columns: result.fields.map((f) => f.name),
|
|
86
|
+
rows: result.rows,
|
|
87
|
+
rowCount: result.rowCount ?? 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
try {
|
|
92
|
+
await client.query('ROLLBACK');
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// ignore rollback errors
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
client.release();
|
|
101
|
+
await pool.end();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=sql-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-executor.js","sourceRoot":"","sources":["../../src/services/sql-executor.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sql-chatbot-agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI chatbot middleware for Express apps with PostgreSQL — auto-discovers schema, indexes code, executes SQL, streams answers via chat widget",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/bhumit4220/sql-chatbot.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["chatbot", "sql", "postgresql", "express", "middleware", "ai", "llm", "groq"],
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"widget"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"build:widget": "cd widget-src && npx vite build",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"express": "^4.0.0",
|
|
28
|
+
"openai": "^4.0.0",
|
|
29
|
+
"pg": "^8.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/express": "^4.0.0",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"@types/pg": "^8.0.0",
|
|
35
|
+
"@types/supertest": "^7.2.0",
|
|
36
|
+
"supertest": "^7.2.2",
|
|
37
|
+
"typescript": "^5.0.0",
|
|
38
|
+
"vitest": "^3.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|