crushdataai 1.2.7 → 1.2.9
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 +8 -4
- package/assets/claude/SKILL.md +7 -2
- package/assets/copilot/data-analyst.prompt.md +8 -4
- package/assets/cursor/data-analyst.md +8 -4
- package/assets/kiro/data-analyst.md +8 -4
- package/assets/windsurf/data-analyst.md +8 -4
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/schema.js +54 -0
- package/dist/connections.d.ts +3 -1
- package/dist/connections.js +125 -0
- package/dist/connectors/additional/index.d.ts +42 -0
- package/dist/connectors/additional/index.js +268 -0
- package/dist/connectors/cloud/index.d.ts +8 -3
- package/dist/connectors/cloud/index.js +321 -10
- package/dist/connectors/csv/index.d.ts +1 -0
- package/dist/connectors/csv/index.js +27 -7
- package/dist/connectors/custom/index.d.ts +10 -0
- package/dist/connectors/custom/index.js +61 -0
- package/dist/connectors/index.d.ts +6 -0
- package/dist/connectors/mysql/index.d.ts +1 -0
- package/dist/connectors/mysql/index.js +162 -9
- package/dist/connectors/postgresql/index.d.ts +2 -0
- package/dist/connectors/postgresql/index.js +140 -8
- package/dist/connectors/shopify/index.d.ts +1 -0
- package/dist/connectors/shopify/index.js +44 -5
- package/dist/index.js +7 -0
- package/dist/routes/connections.js +2 -0
- package/dist/server.js +8 -0
- package/package.json +9 -4
- package/ui/assets/index-DK2tLINh.js +40 -0
- package/ui/assets/index-rlcHFDJB.css +1 -0
- package/ui/favicon.svg +4 -18
- package/ui/index.html +7 -324
- package/ui/favicon-32x32.png +0 -0
- package/ui/main.js +0 -541
- package/ui/styles.css +0 -718
|
@@ -1,29 +1,182 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.MySQLConnector = void 0;
|
|
7
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
4
8
|
class MySQLConnector {
|
|
5
9
|
constructor() {
|
|
6
10
|
this.type = 'mysql';
|
|
7
11
|
}
|
|
8
12
|
async test(connection) {
|
|
9
|
-
|
|
13
|
+
console.log(`[MySQL] Testing connection for ${connection.name} (Host: ${connection.host})`);
|
|
10
14
|
if (!connection.host || !connection.user || !connection.database) {
|
|
11
15
|
throw new Error('Host, user, and database are required');
|
|
12
16
|
}
|
|
13
|
-
|
|
17
|
+
let conn = null;
|
|
18
|
+
try {
|
|
19
|
+
conn = await promise_1.default.createConnection({
|
|
20
|
+
host: connection.host,
|
|
21
|
+
port: connection.port || 3306,
|
|
22
|
+
user: connection.user,
|
|
23
|
+
password: connection.password || '',
|
|
24
|
+
database: connection.database,
|
|
25
|
+
connectTimeout: 10000
|
|
26
|
+
});
|
|
27
|
+
await conn.execute('SELECT 1');
|
|
28
|
+
console.log(`[MySQL] Connection test successful for ${connection.name}`);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`[MySQL] Connection test failed:`, error.message);
|
|
33
|
+
throw new Error(`MySQL connection failed: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
if (conn)
|
|
37
|
+
await conn.end();
|
|
38
|
+
}
|
|
14
39
|
}
|
|
15
40
|
async getTables(connection) {
|
|
16
|
-
|
|
41
|
+
console.log(`[MySQL] getTables called for ${connection.name}`);
|
|
42
|
+
let conn = null;
|
|
43
|
+
try {
|
|
44
|
+
conn = await promise_1.default.createConnection({
|
|
45
|
+
host: connection.host,
|
|
46
|
+
port: connection.port || 3306,
|
|
47
|
+
user: connection.user,
|
|
48
|
+
password: connection.password || '',
|
|
49
|
+
database: connection.database
|
|
50
|
+
});
|
|
51
|
+
const [rows] = await conn.execute(`
|
|
52
|
+
SELECT
|
|
53
|
+
TABLE_NAME as name,
|
|
54
|
+
TABLE_ROWS as rowCount
|
|
55
|
+
FROM information_schema.TABLES
|
|
56
|
+
WHERE TABLE_SCHEMA = ?
|
|
57
|
+
ORDER BY TABLE_NAME
|
|
58
|
+
`, [connection.database]);
|
|
59
|
+
return rows.map(row => ({
|
|
60
|
+
name: row.name,
|
|
61
|
+
type: 'table',
|
|
62
|
+
rowCount: row.rowCount
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`[MySQL] getTables failed:`, error.message);
|
|
67
|
+
throw new Error(`Failed to fetch tables: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
if (conn)
|
|
71
|
+
await conn.end();
|
|
72
|
+
}
|
|
17
73
|
}
|
|
18
74
|
async getData(connection, tableName, page, limit) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
75
|
+
console.log(`[MySQL] getData called for ${connection.name}, table: ${tableName}, page: ${page}`);
|
|
76
|
+
let conn = null;
|
|
77
|
+
try {
|
|
78
|
+
conn = await promise_1.default.createConnection({
|
|
79
|
+
host: connection.host,
|
|
80
|
+
port: connection.port || 3306,
|
|
81
|
+
user: connection.user,
|
|
82
|
+
password: connection.password || '',
|
|
83
|
+
database: connection.database
|
|
84
|
+
});
|
|
85
|
+
// Get total row count
|
|
86
|
+
const [countResult] = await conn.execute(`SELECT COUNT(*) as total FROM \`${tableName}\``);
|
|
87
|
+
const totalRows = countResult[0]?.total || 0;
|
|
88
|
+
// Get paginated data
|
|
89
|
+
const offset = (page - 1) * limit;
|
|
90
|
+
const [rows] = await conn.execute(`SELECT * FROM \`${tableName}\` LIMIT ? OFFSET ?`, [limit, offset]);
|
|
91
|
+
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
92
|
+
const totalPages = Math.ceil(totalRows / limit) || 1;
|
|
93
|
+
return {
|
|
94
|
+
columns,
|
|
95
|
+
rows: rows,
|
|
96
|
+
pagination: {
|
|
97
|
+
page,
|
|
98
|
+
limit,
|
|
99
|
+
totalRows,
|
|
100
|
+
totalPages,
|
|
101
|
+
startIdx: offset + 1,
|
|
102
|
+
endIdx: offset + rows.length
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(`[MySQL] getData failed:`, error.message);
|
|
108
|
+
throw new Error(`Failed to fetch data: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (conn)
|
|
112
|
+
await conn.end();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async getSchema(connection, tableName) {
|
|
116
|
+
console.log(`[MySQL] getSchema called for ${connection.name}, table: ${tableName}`);
|
|
117
|
+
let conn = null;
|
|
118
|
+
try {
|
|
119
|
+
conn = await promise_1.default.createConnection({
|
|
120
|
+
host: connection.host,
|
|
121
|
+
port: connection.port || 3306,
|
|
122
|
+
user: connection.user,
|
|
123
|
+
password: connection.password || '',
|
|
124
|
+
database: connection.database
|
|
125
|
+
});
|
|
126
|
+
const [rows] = await conn.execute(`
|
|
127
|
+
SELECT
|
|
128
|
+
COLUMN_NAME as name,
|
|
129
|
+
DATA_TYPE as type,
|
|
130
|
+
IS_NULLABLE as nullable
|
|
131
|
+
FROM information_schema.COLUMNS
|
|
132
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
|
|
133
|
+
ORDER BY ORDINAL_POSITION
|
|
134
|
+
`, [connection.database, tableName]);
|
|
135
|
+
return rows.map(row => ({
|
|
136
|
+
name: row.name,
|
|
137
|
+
type: row.type,
|
|
138
|
+
nullable: row.nullable === 'YES'
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error(`[MySQL] getSchema failed:`, error.message);
|
|
143
|
+
throw new Error(`Failed to fetch schema: ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (conn)
|
|
147
|
+
await conn.end();
|
|
148
|
+
}
|
|
24
149
|
}
|
|
25
150
|
getSnippet(connection, lang) {
|
|
26
|
-
|
|
151
|
+
// Generate env var names
|
|
152
|
+
const prefix = connection.name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
153
|
+
if (lang === 'python') {
|
|
154
|
+
return `import os
|
|
155
|
+
import pymysql
|
|
156
|
+
import pandas as pd
|
|
157
|
+
|
|
158
|
+
# Connection: ${connection.name}
|
|
159
|
+
# Type: mysql
|
|
160
|
+
# Credentials loaded from environment variables (set in .env file)
|
|
161
|
+
connection = pymysql.connect(
|
|
162
|
+
host=os.environ["${prefix}_HOST"],
|
|
163
|
+
port=int(os.environ.get("${prefix}_PORT", "3306")),
|
|
164
|
+
user=os.environ["${prefix}_USER"],
|
|
165
|
+
password=os.environ["${prefix}_PASSWORD"],
|
|
166
|
+
database=os.environ["${prefix}_DATABASE"]
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
# Example: Fetch data from a table
|
|
171
|
+
query = "SELECT * FROM your_table LIMIT 100"
|
|
172
|
+
df = pd.read_sql(query, connection)
|
|
173
|
+
print(f"Successfully loaded {len(df)} rows from ${connection.name}")
|
|
174
|
+
print(df.head())
|
|
175
|
+
finally:
|
|
176
|
+
connection.close()
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
return `# Language ${lang} not supported for MySQL connector yet.`;
|
|
27
180
|
}
|
|
28
181
|
}
|
|
29
182
|
exports.MySQLConnector = MySQLConnector;
|
|
@@ -2,8 +2,10 @@ import { Connector, Table, TableData } from '../index';
|
|
|
2
2
|
import { Connection } from '../../connections';
|
|
3
3
|
export declare class PostgreSQLConnector implements Connector {
|
|
4
4
|
type: string;
|
|
5
|
+
private createClient;
|
|
5
6
|
test(connection: Connection): Promise<boolean>;
|
|
6
7
|
getTables(connection: Connection): Promise<Table[]>;
|
|
7
8
|
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
9
|
+
getSchema(connection: Connection, tableName: string): Promise<import('../index').ColumnInfo[]>;
|
|
8
10
|
getSnippet(connection: Connection, lang: string): string;
|
|
9
11
|
}
|
|
@@ -1,28 +1,160 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PostgreSQLConnector = void 0;
|
|
4
|
+
const pg_1 = require("pg");
|
|
4
5
|
class PostgreSQLConnector {
|
|
5
6
|
constructor() {
|
|
6
7
|
this.type = 'postgresql';
|
|
7
8
|
}
|
|
9
|
+
createClient(connection) {
|
|
10
|
+
return new pg_1.Client({
|
|
11
|
+
host: connection.host,
|
|
12
|
+
port: connection.port || 5432,
|
|
13
|
+
user: connection.user,
|
|
14
|
+
password: connection.password || '',
|
|
15
|
+
database: connection.database,
|
|
16
|
+
connectionTimeoutMillis: 10000
|
|
17
|
+
});
|
|
18
|
+
}
|
|
8
19
|
async test(connection) {
|
|
20
|
+
console.log(`[PostgreSQL] Testing connection for ${connection.name} (Host: ${connection.host})`);
|
|
9
21
|
if (!connection.host || !connection.user || !connection.database) {
|
|
10
22
|
throw new Error('Host, user, and database are required');
|
|
11
23
|
}
|
|
12
|
-
|
|
24
|
+
const client = this.createClient(connection);
|
|
25
|
+
try {
|
|
26
|
+
await client.connect();
|
|
27
|
+
await client.query('SELECT NOW()');
|
|
28
|
+
console.log(`[PostgreSQL] Connection test successful for ${connection.name}`);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`[PostgreSQL] Connection test failed:`, error.message);
|
|
33
|
+
throw new Error(`PostgreSQL connection failed: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await client.end();
|
|
37
|
+
}
|
|
13
38
|
}
|
|
14
39
|
async getTables(connection) {
|
|
15
|
-
|
|
40
|
+
console.log(`[PostgreSQL] getTables called for ${connection.name}`);
|
|
41
|
+
const client = this.createClient(connection);
|
|
42
|
+
try {
|
|
43
|
+
await client.connect();
|
|
44
|
+
const result = await client.query(`
|
|
45
|
+
SELECT
|
|
46
|
+
t.table_name as name,
|
|
47
|
+
(SELECT reltuples::bigint FROM pg_class WHERE relname = t.table_name) as row_count
|
|
48
|
+
FROM information_schema.tables t
|
|
49
|
+
WHERE t.table_schema = 'public'
|
|
50
|
+
AND t.table_type = 'BASE TABLE'
|
|
51
|
+
ORDER BY t.table_name
|
|
52
|
+
`);
|
|
53
|
+
return result.rows.map(row => ({
|
|
54
|
+
name: row.name,
|
|
55
|
+
type: 'table',
|
|
56
|
+
rowCount: row.row_count
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(`[PostgreSQL] getTables failed:`, error.message);
|
|
61
|
+
throw new Error(`Failed to fetch tables: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await client.end();
|
|
65
|
+
}
|
|
16
66
|
}
|
|
17
67
|
async getData(connection, tableName, page, limit) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
68
|
+
console.log(`[PostgreSQL] getData called for ${connection.name}, table: ${tableName}, page: ${page}`);
|
|
69
|
+
const client = this.createClient(connection);
|
|
70
|
+
try {
|
|
71
|
+
await client.connect();
|
|
72
|
+
// Get total row count
|
|
73
|
+
const countResult = await client.query(`SELECT COUNT(*) as total FROM "${tableName}"`);
|
|
74
|
+
const totalRows = parseInt(countResult.rows[0]?.total || '0', 10);
|
|
75
|
+
// Get paginated data
|
|
76
|
+
const offset = (page - 1) * limit;
|
|
77
|
+
const dataResult = await client.query(`SELECT * FROM "${tableName}" LIMIT $1 OFFSET $2`, [limit, offset]);
|
|
78
|
+
const columns = dataResult.fields.map(f => f.name);
|
|
79
|
+
const totalPages = Math.ceil(totalRows / limit) || 1;
|
|
80
|
+
return {
|
|
81
|
+
columns,
|
|
82
|
+
rows: dataResult.rows,
|
|
83
|
+
pagination: {
|
|
84
|
+
page,
|
|
85
|
+
limit,
|
|
86
|
+
totalRows,
|
|
87
|
+
totalPages,
|
|
88
|
+
startIdx: offset + 1,
|
|
89
|
+
endIdx: offset + dataResult.rows.length
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error(`[PostgreSQL] getData failed:`, error.message);
|
|
95
|
+
throw new Error(`Failed to fetch data: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await client.end();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async getSchema(connection, tableName) {
|
|
102
|
+
console.log(`[PostgreSQL] getSchema called for ${connection.name}, table: ${tableName}`);
|
|
103
|
+
const client = this.createClient(connection);
|
|
104
|
+
try {
|
|
105
|
+
await client.connect();
|
|
106
|
+
const result = await client.query(`
|
|
107
|
+
SELECT
|
|
108
|
+
column_name as name,
|
|
109
|
+
data_type as type,
|
|
110
|
+
is_nullable as nullable
|
|
111
|
+
FROM information_schema.columns
|
|
112
|
+
WHERE table_schema = 'public' AND table_name = $1
|
|
113
|
+
ORDER BY ordinal_position
|
|
114
|
+
`, [tableName]);
|
|
115
|
+
return result.rows.map(row => ({
|
|
116
|
+
name: row.name,
|
|
117
|
+
type: row.type,
|
|
118
|
+
nullable: row.nullable === 'YES'
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error(`[PostgreSQL] getSchema failed:`, error.message);
|
|
123
|
+
throw new Error(`Failed to fetch schema: ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
await client.end();
|
|
127
|
+
}
|
|
23
128
|
}
|
|
24
129
|
getSnippet(connection, lang) {
|
|
25
|
-
|
|
130
|
+
const prefix = connection.name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
131
|
+
if (lang === 'python') {
|
|
132
|
+
return `import os
|
|
133
|
+
import psycopg2
|
|
134
|
+
import pandas as pd
|
|
135
|
+
|
|
136
|
+
# Connection: ${connection.name}
|
|
137
|
+
# Type: postgresql
|
|
138
|
+
# Credentials loaded from environment variables (set in .env file)
|
|
139
|
+
connection = psycopg2.connect(
|
|
140
|
+
host=os.environ["${prefix}_HOST"],
|
|
141
|
+
port=int(os.environ.get("${prefix}_PORT", "5432")),
|
|
142
|
+
user=os.environ["${prefix}_USER"],
|
|
143
|
+
password=os.environ["${prefix}_PASSWORD"],
|
|
144
|
+
dbname=os.environ["${prefix}_DATABASE"]
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
# Example: Fetch data from a table
|
|
149
|
+
query = "SELECT * FROM your_table LIMIT 100"
|
|
150
|
+
df = pd.read_sql(query, connection)
|
|
151
|
+
print(f"Successfully loaded {len(df)} rows from ${connection.name}")
|
|
152
|
+
print(df.head())
|
|
153
|
+
finally:
|
|
154
|
+
connection.close()
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
return `# Language ${lang} not supported for PostgreSQL connector yet.`;
|
|
26
158
|
}
|
|
27
159
|
}
|
|
28
160
|
exports.PostgreSQLConnector = PostgreSQLConnector;
|
|
@@ -6,5 +6,6 @@ export declare class ShopifyConnector implements Connector {
|
|
|
6
6
|
getTables(connection: Connection): Promise<Table[]>;
|
|
7
7
|
private fetchTotalCount;
|
|
8
8
|
getData(connection: Connection, tableName: string, page: number, limit: number): Promise<TableData>;
|
|
9
|
+
getSchema(connection: Connection, tableName: string): Promise<import('../index').ColumnInfo[]>;
|
|
9
10
|
getSnippet(connection: Connection, lang: string): string;
|
|
10
11
|
}
|
|
@@ -133,17 +133,56 @@ class ShopifyConnector {
|
|
|
133
133
|
throw new Error(`Failed to fetch data: ${error.message}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
async getSchema(connection, tableName) {
|
|
137
|
+
// Shopify schemas are predefined - return common fields for each table type
|
|
138
|
+
const schemas = {
|
|
139
|
+
orders: [
|
|
140
|
+
{ name: 'id', type: 'integer', nullable: false },
|
|
141
|
+
{ name: 'email', type: 'string', nullable: true },
|
|
142
|
+
{ name: 'created_at', type: 'datetime', nullable: false },
|
|
143
|
+
{ name: 'total_price', type: 'decimal', nullable: false },
|
|
144
|
+
{ name: 'currency', type: 'string', nullable: false },
|
|
145
|
+
{ name: 'financial_status', type: 'string', nullable: true },
|
|
146
|
+
{ name: 'fulfillment_status', type: 'string', nullable: true },
|
|
147
|
+
{ name: 'customer', type: 'json', nullable: true },
|
|
148
|
+
{ name: 'line_items', type: 'json', nullable: false }
|
|
149
|
+
],
|
|
150
|
+
products: [
|
|
151
|
+
{ name: 'id', type: 'integer', nullable: false },
|
|
152
|
+
{ name: 'title', type: 'string', nullable: false },
|
|
153
|
+
{ name: 'body_html', type: 'string', nullable: true },
|
|
154
|
+
{ name: 'vendor', type: 'string', nullable: true },
|
|
155
|
+
{ name: 'product_type', type: 'string', nullable: true },
|
|
156
|
+
{ name: 'created_at', type: 'datetime', nullable: false },
|
|
157
|
+
{ name: 'handle', type: 'string', nullable: false },
|
|
158
|
+
{ name: 'status', type: 'string', nullable: false },
|
|
159
|
+
{ name: 'variants', type: 'json', nullable: false }
|
|
160
|
+
],
|
|
161
|
+
customers: [
|
|
162
|
+
{ name: 'id', type: 'integer', nullable: false },
|
|
163
|
+
{ name: 'email', type: 'string', nullable: true },
|
|
164
|
+
{ name: 'first_name', type: 'string', nullable: true },
|
|
165
|
+
{ name: 'last_name', type: 'string', nullable: true },
|
|
166
|
+
{ name: 'created_at', type: 'datetime', nullable: false },
|
|
167
|
+
{ name: 'orders_count', type: 'integer', nullable: false },
|
|
168
|
+
{ name: 'total_spent', type: 'decimal', nullable: false },
|
|
169
|
+
{ name: 'addresses', type: 'json', nullable: true }
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
return schemas[tableName] || [];
|
|
173
|
+
}
|
|
136
174
|
getSnippet(connection, lang) {
|
|
137
|
-
const
|
|
138
|
-
const apiKey = connection.apiKey || 'shpat_...';
|
|
175
|
+
const prefix = connection.name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
139
176
|
if (lang === 'python') {
|
|
140
|
-
return `import
|
|
177
|
+
return `import os
|
|
178
|
+
import requests
|
|
141
179
|
import pandas as pd
|
|
142
180
|
|
|
143
181
|
# Connection: ${connection.name}
|
|
144
182
|
# Type: shopify
|
|
145
|
-
|
|
146
|
-
|
|
183
|
+
# Credentials loaded from environment variables (set in .env file)
|
|
184
|
+
shop_url = os.environ["${prefix}_STORE"]
|
|
185
|
+
access_token = os.environ["${prefix}_API_KEY"]
|
|
147
186
|
api_version = "2024-04"
|
|
148
187
|
|
|
149
188
|
def fetch_shopify_data(endpoint):
|
package/dist/index.js
CHANGED
|
@@ -118,4 +118,11 @@ program
|
|
|
118
118
|
const { snippet } = await Promise.resolve().then(() => __importStar(require('./commands/snippet')));
|
|
119
119
|
await snippet(name, options.lang);
|
|
120
120
|
});
|
|
121
|
+
program
|
|
122
|
+
.command('schema <connection> [table]')
|
|
123
|
+
.description('Show table schema (columns) for a connection')
|
|
124
|
+
.action(async (connection, table) => {
|
|
125
|
+
const { schema } = await Promise.resolve().then(() => __importStar(require('./commands/schema')));
|
|
126
|
+
await schema(connection, table);
|
|
127
|
+
});
|
|
121
128
|
program.parse();
|
|
@@ -45,7 +45,9 @@ router.post('/', (req, res) => {
|
|
|
45
45
|
res.status(400).json({ success: false, error: 'Name and type are required' });
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
+
// Save to encrypted config AND export to .env
|
|
48
49
|
(0, connections_1.saveConnection)(connection);
|
|
50
|
+
(0, connections_1.writeToEnvFile)(connection);
|
|
49
51
|
res.json({ success: true, message: 'Connection saved' });
|
|
50
52
|
}
|
|
51
53
|
catch (error) {
|
package/dist/server.js
CHANGED
|
@@ -45,6 +45,8 @@ const mysql_1 = require("./connectors/mysql");
|
|
|
45
45
|
const postgresql_1 = require("./connectors/postgresql");
|
|
46
46
|
const shopify_1 = require("./connectors/shopify");
|
|
47
47
|
const cloud_1 = require("./connectors/cloud");
|
|
48
|
+
const custom_1 = require("./connectors/custom");
|
|
49
|
+
const additional_1 = require("./connectors/additional");
|
|
48
50
|
const connections_1 = __importDefault(require("./routes/connections"));
|
|
49
51
|
const app = (0, express_1.default)();
|
|
50
52
|
app.use(express_1.default.json());
|
|
@@ -60,6 +62,12 @@ connectors_1.ConnectorRegistry.register(new postgresql_1.PostgreSQLConnector());
|
|
|
60
62
|
connectors_1.ConnectorRegistry.register(new shopify_1.ShopifyConnector());
|
|
61
63
|
connectors_1.ConnectorRegistry.register(new cloud_1.BigQueryConnector());
|
|
62
64
|
connectors_1.ConnectorRegistry.register(new cloud_1.SnowflakeConnector());
|
|
65
|
+
connectors_1.ConnectorRegistry.register(new custom_1.CustomConnector());
|
|
66
|
+
connectors_1.ConnectorRegistry.register(new additional_1.SQLServerConnector());
|
|
67
|
+
connectors_1.ConnectorRegistry.register(new additional_1.RedshiftConnector());
|
|
68
|
+
connectors_1.ConnectorRegistry.register(new additional_1.DatabricksConnector());
|
|
69
|
+
connectors_1.ConnectorRegistry.register(new additional_1.ClickHouseConnector());
|
|
70
|
+
connectors_1.ConnectorRegistry.register(new additional_1.MongoDBConnector());
|
|
63
71
|
// Serve static files from ui directory
|
|
64
72
|
const uiPath = path.join(__dirname, '..', 'ui');
|
|
65
73
|
app.use(express_1.default.static(uiPath));
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crushdataai",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.9",
|
|
4
4
|
"description": "CLI to install CrushData AI data analyst skill for AI coding assistants",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"crushdataai": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "tsc",
|
|
10
|
+
"build": "tsc && cd ui-react && npm install && npm run build",
|
|
11
11
|
"dev": "ts-node src/index.ts",
|
|
12
12
|
"prepublishOnly": "npm run build"
|
|
13
13
|
},
|
|
@@ -27,14 +27,19 @@
|
|
|
27
27
|
"@types/fs-extra": "^11.0.4",
|
|
28
28
|
"@types/node": "^20.10.0",
|
|
29
29
|
"@types/papaparse": "^5.5.2",
|
|
30
|
+
"@types/pg": "^8.16.0",
|
|
30
31
|
"typescript": "^5.3.0"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
34
|
+
"@google-cloud/bigquery": "^8.1.1",
|
|
33
35
|
"commander": "^11.1.0",
|
|
34
36
|
"express": "^4.18.2",
|
|
35
37
|
"fs-extra": "^11.2.0",
|
|
38
|
+
"mysql2": "^3.16.0",
|
|
36
39
|
"open": "^10.0.0",
|
|
37
|
-
"papaparse": "^5.5.3"
|
|
40
|
+
"papaparse": "^5.5.3",
|
|
41
|
+
"pg": "^8.16.3",
|
|
42
|
+
"snowflake-sdk": "^2.3.3"
|
|
38
43
|
},
|
|
39
44
|
"engines": {
|
|
40
45
|
"node": ">=18.0.0"
|
|
@@ -48,4 +53,4 @@
|
|
|
48
53
|
"type": "git",
|
|
49
54
|
"url": "git+https://github.com/SankaiAI/crushdataai-agent.git"
|
|
50
55
|
}
|
|
51
|
-
}
|
|
56
|
+
}
|