crushdataai 1.0.0 → 1.2.1
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/assets/antigravity/data-analyst.md +7 -0
- package/assets/claude/SKILL.md +8 -0
- package/assets/copilot/data-analyst.prompt.md +8 -1
- package/assets/cursor/data-analyst.md +7 -0
- package/assets/kiro/data-analyst.md +8 -1
- package/assets/windsurf/data-analyst.md +8 -1
- package/dist/commands/snippet.d.ts +1 -0
- package/dist/commands/snippet.js +26 -0
- package/dist/commands.js +4 -4
- package/dist/connections.d.ts +37 -0
- package/dist/connections.js +176 -0
- package/dist/connectors/cloud/index.d.ts +16 -0
- package/dist/connectors/cloud/index.js +39 -0
- package/dist/connectors/csv/index.d.ts +9 -0
- package/dist/connectors/csv/index.js +135 -0
- package/dist/connectors/index.d.ts +30 -0
- package/dist/connectors/index.js +17 -0
- package/dist/connectors/mysql/index.d.ts +9 -0
- package/dist/connectors/mysql/index.js +29 -0
- package/dist/connectors/postgresql/index.d.ts +9 -0
- package/dist/connectors/postgresql/index.js +28 -0
- package/dist/connectors/shopify/index.d.ts +9 -0
- package/dist/connectors/shopify/index.js +28 -0
- package/dist/index.js +91 -1
- package/dist/routes/connections.d.ts +2 -0
- package/dist/routes/connections.js +124 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +98 -0
- package/package.json +9 -3
- package/ui/favicon-32x32.png +0 -0
- package/ui/favicon.svg +19 -0
- package/ui/index.html +341 -0
- package/ui/main.js +542 -0
- package/ui/styles.css +680 -0
|
@@ -25,6 +25,13 @@ Before writing any code, ask the user:
|
|
|
25
25
|
2. **Data Context**: Which tables contain the data? What time range?
|
|
26
26
|
3. **Metric Definitions**: How does YOUR company define the key metrics? Any filters?
|
|
27
27
|
|
|
28
|
+
### 2. Secure Data Access
|
|
29
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
30
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
31
|
+
`npx crushdataai connect`
|
|
32
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
33
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
34
|
+
|
|
28
35
|
---
|
|
29
36
|
|
|
30
37
|
## Step 2: Search Knowledge Base
|
package/assets/claude/SKILL.md
CHANGED
|
@@ -29,8 +29,16 @@ When user requests data analysis work (analyze, query, dashboard, metrics, EDA,
|
|
|
29
29
|
- How does YOUR company define the key metrics?
|
|
30
30
|
- Any filters to apply? (exclude test users, internal accounts?)
|
|
31
31
|
- What timezone should I use for dates?
|
|
32
|
+
- What timezone should I use for dates?
|
|
32
33
|
```
|
|
33
34
|
|
|
35
|
+
### 1b. Secure Data Access
|
|
36
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
37
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
38
|
+
`npx crushdataai connect`
|
|
39
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
40
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
41
|
+
|
|
34
42
|
### Step 2: Search Relevant Domains
|
|
35
43
|
|
|
36
44
|
Use `search.py` to gather comprehensive information:
|
|
@@ -19,7 +19,14 @@ Before coding, ask:
|
|
|
19
19
|
- Which tables contain the data?
|
|
20
20
|
- How does YOUR company define key metrics?
|
|
21
21
|
|
|
22
|
-
### 2.
|
|
22
|
+
### 2. Secure Data Access
|
|
23
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
24
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
25
|
+
`npx crushdataai connect`
|
|
26
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
27
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
28
|
+
|
|
29
|
+
### 3. Search Knowledge Base
|
|
23
30
|
```bash
|
|
24
31
|
python3 .github/prompts/../.shared/data-analyst/scripts/search.py "<query>" --domain <domain>
|
|
25
32
|
```
|
|
@@ -16,6 +16,13 @@ Before coding, ask:
|
|
|
16
16
|
- Which tables contain the data?
|
|
17
17
|
- How does YOUR company define the key metrics?
|
|
18
18
|
|
|
19
|
+
### 2. Secure Data Access
|
|
20
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
21
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
22
|
+
`npx crushdataai connect`
|
|
23
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
24
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
25
|
+
|
|
19
26
|
### 2. Search Knowledge Base
|
|
20
27
|
```bash
|
|
21
28
|
python3 .cursor/commands/../.shared/data-analyst/scripts/search.py "<query>" --domain <domain>
|
|
@@ -15,7 +15,14 @@ Before writing code, gather:
|
|
|
15
15
|
- Company-specific metric definitions
|
|
16
16
|
- Time range and filters
|
|
17
17
|
|
|
18
|
-
### 2.
|
|
18
|
+
### 2. Secure Data Access
|
|
19
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
20
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
21
|
+
`npx crushdataai connect`
|
|
22
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
23
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
24
|
+
|
|
25
|
+
### 3. Search Before Implementing
|
|
19
26
|
```bash
|
|
20
27
|
python3 .kiro/steering/../.shared/data-analyst/scripts/search.py "<query>" --domain <domain>
|
|
21
28
|
```
|
|
@@ -13,7 +13,14 @@ Ask before coding:
|
|
|
13
13
|
- Which tables/databases contain the data
|
|
14
14
|
- Company-specific metric definitions
|
|
15
15
|
|
|
16
|
-
### 2.
|
|
16
|
+
### 2. Secure Data Access
|
|
17
|
+
- **Check Connections**: Run `npx crushdataai connections` first.
|
|
18
|
+
- **Missing Data?**: If the data source is not listed (e.g. on Desktop/Database), **INSTRUCT** the user to run:
|
|
19
|
+
`npx crushdataai connect`
|
|
20
|
+
- **Get Code**: Use `npx crushdataai snippet <name>` to access data.
|
|
21
|
+
- **Security**: **DO NOT** ask for credentials or manual file moves.
|
|
22
|
+
|
|
23
|
+
### 3. Search Knowledge
|
|
17
24
|
```bash
|
|
18
25
|
python3 .windsurf/workflows/../.shared/data-analyst/scripts/search.py "<query>" --domain <domain>
|
|
19
26
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function snippet(connectionName: string, lang?: string): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snippet = snippet;
|
|
4
|
+
const connections_1 = require("../connections");
|
|
5
|
+
const connectors_1 = require("../connectors");
|
|
6
|
+
async function snippet(connectionName, lang = 'python') {
|
|
7
|
+
const connection = (0, connections_1.getConnection)(connectionName);
|
|
8
|
+
if (!connection) {
|
|
9
|
+
console.error(`❌ Connection '${connectionName}' not found.`);
|
|
10
|
+
console.error(` Run 'crushdataai connections' to see available connections.`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const connector = connectors_1.ConnectorRegistry.get(connection.type);
|
|
15
|
+
if (!connector) {
|
|
16
|
+
console.error(`❌ No connector found for type: ${connection.type}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const code = connector.getSnippet(connection, lang);
|
|
20
|
+
console.log(code);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error(`❌ Error generating snippet: ${error.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/dist/commands.js
CHANGED
|
@@ -148,12 +148,12 @@ function init(aiType, force) {
|
|
|
148
148
|
console.log(` - Search: python3 .shared/data-analyst/scripts/search.py "<query>" --domain <domain>\n`);
|
|
149
149
|
}
|
|
150
150
|
function update() {
|
|
151
|
-
console.log('\n🔄
|
|
152
|
-
console.log(' Run: npm
|
|
153
|
-
console.log('
|
|
151
|
+
console.log('\n🔄 To update CrushData AI:');
|
|
152
|
+
console.log(' 1. Run: npm install -g crushdataai@latest');
|
|
153
|
+
console.log(' 2. Run: crushdataai init --force (to update project files)\n');
|
|
154
154
|
}
|
|
155
155
|
function versions() {
|
|
156
156
|
console.log('\n📦 CrushData AI Versions');
|
|
157
|
-
console.log(' Installed: 1.
|
|
157
|
+
console.log(' Installed: 1.2.1');
|
|
158
158
|
console.log(' Latest: Check npm: npm show crushdataai version\n');
|
|
159
159
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ConnectionType = 'mysql' | 'postgresql' | 'bigquery' | 'snowflake' | 'shopify' | 'csv';
|
|
2
|
+
export interface Connection {
|
|
3
|
+
name: string;
|
|
4
|
+
type: ConnectionType;
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
database?: string;
|
|
8
|
+
user?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
projectId?: string;
|
|
11
|
+
keyFile?: string;
|
|
12
|
+
account?: string;
|
|
13
|
+
warehouse?: string;
|
|
14
|
+
store?: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
apiSecret?: string;
|
|
17
|
+
filePath?: string;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ConnectionsConfig {
|
|
22
|
+
version: string;
|
|
23
|
+
connections: Connection[];
|
|
24
|
+
}
|
|
25
|
+
export declare function encrypt(text: string): string;
|
|
26
|
+
export declare function decrypt(encryptedText: string): string;
|
|
27
|
+
export declare function loadConnections(): ConnectionsConfig;
|
|
28
|
+
export declare function saveConnections(config: ConnectionsConfig): void;
|
|
29
|
+
export declare function saveConnection(connection: Connection): void;
|
|
30
|
+
export declare function getConnection(name: string): Connection | null;
|
|
31
|
+
export declare function deleteConnection(name: string): boolean;
|
|
32
|
+
export declare function listConnections(): Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
type: ConnectionType;
|
|
35
|
+
host?: string;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.encrypt = encrypt;
|
|
37
|
+
exports.decrypt = decrypt;
|
|
38
|
+
exports.loadConnections = loadConnections;
|
|
39
|
+
exports.saveConnections = saveConnections;
|
|
40
|
+
exports.saveConnection = saveConnection;
|
|
41
|
+
exports.getConnection = getConnection;
|
|
42
|
+
exports.deleteConnection = deleteConnection;
|
|
43
|
+
exports.listConnections = listConnections;
|
|
44
|
+
const crypto = __importStar(require("crypto"));
|
|
45
|
+
const fs = __importStar(require("fs-extra"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
// Get config directory
|
|
49
|
+
function getConfigDir() {
|
|
50
|
+
return path.join(os.homedir(), '.crushdataai');
|
|
51
|
+
}
|
|
52
|
+
function getConfigPath() {
|
|
53
|
+
return path.join(getConfigDir(), 'connections.json');
|
|
54
|
+
}
|
|
55
|
+
// Get machine-specific encryption key
|
|
56
|
+
function getEncryptionKey() {
|
|
57
|
+
const machineId = os.hostname() + os.userInfo().username;
|
|
58
|
+
return crypto.scryptSync(machineId, 'crushdataai-salt', 32);
|
|
59
|
+
}
|
|
60
|
+
// Encrypt sensitive data
|
|
61
|
+
function encrypt(text) {
|
|
62
|
+
if (!text)
|
|
63
|
+
return '';
|
|
64
|
+
const key = getEncryptionKey();
|
|
65
|
+
const iv = crypto.randomBytes(16);
|
|
66
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
67
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
68
|
+
encrypted += cipher.final('hex');
|
|
69
|
+
return iv.toString('hex') + ':' + encrypted;
|
|
70
|
+
}
|
|
71
|
+
// Decrypt sensitive data
|
|
72
|
+
function decrypt(encryptedText) {
|
|
73
|
+
if (!encryptedText || !encryptedText.includes(':'))
|
|
74
|
+
return encryptedText;
|
|
75
|
+
try {
|
|
76
|
+
const key = getEncryptionKey();
|
|
77
|
+
const [ivHex, encrypted] = encryptedText.split(':');
|
|
78
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
79
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
80
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
81
|
+
decrypted += decipher.final('utf8');
|
|
82
|
+
return decrypted;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Load connections from config file
|
|
89
|
+
function loadConnections() {
|
|
90
|
+
const configPath = getConfigPath();
|
|
91
|
+
if (!fs.existsSync(configPath)) {
|
|
92
|
+
return { version: '2.0.0', connections: [] };
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const data = fs.readJsonSync(configPath);
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return { version: '2.0.0', connections: [] };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Save connections to config file
|
|
103
|
+
function saveConnections(config) {
|
|
104
|
+
const configDir = getConfigDir();
|
|
105
|
+
const configPath = getConfigPath();
|
|
106
|
+
fs.ensureDirSync(configDir);
|
|
107
|
+
fs.writeJsonSync(configPath, config, { spaces: 2 });
|
|
108
|
+
// Set file permissions (user-only on Unix)
|
|
109
|
+
if (process.platform !== 'win32') {
|
|
110
|
+
fs.chmodSync(configPath, 0o600);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Add or update a connection
|
|
114
|
+
function saveConnection(connection) {
|
|
115
|
+
const config = loadConnections();
|
|
116
|
+
// Encrypt sensitive fields
|
|
117
|
+
const secureConnection = { ...connection };
|
|
118
|
+
if (secureConnection.password) {
|
|
119
|
+
secureConnection.password = encrypt(secureConnection.password);
|
|
120
|
+
}
|
|
121
|
+
if (secureConnection.apiKey) {
|
|
122
|
+
secureConnection.apiKey = encrypt(secureConnection.apiKey);
|
|
123
|
+
}
|
|
124
|
+
if (secureConnection.apiSecret) {
|
|
125
|
+
secureConnection.apiSecret = encrypt(secureConnection.apiSecret);
|
|
126
|
+
}
|
|
127
|
+
// Update or add
|
|
128
|
+
const existingIndex = config.connections.findIndex(c => c.name === connection.name);
|
|
129
|
+
if (existingIndex >= 0) {
|
|
130
|
+
config.connections[existingIndex] = secureConnection;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
config.connections.push(secureConnection);
|
|
134
|
+
}
|
|
135
|
+
saveConnections(config);
|
|
136
|
+
}
|
|
137
|
+
// Get a connection (with decrypted credentials)
|
|
138
|
+
function getConnection(name) {
|
|
139
|
+
const config = loadConnections();
|
|
140
|
+
const connection = config.connections.find(c => c.name === name);
|
|
141
|
+
if (!connection)
|
|
142
|
+
return null;
|
|
143
|
+
// Decrypt sensitive fields
|
|
144
|
+
const decrypted = { ...connection };
|
|
145
|
+
if (decrypted.password) {
|
|
146
|
+
decrypted.password = decrypt(decrypted.password);
|
|
147
|
+
}
|
|
148
|
+
if (decrypted.apiKey) {
|
|
149
|
+
decrypted.apiKey = decrypt(decrypted.apiKey);
|
|
150
|
+
}
|
|
151
|
+
if (decrypted.apiSecret) {
|
|
152
|
+
decrypted.apiSecret = decrypt(decrypted.apiSecret);
|
|
153
|
+
}
|
|
154
|
+
return decrypted;
|
|
155
|
+
}
|
|
156
|
+
// Delete a connection
|
|
157
|
+
function deleteConnection(name) {
|
|
158
|
+
const config = loadConnections();
|
|
159
|
+
const initialLength = config.connections.length;
|
|
160
|
+
config.connections = config.connections.filter(c => c.name !== name);
|
|
161
|
+
if (config.connections.length < initialLength) {
|
|
162
|
+
saveConnections(config);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// List all connections (without sensitive data)
|
|
168
|
+
function listConnections() {
|
|
169
|
+
const config = loadConnections();
|
|
170
|
+
return config.connections.map(c => ({
|
|
171
|
+
name: c.name,
|
|
172
|
+
type: c.type,
|
|
173
|
+
host: c.host || c.store || c.account || c.filePath,
|
|
174
|
+
createdAt: c.createdAt
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Connector, Table, TableData } from '../index';
|
|
2
|
+
import { Connection } from '../../connections';
|
|
3
|
+
export declare class BigQueryConnector implements Connector {
|
|
4
|
+
type: string;
|
|
5
|
+
test(connection: Connection): Promise<boolean>;
|
|
6
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
7
|
+
getData(connection: Connection, table: string, page: number, limit: number): Promise<TableData>;
|
|
8
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
9
|
+
}
|
|
10
|
+
export declare class SnowflakeConnector implements Connector {
|
|
11
|
+
type: string;
|
|
12
|
+
test(connection: Connection): Promise<boolean>;
|
|
13
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
14
|
+
getData(connection: Connection, table: string, page: number, limit: number): Promise<TableData>;
|
|
15
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SnowflakeConnector = exports.BigQueryConnector = void 0;
|
|
4
|
+
class BigQueryConnector {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.type = 'bigquery';
|
|
7
|
+
}
|
|
8
|
+
async test(connection) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
async getTables(connection) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
async getData(connection, table, page, limit) {
|
|
15
|
+
return { columns: [], rows: [], pagination: { page, limit, totalRows: 0, totalPages: 0, startIdx: 0, endIdx: 0 } };
|
|
16
|
+
}
|
|
17
|
+
getSnippet(connection, lang) {
|
|
18
|
+
return `# BigQuery snippet generation not implemented yet`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.BigQueryConnector = BigQueryConnector;
|
|
22
|
+
class SnowflakeConnector {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.type = 'snowflake';
|
|
25
|
+
}
|
|
26
|
+
async test(connection) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
async getTables(connection) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
async getData(connection, table, page, limit) {
|
|
33
|
+
return { columns: [], rows: [], pagination: { page, limit, totalRows: 0, totalPages: 0, startIdx: 0, endIdx: 0 } };
|
|
34
|
+
}
|
|
35
|
+
getSnippet(connection, lang) {
|
|
36
|
+
return `# Snowflake snippet generation not implemented yet`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.SnowflakeConnector = SnowflakeConnector;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Connector, Table, TableData } from '../index';
|
|
2
|
+
import { Connection } from '../../connections';
|
|
3
|
+
export declare class CSVConnector implements Connector {
|
|
4
|
+
type: string;
|
|
5
|
+
test(connection: Connection): Promise<boolean>;
|
|
6
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
7
|
+
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
8
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.CSVConnector = void 0;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const papaparse_1 = __importDefault(require("papaparse"));
|
|
43
|
+
class CSVConnector {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.type = 'csv';
|
|
46
|
+
}
|
|
47
|
+
async test(connection) {
|
|
48
|
+
if (!connection.filePath) {
|
|
49
|
+
throw new Error('File path is required');
|
|
50
|
+
}
|
|
51
|
+
// Remove surrounding quotes if present
|
|
52
|
+
let filePath = connection.filePath;
|
|
53
|
+
if (filePath.startsWith('"') && filePath.endsWith('"')) {
|
|
54
|
+
filePath = filePath.slice(1, -1);
|
|
55
|
+
}
|
|
56
|
+
// Check if file exists
|
|
57
|
+
if (!fs.existsSync(filePath)) {
|
|
58
|
+
throw new Error('File not found');
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
async getTables(connection) {
|
|
63
|
+
if (!connection.filePath) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
let filePath = connection.filePath;
|
|
67
|
+
if (filePath.startsWith('"') && filePath.endsWith('"')) {
|
|
68
|
+
filePath = filePath.slice(1, -1);
|
|
69
|
+
}
|
|
70
|
+
const fileName = path.basename(filePath, '.csv');
|
|
71
|
+
// Basic check if file exists, if not it might have been deleted
|
|
72
|
+
if (fs.existsSync(filePath)) {
|
|
73
|
+
return [{ name: fileName, type: 'csv', rowCount: null }];
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
async getData(connection, tableName, page, limit) {
|
|
78
|
+
let filePath = connection.filePath;
|
|
79
|
+
if (filePath && filePath.startsWith('"') && filePath.endsWith('"')) {
|
|
80
|
+
filePath = filePath.slice(1, -1);
|
|
81
|
+
}
|
|
82
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
83
|
+
throw new Error('CSV file not found');
|
|
84
|
+
}
|
|
85
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
86
|
+
const result = papaparse_1.default.parse(fileContent, {
|
|
87
|
+
header: true,
|
|
88
|
+
skipEmptyLines: true
|
|
89
|
+
});
|
|
90
|
+
const data = result.data;
|
|
91
|
+
const columns = result.meta.fields || [];
|
|
92
|
+
const totalRows = data.length;
|
|
93
|
+
const totalPages = Math.ceil(totalRows / limit);
|
|
94
|
+
const startIdx = (page - 1) * limit;
|
|
95
|
+
const endIdx = Math.min(startIdx + limit, totalRows);
|
|
96
|
+
const rows = data.slice(startIdx, endIdx);
|
|
97
|
+
return {
|
|
98
|
+
columns,
|
|
99
|
+
rows,
|
|
100
|
+
pagination: {
|
|
101
|
+
page,
|
|
102
|
+
limit,
|
|
103
|
+
totalRows,
|
|
104
|
+
totalPages,
|
|
105
|
+
startIdx: startIdx + 1,
|
|
106
|
+
endIdx
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
getSnippet(connection, lang) {
|
|
111
|
+
let filePath = connection.filePath || '';
|
|
112
|
+
if (filePath.startsWith('"') && filePath.endsWith('"')) {
|
|
113
|
+
filePath = filePath.slice(1, -1);
|
|
114
|
+
}
|
|
115
|
+
// Escape backslashes for string literals
|
|
116
|
+
const escapedPath = filePath.replace(/\\/g, '\\\\');
|
|
117
|
+
if (lang === 'python') {
|
|
118
|
+
return `import pandas as pd
|
|
119
|
+
|
|
120
|
+
# Connection: ${connection.name}
|
|
121
|
+
# Type: csv
|
|
122
|
+
file_path = "${escapedPath}"
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
df = pd.read_csv(file_path)
|
|
126
|
+
print(f"Successfully loaded {len(df)} rows from ${connection.name}")
|
|
127
|
+
print(df.head())
|
|
128
|
+
except Exception as e:
|
|
129
|
+
print(f"Error loading CSV: {e}")
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
return `# Language ${lang} not supported for CSV connector yet.`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.CSVConnector = CSVConnector;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Connection } from '../connections';
|
|
2
|
+
export interface Table {
|
|
3
|
+
name: string;
|
|
4
|
+
type?: string;
|
|
5
|
+
rowCount?: number | null;
|
|
6
|
+
}
|
|
7
|
+
export interface TableData {
|
|
8
|
+
columns: string[];
|
|
9
|
+
rows: any[];
|
|
10
|
+
pagination: {
|
|
11
|
+
page: number;
|
|
12
|
+
limit: number;
|
|
13
|
+
totalRows: number;
|
|
14
|
+
totalPages: number;
|
|
15
|
+
startIdx: number;
|
|
16
|
+
endIdx: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface Connector {
|
|
20
|
+
type: string;
|
|
21
|
+
test(connection: Connection): Promise<boolean>;
|
|
22
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
23
|
+
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
24
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
25
|
+
}
|
|
26
|
+
export declare class ConnectorRegistry {
|
|
27
|
+
private static connectors;
|
|
28
|
+
static register(connector: Connector): void;
|
|
29
|
+
static get(type: string): Connector;
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectorRegistry = void 0;
|
|
4
|
+
class ConnectorRegistry {
|
|
5
|
+
static register(connector) {
|
|
6
|
+
this.connectors.set(connector.type, connector);
|
|
7
|
+
}
|
|
8
|
+
static get(type) {
|
|
9
|
+
const connector = this.connectors.get(type);
|
|
10
|
+
if (!connector) {
|
|
11
|
+
throw new Error(`No connector found for type: ${type}`);
|
|
12
|
+
}
|
|
13
|
+
return connector;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.ConnectorRegistry = ConnectorRegistry;
|
|
17
|
+
ConnectorRegistry.connectors = new Map();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Connector, Table, TableData } from '../index';
|
|
2
|
+
import { Connection } from '../../connections';
|
|
3
|
+
export declare class MySQLConnector implements Connector {
|
|
4
|
+
type: string;
|
|
5
|
+
test(connection: Connection): Promise<boolean>;
|
|
6
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
7
|
+
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
8
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MySQLConnector = void 0;
|
|
4
|
+
class MySQLConnector {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.type = 'mysql';
|
|
7
|
+
}
|
|
8
|
+
async test(connection) {
|
|
9
|
+
// Stub implementation
|
|
10
|
+
if (!connection.host || !connection.user || !connection.database) {
|
|
11
|
+
throw new Error('Host, user, and database are required');
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
async getTables(connection) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
async getData(connection, tableName, page, limit) {
|
|
19
|
+
return {
|
|
20
|
+
columns: [],
|
|
21
|
+
rows: [],
|
|
22
|
+
pagination: { page, limit, totalRows: 0, totalPages: 0, startIdx: 0, endIdx: 0 }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
getSnippet(connection, lang) {
|
|
26
|
+
return `# MySQL snippet generation not implemented yet`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.MySQLConnector = MySQLConnector;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Connector, Table, TableData } from '../index';
|
|
2
|
+
import { Connection } from '../../connections';
|
|
3
|
+
export declare class PostgreSQLConnector implements Connector {
|
|
4
|
+
type: string;
|
|
5
|
+
test(connection: Connection): Promise<boolean>;
|
|
6
|
+
getTables(connection: Connection): Promise<Table[]>;
|
|
7
|
+
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
8
|
+
getSnippet(connection: Connection, lang: string): string;
|
|
9
|
+
}
|