@squadbase/connectors 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/index.d.ts +718 -69
- package/dist/index.js +2946 -92
- package/package.json +12 -18
- package/dist/index.ui.d.ts +0 -14
- package/dist/index.ui.js +0 -387
package/dist/index.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
// src/connector-setup.ts
|
|
2
|
+
var ConnectorSetup = class {
|
|
3
|
+
prompts;
|
|
4
|
+
constructor(prompts) {
|
|
5
|
+
this.prompts = prompts;
|
|
6
|
+
}
|
|
7
|
+
getPrompt(language) {
|
|
8
|
+
return this.prompts[language];
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
1
12
|
// src/parameter-definition.ts
|
|
2
13
|
var ParameterDefinition = class {
|
|
3
14
|
slug;
|
|
@@ -17,10 +28,12 @@ var ParameterDefinition = class {
|
|
|
17
28
|
this.required = config.required;
|
|
18
29
|
}
|
|
19
30
|
/**
|
|
20
|
-
*
|
|
31
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
21
32
|
*/
|
|
22
|
-
|
|
23
|
-
const param = connection.parameters.find(
|
|
33
|
+
getValue(connection) {
|
|
34
|
+
const param = connection.parameters.find(
|
|
35
|
+
(p) => p.parameterSlug === this.slug
|
|
36
|
+
);
|
|
24
37
|
if (!param || param.value == null) {
|
|
25
38
|
throw new Error(
|
|
26
39
|
`Parameter "${this.slug}" not found or has no value in connection "${connection.id}"`
|
|
@@ -29,22 +42,14 @@ var ParameterDefinition = class {
|
|
|
29
42
|
return param.value;
|
|
30
43
|
}
|
|
31
44
|
/**
|
|
32
|
-
*
|
|
45
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
33
46
|
*/
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const value = process.env[envVarName];
|
|
42
|
-
if (!value) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`Environment variable "${envVarName}" is not set (connection "${connectionId}", parameter "${this.slug}")`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return value;
|
|
47
|
+
tryGetValue(connection) {
|
|
48
|
+
const param = connection.parameters.find(
|
|
49
|
+
(p) => p.parameterSlug === this.slug
|
|
50
|
+
);
|
|
51
|
+
if (!param || param.value == null) return void 0;
|
|
52
|
+
return param.value;
|
|
48
53
|
}
|
|
49
54
|
};
|
|
50
55
|
|
|
@@ -73,35 +78,58 @@ var ConnectorTool = class {
|
|
|
73
78
|
};
|
|
74
79
|
|
|
75
80
|
// src/connector-plugin.ts
|
|
76
|
-
var ConnectorPlugin = class {
|
|
81
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
77
82
|
slug;
|
|
78
83
|
authType;
|
|
79
84
|
name;
|
|
80
85
|
description;
|
|
81
86
|
iconUrl;
|
|
82
|
-
order;
|
|
83
87
|
parameters;
|
|
84
88
|
releaseFlag;
|
|
85
89
|
proxyPolicy;
|
|
90
|
+
experimentalAttributes;
|
|
91
|
+
setup;
|
|
86
92
|
systemPrompt;
|
|
87
93
|
tools;
|
|
88
|
-
|
|
94
|
+
query;
|
|
89
95
|
constructor(config) {
|
|
90
96
|
this.slug = config.slug;
|
|
91
97
|
this.authType = config.authType;
|
|
92
98
|
this.name = config.name;
|
|
93
99
|
this.description = config.description;
|
|
94
100
|
this.iconUrl = config.iconUrl;
|
|
95
|
-
this.order = config.order;
|
|
96
101
|
this.parameters = config.parameters;
|
|
97
102
|
this.releaseFlag = config.releaseFlag;
|
|
98
103
|
this.proxyPolicy = config.proxyPolicy;
|
|
104
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
105
|
+
this.setup = config.setup;
|
|
99
106
|
this.systemPrompt = config.systemPrompt;
|
|
100
107
|
this.tools = config.tools;
|
|
101
|
-
this.
|
|
108
|
+
this.query = config.query;
|
|
102
109
|
}
|
|
103
110
|
get connectorKey() {
|
|
104
|
-
return
|
|
111
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create tools for connections that belong to this connector.
|
|
115
|
+
* Filters connections by connectorKey internally.
|
|
116
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
117
|
+
*/
|
|
118
|
+
createTools(connections, config) {
|
|
119
|
+
const myConnections = connections.filter(
|
|
120
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
121
|
+
);
|
|
122
|
+
const result = {};
|
|
123
|
+
for (const t of Object.values(this.tools)) {
|
|
124
|
+
result[`${this.connectorKey}_${t.name}`] = t.createTool(
|
|
125
|
+
myConnections,
|
|
126
|
+
config
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
static deriveKey(slug, authType) {
|
|
132
|
+
return authType ? `${slug}-${authType}` : slug;
|
|
105
133
|
}
|
|
106
134
|
};
|
|
107
135
|
|
|
@@ -114,7 +142,126 @@ var AUTH_TYPES = {
|
|
|
114
142
|
PAT: "pat"
|
|
115
143
|
};
|
|
116
144
|
|
|
117
|
-
// src/
|
|
145
|
+
// src/lib/query-utils.ts
|
|
146
|
+
function replaceLiteralParams(sql, namedParams) {
|
|
147
|
+
if (!namedParams) return sql;
|
|
148
|
+
return sql.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
149
|
+
const value = namedParams[name];
|
|
150
|
+
if (value === null || value === void 0) return "NULL";
|
|
151
|
+
if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
|
|
152
|
+
return String(value);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function buildPositionalParams(sql, namedParams) {
|
|
156
|
+
if (!namedParams) return { text: sql, values: [] };
|
|
157
|
+
const values = [];
|
|
158
|
+
const nameToIndex = /* @__PURE__ */ new Map();
|
|
159
|
+
const text = sql.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
160
|
+
if (!nameToIndex.has(name)) {
|
|
161
|
+
values.push(namedParams[name] ?? null);
|
|
162
|
+
nameToIndex.set(name, values.length);
|
|
163
|
+
}
|
|
164
|
+
return `$${nameToIndex.get(name)}`;
|
|
165
|
+
});
|
|
166
|
+
return { text, values };
|
|
167
|
+
}
|
|
168
|
+
function buildQuestionmarkParams(sql, namedParams) {
|
|
169
|
+
if (!namedParams) return { text: sql, values: [] };
|
|
170
|
+
const values = [];
|
|
171
|
+
const text = sql.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
|
|
172
|
+
values.push(namedParams[name] ?? null);
|
|
173
|
+
return "?";
|
|
174
|
+
});
|
|
175
|
+
return { text, values };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/connectors/snowflake/setup.ts
|
|
179
|
+
var snowflakeSetup = new ConnectorSetup({
|
|
180
|
+
ja: `## Snowflake \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
|
|
181
|
+
|
|
182
|
+
\u4EE5\u4E0B\u306E\u624B\u9806\u3067Snowflake\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
183
|
+
|
|
184
|
+
### \u624B\u9806
|
|
185
|
+
|
|
186
|
+
#### \u30B9\u30C6\u30C3\u30D71: \u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u9078\u629E
|
|
187
|
+
1. \`SHOW DATABASES\` \u3092\u5B9F\u884C\u3057\u3066\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
188
|
+
2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
189
|
+
- **\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u9078\u629E\u3055\u305B\u308B
|
|
190
|
+
- **\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
|
|
191
|
+
|
|
192
|
+
#### \u30B9\u30C6\u30C3\u30D72: \u30B9\u30AD\u30FC\u30DE\u9078\u629E\uFF08\u30C6\u30FC\u30D6\u30EB\u7BC4\u56F2\u306E\u6307\u5B9A\u3092\u542B\u3080\uFF09
|
|
193
|
+
3. \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306B\u5BFE\u3057\u3066 \`SHOW SCHEMAS IN DATABASE {database}\` \u3092\u5B9F\u884C\u3057\u3066\u30B9\u30AD\u30FC\u30DE\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\uFF08\u8907\u6570\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u5834\u5408\u306F\u305D\u308C\u305E\u308C\u5B9F\u884C\uFF09
|
|
194
|
+
4. INFORMATION_SCHEMA \u306A\u3069\u5185\u90E8\u30B9\u30AD\u30FC\u30DE\u306F\u9664\u5916\u3059\u308B
|
|
195
|
+
5. \u5404\u30B9\u30AD\u30FC\u30DE\u306B\u3064\u3044\u3066 **2\u3064\u306E\u9078\u629E\u80A2** \u3092\u751F\u6210\u3057\u3001\`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3059\u308B:
|
|
196
|
+
- \`{schema}\uFF08\u5168\u30C6\u30FC\u30D6\u30EB\uFF09\` \u2014 description: "{database}.{schema} \u5185\u306E\u5168\u30C6\u30FC\u30D6\u30EB\u3092\u4F7F\u7528"
|
|
197
|
+
- \`{schema}\uFF08\u7279\u5B9A\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\uFF09\` \u2014 description: "{database}.{schema} \u5185\u306E\u7279\u5B9A\u30C6\u30FC\u30D6\u30EB\u306E\u307F\u4F7F\u7528"
|
|
198
|
+
- \u30B9\u30AD\u30FC\u30DE\u304C1\u3064\u3060\u3051\u306E\u5834\u5408\u3082\u3001\u300C\u5168\u30C6\u30FC\u30D6\u30EB\u300D\u3068\u300C\u7279\u5B9A\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u300D\u306E2\u3064\u306E\u9078\u629E\u80A2\u3092\u63D0\u793A\u3059\u308B\u3053\u3068
|
|
199
|
+
|
|
200
|
+
#### \u30B9\u30C6\u30C3\u30D73: \u30C6\u30FC\u30D6\u30EB\u9078\u629E\uFF08\u300C\u7279\u5B9A\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u300D\u304C\u9078\u3070\u308C\u305F\u30B9\u30AD\u30FC\u30DE\u306E\u307F\uFF09
|
|
201
|
+
6. \u300C\u5168\u30C6\u30FC\u30D6\u30EB\u300D\u304C\u9078\u3070\u308C\u305F\u30B9\u30AD\u30FC\u30DE\u306F\u30C6\u30FC\u30D6\u30EB\u9078\u629E\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3001\u5168\u30C6\u30FC\u30D6\u30EB\u3092\u5BFE\u8C61\u3068\u3059\u308B
|
|
202
|
+
7. \u300C\u7279\u5B9A\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u300D\u304C\u9078\u3070\u308C\u305F\u30B9\u30AD\u30FC\u30DE\u306B\u5BFE\u3057\u3066\u306E\u307F \`SHOW TABLES IN SCHEMA {database}.{schema}\` \u3092\u5B9F\u884C\u3057\u3066\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
203
|
+
8. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
204
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u3055\u305B\u308B\u3002description \u306B\u306F\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u540D.\u30B9\u30AD\u30FC\u30DE\u540D\u3092\u8A18\u8F09\u3059\u308B
|
|
205
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528
|
|
206
|
+
|
|
207
|
+
#### \u30B9\u30C6\u30C3\u30D74: \u4FDD\u5B58
|
|
208
|
+
9. \`updateConnectionContext\` \u3067\u4EE5\u4E0B\u3092\u4FDD\u5B58\u3059\u308B:
|
|
209
|
+
- \`database\`: \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
210
|
+
- \`schema\`: \u9078\u629E\u3055\u308C\u305F\u30B9\u30AD\u30FC\u30DE\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
211
|
+
- \`tables\`: \u9078\u629E\u3055\u308C\u305F\u30C6\u30FC\u30D6\u30EB\u540D\uFF08\u5B8C\u5168\u4FEE\u98FE\u540D database.schema.table\u3001\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\u3002\u300C\u5168\u30C6\u30FC\u30D6\u30EB\u300D\u306E\u5834\u5408\u306F "{database}.{schema}.*"\uFF09
|
|
212
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
213
|
+
|
|
214
|
+
### \u91CD\u8981\u306A\u5236\u7D04
|
|
215
|
+
- askUserQuestion \u306E options \u306B\u306F\u6700\u4F4E2\u4EF6\u5FC5\u8981\u30021\u4EF6\u3057\u304B\u306A\u3044\u5834\u5408\u306F askUserQuestion \u3092\u547C\u3070\u305A\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080\u3053\u3068
|
|
216
|
+
- **\u30C6\u30FC\u30D6\u30EB\u306E\u884C\u30C7\u30FC\u30BF\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u30AF\u30A8\u30EA\u306E\u307F\u3002\u305D\u308C\u4EE5\u5916\u306E\u30AF\u30A8\u30EA\u306F\u5B9F\u884C\u7981\u6B62
|
|
217
|
+
|
|
218
|
+
### \u5B9F\u884C\u65B9\u91DD
|
|
219
|
+
- \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
|
|
220
|
+
- \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
|
|
221
|
+
en: `## Snowflake Setup Instructions
|
|
222
|
+
|
|
223
|
+
Follow these steps to set up the Snowflake connection.
|
|
224
|
+
|
|
225
|
+
### Steps
|
|
226
|
+
|
|
227
|
+
#### Step 1: Database Selection
|
|
228
|
+
1. Run \`SHOW DATABASES\` to get the list of databases
|
|
229
|
+
2. Branch based on results:
|
|
230
|
+
- **2 or more databases**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which databases to use
|
|
231
|
+
- **Exactly 1 database**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected database X" in one sentence
|
|
232
|
+
|
|
233
|
+
#### Step 2: Schema Selection (including table scope)
|
|
234
|
+
3. For the selected database(s), run \`SHOW SCHEMAS IN DATABASE {database}\` to get the schema list (run separately for each database if multiple)
|
|
235
|
+
4. Exclude internal schemas such as INFORMATION_SCHEMA
|
|
236
|
+
5. For each schema, generate **two options** and present them via \`askUserQuestion\` (multiSelect: true):
|
|
237
|
+
- \`{schema} (all tables)\` \u2014 description: "Use all tables in {database}.{schema}"
|
|
238
|
+
- \`{schema} (select specific tables)\` \u2014 description: "Use only specific tables in {database}.{schema}"
|
|
239
|
+
- Even if there is only 1 schema, present both "all tables" and "select specific tables" options
|
|
240
|
+
|
|
241
|
+
#### Step 3: Table Selection (only for schemas where "select specific tables" was chosen)
|
|
242
|
+
6. For schemas where "all tables" was chosen, skip table selection and include all tables
|
|
243
|
+
7. For schemas where "select specific tables" was chosen, run \`SHOW TABLES IN SCHEMA {database}.{schema}\` to get the table list
|
|
244
|
+
8. Branch based on results:
|
|
245
|
+
- **2 or more tables**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which tables to use. Include database.schema in the description
|
|
246
|
+
- **Exactly 1 table**: Do NOT call askUserQuestion. Auto-select it
|
|
247
|
+
|
|
248
|
+
#### Step 4: Save
|
|
249
|
+
9. Call \`updateConnectionContext\` to save:
|
|
250
|
+
- \`database\`: Selected database name(s) (comma-separated if multiple)
|
|
251
|
+
- \`schema\`: Selected schema name(s) (comma-separated if multiple)
|
|
252
|
+
- \`tables\`: Selected table names (fully qualified database.schema.table, comma-separated if multiple. Use "{database}.{schema}.*" for "all tables" schemas)
|
|
253
|
+
- \`note\`: Brief description of the setup
|
|
254
|
+
|
|
255
|
+
### Important Constraint
|
|
256
|
+
- askUserQuestion options requires at least 2 items. If there is only 1 item, do NOT call askUserQuestion \u2014 proceed to the next step directly
|
|
257
|
+
- **Do NOT read table row data**. Only the metadata queries specified in the steps above are allowed. All other queries are forbidden
|
|
258
|
+
|
|
259
|
+
### Execution Policy
|
|
260
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool
|
|
261
|
+
- Skip unnecessary explanations and proceed efficiently`
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// src/connectors/snowflake/parameters.ts
|
|
118
265
|
var parameters = {
|
|
119
266
|
account: new ParameterDefinition({
|
|
120
267
|
slug: "account",
|
|
@@ -152,18 +299,18 @@ var parameters = {
|
|
|
152
299
|
secret: false,
|
|
153
300
|
required: true
|
|
154
301
|
}),
|
|
155
|
-
|
|
156
|
-
slug: "
|
|
157
|
-
name: "
|
|
158
|
-
description: "
|
|
159
|
-
envVarBaseKey: "
|
|
160
|
-
type: "
|
|
302
|
+
privateKeyBase64: new ParameterDefinition({
|
|
303
|
+
slug: "private-key-base64",
|
|
304
|
+
name: "Snowflake Private Key",
|
|
305
|
+
description: "The private key for key-pair authentication.",
|
|
306
|
+
envVarBaseKey: "SNOWFLAKE_PRIVATE_KEY_BASE64",
|
|
307
|
+
type: "base64EncodedText",
|
|
161
308
|
secret: true,
|
|
162
309
|
required: true
|
|
163
310
|
})
|
|
164
311
|
};
|
|
165
312
|
|
|
166
|
-
// src/connectors/snowflake
|
|
313
|
+
// src/connectors/snowflake/tools/execute-query.ts
|
|
167
314
|
import { z } from "zod";
|
|
168
315
|
var MAX_ROWS = 500;
|
|
169
316
|
var QUERY_TIMEOUT_MS = 6e4;
|
|
@@ -181,7 +328,7 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
181
328
|
success: z.literal(true),
|
|
182
329
|
rowCount: z.number(),
|
|
183
330
|
truncated: z.boolean(),
|
|
184
|
-
rows: z.array(z.record(z.unknown()))
|
|
331
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
185
332
|
}),
|
|
186
333
|
z.object({
|
|
187
334
|
success: z.literal(false),
|
|
@@ -204,22 +351,26 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
|
204
351
|
};
|
|
205
352
|
}
|
|
206
353
|
console.log(
|
|
207
|
-
`[connector-query] snowflake
|
|
354
|
+
`[connector-query] snowflake/${connection.name}: ${sql}`
|
|
208
355
|
);
|
|
209
356
|
try {
|
|
210
357
|
const snowflake = (await import("snowflake-sdk")).default;
|
|
211
358
|
snowflake.configure({ logLevel: "ERROR" });
|
|
212
|
-
const account = parameters.account.
|
|
213
|
-
const user = parameters.user.
|
|
214
|
-
const role = parameters.role.
|
|
215
|
-
const warehouse = parameters.warehouse.
|
|
216
|
-
const
|
|
359
|
+
const account = parameters.account.getValue(connection);
|
|
360
|
+
const user = parameters.user.getValue(connection);
|
|
361
|
+
const role = parameters.role.getValue(connection);
|
|
362
|
+
const warehouse = parameters.warehouse.getValue(connection);
|
|
363
|
+
const privateKeyBase64 = parameters.privateKeyBase64.getValue(connection);
|
|
364
|
+
const privateKey = Buffer.from(privateKeyBase64, "base64").toString(
|
|
365
|
+
"utf-8"
|
|
366
|
+
);
|
|
217
367
|
const conn = snowflake.createConnection({
|
|
218
368
|
account,
|
|
219
369
|
username: user,
|
|
220
370
|
role,
|
|
221
371
|
warehouse,
|
|
222
|
-
|
|
372
|
+
authenticator: "SNOWFLAKE_JWT",
|
|
373
|
+
privateKey
|
|
223
374
|
});
|
|
224
375
|
await new Promise((resolve, reject) => {
|
|
225
376
|
conn.connect((err) => {
|
|
@@ -268,17 +419,231 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
|
268
419
|
}
|
|
269
420
|
});
|
|
270
421
|
|
|
422
|
+
// src/connectors/snowflake/index.ts
|
|
423
|
+
var tools = { executeQuery: executeQueryTool };
|
|
424
|
+
var snowflakeConnector = new ConnectorPlugin({
|
|
425
|
+
slug: "snowflake",
|
|
426
|
+
authType: null,
|
|
427
|
+
name: "Snowflake",
|
|
428
|
+
description: "Connect to Snowflake for cloud data warehousing and analytics.",
|
|
429
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6oyVtAcP3pMlXaOrts9unk/b7a9dc25d15c388b66e983041b855447/snowflake.svg",
|
|
430
|
+
parameters,
|
|
431
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
432
|
+
setup: snowflakeSetup,
|
|
433
|
+
systemPrompt: `## Snowflake SQL Notes
|
|
434
|
+
- Use fully qualified names DB.SCHEMA.TABLE for table references
|
|
435
|
+
- Schema exploration commands:
|
|
436
|
+
- List databases: \`SHOW DATABASES\`
|
|
437
|
+
- List schemas: \`SHOW SCHEMAS IN DATABASE db_name\`
|
|
438
|
+
- List tables: \`SHOW TABLES IN SCHEMA db_name.schema_name\`
|
|
439
|
+
- List columns: \`DESCRIBE TABLE db_name.schema_name.table_name\`
|
|
440
|
+
- INFORMATION_SCHEMA is also available: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``,
|
|
441
|
+
tools,
|
|
442
|
+
async query(params, sql, namedParams) {
|
|
443
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
444
|
+
const snowflake = (await import("snowflake-sdk")).default;
|
|
445
|
+
snowflake.configure({ logLevel: "ERROR" });
|
|
446
|
+
const privateKey = Buffer.from(
|
|
447
|
+
params[parameters.privateKeyBase64.slug],
|
|
448
|
+
"base64"
|
|
449
|
+
).toString("utf-8");
|
|
450
|
+
const conn = snowflake.createConnection({
|
|
451
|
+
account: params[parameters.account.slug],
|
|
452
|
+
username: params[parameters.user.slug],
|
|
453
|
+
role: params[parameters.role.slug],
|
|
454
|
+
warehouse: params[parameters.warehouse.slug],
|
|
455
|
+
authenticator: "SNOWFLAKE_JWT",
|
|
456
|
+
privateKey
|
|
457
|
+
});
|
|
458
|
+
await new Promise((resolve, reject) => {
|
|
459
|
+
conn.connect((err) => {
|
|
460
|
+
if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
|
|
461
|
+
else resolve();
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
const rows = await new Promise(
|
|
465
|
+
(resolve, reject) => {
|
|
466
|
+
conn.execute({
|
|
467
|
+
sqlText: resolvedSql,
|
|
468
|
+
complete: (err, _stmt, rows2) => {
|
|
469
|
+
if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
|
|
470
|
+
else resolve(rows2 ?? []);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
conn.destroy((err) => {
|
|
476
|
+
if (err) console.warn(`[connector-client] Snowflake destroy error: ${err.message}`);
|
|
477
|
+
});
|
|
478
|
+
return { rows };
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// src/connectors/snowflake-pat/parameters.ts
|
|
483
|
+
var parameters2 = {
|
|
484
|
+
account: new ParameterDefinition({
|
|
485
|
+
slug: "account",
|
|
486
|
+
name: "Snowflake Account",
|
|
487
|
+
description: "The Snowflake account identifier (e.g., xy12345.us-east-1).",
|
|
488
|
+
envVarBaseKey: "SNOWFLAKE_ACCOUNT",
|
|
489
|
+
type: "text",
|
|
490
|
+
secret: false,
|
|
491
|
+
required: true
|
|
492
|
+
}),
|
|
493
|
+
user: new ParameterDefinition({
|
|
494
|
+
slug: "user",
|
|
495
|
+
name: "Snowflake User",
|
|
496
|
+
description: "The username for Snowflake authentication.",
|
|
497
|
+
envVarBaseKey: "SNOWFLAKE_USER",
|
|
498
|
+
type: "text",
|
|
499
|
+
secret: false,
|
|
500
|
+
required: true
|
|
501
|
+
}),
|
|
502
|
+
role: new ParameterDefinition({
|
|
503
|
+
slug: "role",
|
|
504
|
+
name: "Snowflake Role",
|
|
505
|
+
description: "The role to use when connecting to Snowflake.",
|
|
506
|
+
envVarBaseKey: "SNOWFLAKE_ROLE",
|
|
507
|
+
type: "text",
|
|
508
|
+
secret: false,
|
|
509
|
+
required: true
|
|
510
|
+
}),
|
|
511
|
+
warehouse: new ParameterDefinition({
|
|
512
|
+
slug: "warehouse",
|
|
513
|
+
name: "Snowflake Warehouse",
|
|
514
|
+
description: "The warehouse to use for executing queries.",
|
|
515
|
+
envVarBaseKey: "SNOWFLAKE_WAREHOUSE",
|
|
516
|
+
type: "text",
|
|
517
|
+
secret: false,
|
|
518
|
+
required: true
|
|
519
|
+
}),
|
|
520
|
+
pat: new ParameterDefinition({
|
|
521
|
+
slug: "pat",
|
|
522
|
+
name: "Personal Access Token",
|
|
523
|
+
description: "Snowflake Personal Access Token (PAT) for authentication.",
|
|
524
|
+
envVarBaseKey: "SNOWFLAKE_PASSWORD",
|
|
525
|
+
type: "text",
|
|
526
|
+
secret: true,
|
|
527
|
+
required: true
|
|
528
|
+
})
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// src/connectors/snowflake-pat/tools/execute-query.ts
|
|
532
|
+
import { z as z2 } from "zod";
|
|
533
|
+
var MAX_ROWS2 = 500;
|
|
534
|
+
var QUERY_TIMEOUT_MS2 = 6e4;
|
|
535
|
+
var inputSchema2 = z2.object({
|
|
536
|
+
toolUseIntent: z2.string().optional().describe(
|
|
537
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
538
|
+
),
|
|
539
|
+
connectionId: z2.string().describe("ID of the Snowflake connection to use"),
|
|
540
|
+
sql: z2.string().describe(
|
|
541
|
+
"Snowflake SQL query. Use fully qualified names DB.SCHEMA.TABLE for table references."
|
|
542
|
+
)
|
|
543
|
+
});
|
|
544
|
+
var outputSchema2 = z2.discriminatedUnion("success", [
|
|
545
|
+
z2.object({
|
|
546
|
+
success: z2.literal(true),
|
|
547
|
+
rowCount: z2.number(),
|
|
548
|
+
truncated: z2.boolean(),
|
|
549
|
+
rows: z2.array(z2.record(z2.string(), z2.unknown()))
|
|
550
|
+
}),
|
|
551
|
+
z2.object({
|
|
552
|
+
success: z2.literal(false),
|
|
553
|
+
error: z2.string()
|
|
554
|
+
})
|
|
555
|
+
]);
|
|
556
|
+
var executeQueryTool2 = new ConnectorTool({
|
|
557
|
+
name: "executeQuery",
|
|
558
|
+
description: `Execute SQL against Snowflake. Returns up to ${MAX_ROWS2} rows.
|
|
559
|
+
Use for: schema exploration (SHOW DATABASES/SCHEMAS/TABLES, DESCRIBE TABLE, INFORMATION_SCHEMA), data sampling, analytical queries.
|
|
560
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
561
|
+
inputSchema: inputSchema2,
|
|
562
|
+
outputSchema: outputSchema2,
|
|
563
|
+
async execute({ connectionId, sql }, connections) {
|
|
564
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
565
|
+
if (!connection) {
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
error: `Connection ${connectionId} not found`
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
console.log(
|
|
572
|
+
`[connector-query] snowflake-pat/${connection.name}: ${sql}`
|
|
573
|
+
);
|
|
574
|
+
try {
|
|
575
|
+
const snowflake = (await import("snowflake-sdk")).default;
|
|
576
|
+
snowflake.configure({ logLevel: "ERROR" });
|
|
577
|
+
const account = parameters2.account.getValue(connection);
|
|
578
|
+
const user = parameters2.user.getValue(connection);
|
|
579
|
+
const role = parameters2.role.getValue(connection);
|
|
580
|
+
const warehouse = parameters2.warehouse.getValue(connection);
|
|
581
|
+
const password = parameters2.pat.getValue(connection);
|
|
582
|
+
const conn = snowflake.createConnection({
|
|
583
|
+
account,
|
|
584
|
+
username: user,
|
|
585
|
+
role,
|
|
586
|
+
warehouse,
|
|
587
|
+
password
|
|
588
|
+
});
|
|
589
|
+
await new Promise((resolve, reject) => {
|
|
590
|
+
conn.connect((err) => {
|
|
591
|
+
if (err)
|
|
592
|
+
reject(new Error(`Snowflake connect failed: ${err.message}`));
|
|
593
|
+
else resolve();
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
const allRows = await new Promise(
|
|
597
|
+
(resolve, reject) => {
|
|
598
|
+
const timeoutId = setTimeout(() => {
|
|
599
|
+
reject(
|
|
600
|
+
new Error(
|
|
601
|
+
"Snowflake query timeout: query exceeded 60 seconds"
|
|
602
|
+
)
|
|
603
|
+
);
|
|
604
|
+
}, QUERY_TIMEOUT_MS2);
|
|
605
|
+
conn.execute({
|
|
606
|
+
sqlText: sql,
|
|
607
|
+
complete: (err, _stmt, rows) => {
|
|
608
|
+
clearTimeout(timeoutId);
|
|
609
|
+
if (err)
|
|
610
|
+
reject(new Error(`Snowflake query failed: ${err.message}`));
|
|
611
|
+
else resolve(rows ?? []);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
conn.destroy((err) => {
|
|
617
|
+
if (err)
|
|
618
|
+
console.warn(
|
|
619
|
+
`[connector-query] Snowflake destroy error: ${err.message}`
|
|
620
|
+
);
|
|
621
|
+
});
|
|
622
|
+
const truncated = allRows.length > MAX_ROWS2;
|
|
623
|
+
return {
|
|
624
|
+
success: true,
|
|
625
|
+
rowCount: Math.min(allRows.length, MAX_ROWS2),
|
|
626
|
+
truncated,
|
|
627
|
+
rows: allRows.slice(0, MAX_ROWS2)
|
|
628
|
+
};
|
|
629
|
+
} catch (err) {
|
|
630
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
631
|
+
return { success: false, error: msg };
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
271
636
|
// src/connectors/snowflake-pat/index.ts
|
|
272
|
-
var
|
|
637
|
+
var tools2 = { executeQuery: executeQueryTool2 };
|
|
273
638
|
var snowflakePatConnector = new ConnectorPlugin({
|
|
274
639
|
slug: "snowflake",
|
|
275
640
|
authType: AUTH_TYPES.PAT,
|
|
276
641
|
name: "Snowflake (PAT)",
|
|
277
642
|
description: "Connect to Snowflake using a Personal Access Token (PAT).",
|
|
278
643
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6oyVtAcP3pMlXaOrts9unk/b7a9dc25d15c388b66e983041b855447/snowflake.svg",
|
|
279
|
-
|
|
280
|
-
parameters: Object.values(parameters),
|
|
644
|
+
parameters: parameters2,
|
|
281
645
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
646
|
+
setup: snowflakeSetup,
|
|
282
647
|
systemPrompt: `## Snowflake SQL Notes
|
|
283
648
|
- Use fully qualified names DB.SCHEMA.TABLE for table references
|
|
284
649
|
- Schema exploration commands:
|
|
@@ -287,66 +652,2555 @@ var snowflakePatConnector = new ConnectorPlugin({
|
|
|
287
652
|
- List tables: \`SHOW TABLES IN SCHEMA db_name.schema_name\`
|
|
288
653
|
- List columns: \`DESCRIBE TABLE db_name.schema_name.table_name\`
|
|
289
654
|
- INFORMATION_SCHEMA is also available: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``,
|
|
290
|
-
tools,
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
655
|
+
tools: tools2,
|
|
656
|
+
async query(params, sql, namedParams) {
|
|
657
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
658
|
+
const snowflake = (await import("snowflake-sdk")).default;
|
|
659
|
+
snowflake.configure({ logLevel: "ERROR" });
|
|
660
|
+
const conn = snowflake.createConnection({
|
|
661
|
+
account: params[parameters2.account.slug],
|
|
662
|
+
username: params[parameters2.user.slug],
|
|
663
|
+
role: params[parameters2.role.slug],
|
|
664
|
+
warehouse: params[parameters2.warehouse.slug],
|
|
665
|
+
password: params[parameters2.pat.slug]
|
|
666
|
+
});
|
|
667
|
+
await new Promise((resolve, reject) => {
|
|
668
|
+
conn.connect((err) => {
|
|
669
|
+
if (err)
|
|
670
|
+
reject(new Error(`Snowflake connect failed: ${err.message}`));
|
|
671
|
+
else resolve();
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
const rows = await new Promise(
|
|
675
|
+
(resolve, reject) => {
|
|
676
|
+
conn.execute({
|
|
677
|
+
sqlText: resolvedSql,
|
|
678
|
+
complete: (err, _stmt, rows2) => {
|
|
310
679
|
if (err)
|
|
311
|
-
reject(new Error(`Snowflake
|
|
312
|
-
else resolve();
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
const rows = await new Promise(
|
|
316
|
-
(resolve, reject) => {
|
|
317
|
-
conn.execute({
|
|
318
|
-
sqlText: sql,
|
|
319
|
-
complete: (err, _stmt, rows2) => {
|
|
320
|
-
if (err)
|
|
321
|
-
reject(
|
|
322
|
-
new Error(`Snowflake query failed: ${err.message}`)
|
|
323
|
-
);
|
|
324
|
-
else resolve(rows2 ?? []);
|
|
325
|
-
}
|
|
326
|
-
});
|
|
680
|
+
reject(new Error(`Snowflake query failed: ${err.message}`));
|
|
681
|
+
else resolve(rows2 ?? []);
|
|
327
682
|
}
|
|
328
|
-
);
|
|
329
|
-
conn.destroy((err) => {
|
|
330
|
-
if (err)
|
|
331
|
-
console.warn(
|
|
332
|
-
`[connector-client] Snowflake destroy error: ${err.message}`
|
|
333
|
-
);
|
|
334
683
|
});
|
|
335
|
-
return { rows };
|
|
336
684
|
}
|
|
337
|
-
|
|
685
|
+
);
|
|
686
|
+
conn.destroy((err) => {
|
|
687
|
+
if (err)
|
|
688
|
+
console.warn(
|
|
689
|
+
`[connector-client] Snowflake destroy error: ${err.message}`
|
|
690
|
+
);
|
|
691
|
+
});
|
|
692
|
+
return { rows };
|
|
338
693
|
}
|
|
339
694
|
});
|
|
340
695
|
|
|
341
|
-
// src/connectors/
|
|
342
|
-
var
|
|
343
|
-
|
|
344
|
-
|
|
696
|
+
// src/connectors/postgresql/parameters.ts
|
|
697
|
+
var parameters3 = {
|
|
698
|
+
connectionUrl: new ParameterDefinition({
|
|
699
|
+
slug: "connection-url",
|
|
700
|
+
name: "PostgreSQL Connection URL",
|
|
701
|
+
description: "The PostgreSQL connection URL (e.g., postgresql://user:password@host:5432/database).",
|
|
702
|
+
envVarBaseKey: "POSTGRES_URL",
|
|
703
|
+
type: "text",
|
|
704
|
+
secret: true,
|
|
705
|
+
required: true
|
|
706
|
+
})
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// src/connectors/postgresql/tools/execute-query.ts
|
|
710
|
+
import { z as z3 } from "zod";
|
|
711
|
+
var MAX_ROWS3 = 500;
|
|
712
|
+
var CONNECT_TIMEOUT_MS = 1e4;
|
|
713
|
+
var STATEMENT_TIMEOUT_MS = 6e4;
|
|
714
|
+
var inputSchema3 = z3.object({
|
|
715
|
+
toolUseIntent: z3.string().optional().describe(
|
|
716
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
717
|
+
),
|
|
718
|
+
connectionId: z3.string().describe("ID of the PostgreSQL connection to use"),
|
|
719
|
+
sql: z3.string().describe("PostgreSQL SQL query. Always include LIMIT in queries.")
|
|
720
|
+
});
|
|
721
|
+
var outputSchema3 = z3.discriminatedUnion("success", [
|
|
722
|
+
z3.object({
|
|
723
|
+
success: z3.literal(true),
|
|
724
|
+
rowCount: z3.number(),
|
|
725
|
+
truncated: z3.boolean(),
|
|
726
|
+
rows: z3.array(z3.record(z3.string(), z3.unknown()))
|
|
727
|
+
}),
|
|
728
|
+
z3.object({
|
|
729
|
+
success: z3.literal(false),
|
|
730
|
+
error: z3.string()
|
|
731
|
+
})
|
|
732
|
+
]);
|
|
733
|
+
var executeQueryTool3 = new ConnectorTool({
|
|
734
|
+
name: "executeQuery",
|
|
735
|
+
description: `Execute SQL against PostgreSQL. Returns up to ${MAX_ROWS3} rows.
|
|
736
|
+
Use for: schema exploration (information_schema), data sampling, analytical queries.
|
|
737
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
738
|
+
inputSchema: inputSchema3,
|
|
739
|
+
outputSchema: outputSchema3,
|
|
740
|
+
async execute({ connectionId, sql }, connections) {
|
|
741
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
742
|
+
if (!connection) {
|
|
743
|
+
return {
|
|
744
|
+
success: false,
|
|
745
|
+
error: `Connection ${connectionId} not found`
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
console.log(
|
|
749
|
+
`[connector-query] postgresql/${connection.name}: ${sql}`
|
|
750
|
+
);
|
|
751
|
+
let connectionUrl;
|
|
752
|
+
try {
|
|
753
|
+
const { Pool } = await import("pg");
|
|
754
|
+
connectionUrl = parameters3.connectionUrl.getValue(connection);
|
|
755
|
+
const pool = new Pool({
|
|
756
|
+
connectionString: connectionUrl,
|
|
757
|
+
ssl: { rejectUnauthorized: false },
|
|
758
|
+
connectionTimeoutMillis: CONNECT_TIMEOUT_MS,
|
|
759
|
+
statement_timeout: STATEMENT_TIMEOUT_MS
|
|
760
|
+
});
|
|
761
|
+
try {
|
|
762
|
+
const result = await pool.query(sql);
|
|
763
|
+
const rows = result.rows;
|
|
764
|
+
const truncated = rows.length > MAX_ROWS3;
|
|
765
|
+
return {
|
|
766
|
+
success: true,
|
|
767
|
+
rowCount: Math.min(rows.length, MAX_ROWS3),
|
|
768
|
+
truncated,
|
|
769
|
+
rows: rows.slice(0, MAX_ROWS3)
|
|
770
|
+
};
|
|
771
|
+
} finally {
|
|
772
|
+
await pool.end();
|
|
773
|
+
}
|
|
774
|
+
} catch (err) {
|
|
775
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
776
|
+
if (connectionUrl) {
|
|
777
|
+
msg = msg.replaceAll(connectionUrl, "***");
|
|
778
|
+
}
|
|
779
|
+
return { success: false, error: msg };
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// src/connectors/postgresql/index.ts
|
|
785
|
+
var tools3 = { executeQuery: executeQueryTool3 };
|
|
786
|
+
var postgresqlConnector = new ConnectorPlugin({
|
|
787
|
+
slug: "postgresql",
|
|
788
|
+
authType: null,
|
|
789
|
+
name: "PostgreSQL",
|
|
790
|
+
description: "Connect to PostgreSQL databases for relational data storage and querying.",
|
|
791
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/42AHi2uqteUn65MyqdN6V7/a0f68f12af6aac96bbcda5980f43de07/elephant.png",
|
|
792
|
+
parameters: parameters3,
|
|
793
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
794
|
+
systemPrompt: `## PostgreSQL SQL Notes
|
|
795
|
+
- Schema exploration:
|
|
796
|
+
- List tables: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
|
|
797
|
+
- List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
|
|
798
|
+
- Always include LIMIT in queries`,
|
|
799
|
+
tools: tools3,
|
|
800
|
+
async query(params, sql, namedParams) {
|
|
801
|
+
const { Pool } = await import("pg");
|
|
802
|
+
const { text, values } = buildPositionalParams(sql, namedParams);
|
|
803
|
+
const pool = new Pool({
|
|
804
|
+
connectionString: params[parameters3.connectionUrl.slug],
|
|
805
|
+
ssl: { rejectUnauthorized: false },
|
|
806
|
+
connectionTimeoutMillis: 1e4,
|
|
807
|
+
statement_timeout: 6e4
|
|
808
|
+
});
|
|
809
|
+
try {
|
|
810
|
+
const result = await pool.query(text, values);
|
|
811
|
+
return { rows: result.rows };
|
|
812
|
+
} finally {
|
|
813
|
+
await pool.end();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// src/connectors/mysql/parameters.ts
|
|
819
|
+
var parameters4 = {
|
|
820
|
+
connectionUrl: new ParameterDefinition({
|
|
821
|
+
slug: "connection-url",
|
|
822
|
+
name: "MySQL Connection URL",
|
|
823
|
+
description: "The MySQL connection URL (e.g., mysql://user:password@host:3306/database).",
|
|
824
|
+
envVarBaseKey: "MYSQL_URL",
|
|
825
|
+
type: "text",
|
|
826
|
+
secret: true,
|
|
827
|
+
required: true
|
|
828
|
+
})
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/connectors/mysql/tools/execute-query.ts
|
|
832
|
+
import { z as z4 } from "zod";
|
|
833
|
+
var MAX_ROWS4 = 500;
|
|
834
|
+
var CONNECT_TIMEOUT_MS2 = 1e4;
|
|
835
|
+
var QUERY_TIMEOUT_MS3 = 6e4;
|
|
836
|
+
var inputSchema4 = z4.object({
|
|
837
|
+
toolUseIntent: z4.string().optional().describe(
|
|
838
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
839
|
+
),
|
|
840
|
+
connectionId: z4.string().describe("ID of the MySQL connection to use"),
|
|
841
|
+
sql: z4.string().describe("MySQL SQL query. Always include LIMIT in queries.")
|
|
842
|
+
});
|
|
843
|
+
var outputSchema4 = z4.discriminatedUnion("success", [
|
|
844
|
+
z4.object({
|
|
845
|
+
success: z4.literal(true),
|
|
846
|
+
rowCount: z4.number(),
|
|
847
|
+
truncated: z4.boolean(),
|
|
848
|
+
rows: z4.array(z4.record(z4.string(), z4.unknown()))
|
|
849
|
+
}),
|
|
850
|
+
z4.object({
|
|
851
|
+
success: z4.literal(false),
|
|
852
|
+
error: z4.string()
|
|
853
|
+
})
|
|
854
|
+
]);
|
|
855
|
+
var executeQueryTool4 = new ConnectorTool({
|
|
856
|
+
name: "executeQuery",
|
|
857
|
+
description: `Execute SQL against MySQL. Returns up to ${MAX_ROWS4} rows.
|
|
858
|
+
Use for: schema exploration (information_schema), data sampling, analytical queries.
|
|
859
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
860
|
+
inputSchema: inputSchema4,
|
|
861
|
+
outputSchema: outputSchema4,
|
|
862
|
+
async execute({ connectionId, sql }, connections) {
|
|
863
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
864
|
+
if (!connection) {
|
|
865
|
+
return {
|
|
866
|
+
success: false,
|
|
867
|
+
error: `Connection ${connectionId} not found`
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
console.log(
|
|
871
|
+
`[connector-query] mysql/${connection.name}: ${sql}`
|
|
872
|
+
);
|
|
873
|
+
let connectionUrl;
|
|
874
|
+
try {
|
|
875
|
+
const mysql = await import("mysql2/promise");
|
|
876
|
+
connectionUrl = parameters4.connectionUrl.getValue(connection);
|
|
877
|
+
const pool = mysql.createPool({
|
|
878
|
+
uri: connectionUrl,
|
|
879
|
+
connectTimeout: CONNECT_TIMEOUT_MS2
|
|
880
|
+
});
|
|
881
|
+
try {
|
|
882
|
+
const queryPromise = pool.query(sql);
|
|
883
|
+
const timeoutPromise = new Promise(
|
|
884
|
+
(_, reject) => setTimeout(() => reject(new Error("Query timed out after 60 seconds")), QUERY_TIMEOUT_MS3)
|
|
885
|
+
);
|
|
886
|
+
const [rows] = await Promise.race([queryPromise, timeoutPromise]);
|
|
887
|
+
const resultRows = Array.isArray(rows) ? rows : [];
|
|
888
|
+
const truncated = resultRows.length > MAX_ROWS4;
|
|
889
|
+
return {
|
|
890
|
+
success: true,
|
|
891
|
+
rowCount: Math.min(resultRows.length, MAX_ROWS4),
|
|
892
|
+
truncated,
|
|
893
|
+
rows: resultRows.slice(0, MAX_ROWS4)
|
|
894
|
+
};
|
|
895
|
+
} finally {
|
|
896
|
+
await pool.end();
|
|
897
|
+
}
|
|
898
|
+
} catch (err) {
|
|
899
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
900
|
+
if (connectionUrl) {
|
|
901
|
+
msg = msg.replaceAll(connectionUrl, "***");
|
|
902
|
+
}
|
|
903
|
+
return { success: false, error: msg };
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
// src/connectors/mysql/index.ts
|
|
909
|
+
var tools4 = { executeQuery: executeQueryTool4 };
|
|
910
|
+
var mysqlConnector = new ConnectorPlugin({
|
|
911
|
+
slug: "mysql",
|
|
912
|
+
authType: null,
|
|
913
|
+
name: "MySQL",
|
|
914
|
+
description: "Connect to MySQL databases for relational data storage and querying.",
|
|
915
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6ghPFeGgl7uBs5NHH1a4L/512c9433beec5b595caa41f04921c1f9/logo-mysql-170x115.png",
|
|
916
|
+
parameters: parameters4,
|
|
917
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
918
|
+
systemPrompt: `## MySQL SQL Notes
|
|
919
|
+
- Schema exploration:
|
|
920
|
+
- List tables: \`SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()\`
|
|
921
|
+
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xxx'\`
|
|
922
|
+
- Always include LIMIT in queries`,
|
|
923
|
+
tools: tools4,
|
|
924
|
+
async query(params, sql, namedParams) {
|
|
925
|
+
const mysql = await import("mysql2/promise");
|
|
926
|
+
const { text, values } = buildQuestionmarkParams(sql, namedParams);
|
|
927
|
+
const pool = mysql.createPool({
|
|
928
|
+
uri: params[parameters4.connectionUrl.slug],
|
|
929
|
+
connectTimeout: 1e4
|
|
930
|
+
});
|
|
931
|
+
try {
|
|
932
|
+
const queryPromise = pool.query(text, values);
|
|
933
|
+
const timeoutPromise = new Promise(
|
|
934
|
+
(_, reject) => setTimeout(() => reject(new Error("Query timed out after 60 seconds")), 6e4)
|
|
935
|
+
);
|
|
936
|
+
const [rows] = await Promise.race([queryPromise, timeoutPromise]);
|
|
937
|
+
const resultRows = Array.isArray(rows) ? rows : [];
|
|
938
|
+
return { rows: resultRows };
|
|
939
|
+
} finally {
|
|
940
|
+
await pool.end();
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// src/connectors/bigquery/tools/list-projects.ts
|
|
946
|
+
import { z as z5 } from "zod";
|
|
947
|
+
|
|
948
|
+
// src/connectors/bigquery/parameters.ts
|
|
949
|
+
var parameters5 = {
|
|
950
|
+
serviceAccountKeyJsonBase64: new ParameterDefinition({
|
|
951
|
+
slug: "service-account-key-json-base64",
|
|
952
|
+
name: "Google Cloud Service Account JSON",
|
|
953
|
+
description: "The service account JSON key used to authenticate with Google Cloud Platform. Ensure that the service account has the necessary permissions to access the required resources.",
|
|
954
|
+
envVarBaseKey: "BIGQUERY_SERVICE_ACCOUNT_JSON_BASE64",
|
|
955
|
+
type: "base64EncodedJson",
|
|
956
|
+
secret: true,
|
|
957
|
+
required: true
|
|
958
|
+
}),
|
|
959
|
+
projectId: new ParameterDefinition({
|
|
960
|
+
slug: "project-id",
|
|
961
|
+
name: "Google Cloud Project ID",
|
|
962
|
+
description: "The ID of the Google Cloud project where resources will be managed.",
|
|
963
|
+
envVarBaseKey: "BIGQUERY_PROJECT_ID",
|
|
964
|
+
type: "text",
|
|
965
|
+
secret: false,
|
|
966
|
+
required: false
|
|
967
|
+
})
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
// src/connectors/bigquery/tools/list-projects.ts
|
|
971
|
+
var inputSchema5 = z5.object({
|
|
972
|
+
toolUseIntent: z5.string().optional().describe(
|
|
973
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
974
|
+
),
|
|
975
|
+
connectionId: z5.string().describe("ID of the BigQuery connection to use")
|
|
976
|
+
});
|
|
977
|
+
var outputSchema5 = z5.discriminatedUnion("success", [
|
|
978
|
+
z5.object({
|
|
979
|
+
success: z5.literal(true),
|
|
980
|
+
projects: z5.array(
|
|
981
|
+
z5.object({
|
|
982
|
+
projectId: z5.string(),
|
|
983
|
+
friendlyName: z5.string()
|
|
984
|
+
})
|
|
985
|
+
)
|
|
986
|
+
}),
|
|
987
|
+
z5.object({
|
|
988
|
+
success: z5.literal(false),
|
|
989
|
+
error: z5.string()
|
|
990
|
+
})
|
|
991
|
+
]);
|
|
992
|
+
var listProjectsTool = new ConnectorTool({
|
|
993
|
+
name: "listProjects",
|
|
994
|
+
description: `List GCP projects accessible with the service account credentials. Returns project IDs and friendly names.`,
|
|
995
|
+
inputSchema: inputSchema5,
|
|
996
|
+
outputSchema: outputSchema5,
|
|
997
|
+
async execute({ connectionId }, connections) {
|
|
998
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
999
|
+
if (!connection) {
|
|
1000
|
+
return {
|
|
1001
|
+
success: false,
|
|
1002
|
+
error: `Connection ${connectionId} not found`
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
const serviceAccountJsonBase64 = parameters5.serviceAccountKeyJsonBase64.getValue(connection);
|
|
1007
|
+
const credentials = JSON.parse(
|
|
1008
|
+
Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
|
|
1009
|
+
);
|
|
1010
|
+
const { GoogleAuth } = await import("google-auth-library");
|
|
1011
|
+
const auth = new GoogleAuth({
|
|
1012
|
+
credentials,
|
|
1013
|
+
scopes: ["https://www.googleapis.com/auth/bigquery"]
|
|
1014
|
+
});
|
|
1015
|
+
const client = await auth.getClient();
|
|
1016
|
+
const res = await client.request({
|
|
1017
|
+
url: "https://bigquery.googleapis.com/bigquery/v2/projects"
|
|
1018
|
+
});
|
|
1019
|
+
const projects = (res.data.projects ?? []).map((p) => ({
|
|
1020
|
+
projectId: p.projectReference?.projectId ?? "",
|
|
1021
|
+
friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
|
|
1022
|
+
})).filter((p) => p.projectId !== "");
|
|
1023
|
+
return { success: true, projects };
|
|
1024
|
+
} catch (err) {
|
|
1025
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1026
|
+
return { success: false, error: msg };
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
// src/connectors/bigquery/setup.ts
|
|
1032
|
+
var listProjectsToolName = `bigquery_${listProjectsTool.name}`;
|
|
1033
|
+
var bigquerySetup = new ConnectorSetup({
|
|
1034
|
+
ja: `## BigQuery \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
|
|
1035
|
+
|
|
1036
|
+
\u4EE5\u4E0B\u306E\u624B\u9806\u3067BigQuery\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
1037
|
+
|
|
1038
|
+
### \u624B\u9806
|
|
1039
|
+
|
|
1040
|
+
#### \u30B9\u30C6\u30C3\u30D71: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
|
|
1041
|
+
1. \`${listProjectsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
1042
|
+
2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1043
|
+
- **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: false\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u629E\u3055\u305B\u308B\u3002\u9078\u629E\u80A2\u306E label \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u306B\u3059\u308B
|
|
1044
|
+
- **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
|
|
1045
|
+
3. \`updateConnectionParameters\` \u3067\u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
|
|
1046
|
+
- \`parameterSlug\`: \`"project-id"\`
|
|
1047
|
+
- \`value\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
|
|
1048
|
+
- \`displayValue\`: \`"\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)"\`
|
|
1049
|
+
4. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
|
|
1050
|
+
|
|
1051
|
+
#### \u30B9\u30C6\u30C3\u30D72: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
|
|
1052
|
+
1. \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` \u3092\u5B9F\u884C\u3057\u3066\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
1053
|
+
2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1054
|
+
- **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u629E\u3055\u305B\u308B
|
|
1055
|
+
- **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
|
|
1056
|
+
|
|
1057
|
+
#### \u30B9\u30C6\u30C3\u30D73: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
|
|
1058
|
+
3. \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306B\u5BFE\u3057\u3066 \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` \u3092\u5B9F\u884C\u3057\u3066\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\uFF08\u8907\u6570\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306E\u5834\u5408\u306F UNION ALL \u3067\u4E00\u62EC\u53D6\u5F97\uFF09
|
|
1059
|
+
4. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1060
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u3055\u305B\u308B\u3002description \u306B\u306F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\u3092\u8A18\u8F09\u3059\u308B
|
|
1061
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528
|
|
1062
|
+
|
|
1063
|
+
#### \u30B9\u30C6\u30C3\u30D74: \u4FDD\u5B58
|
|
1064
|
+
5. \`updateConnectionContext\` \u3067\u4EE5\u4E0B\u3092\u4FDD\u5B58\u3059\u308B:
|
|
1065
|
+
- \`dataset\`: \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
1066
|
+
- \`tables\`: \u9078\u629E\u3055\u308C\u305F\u30C6\u30FC\u30D6\u30EB\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
1067
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
1068
|
+
|
|
1069
|
+
### \u91CD\u8981\u306A\u5236\u7D04
|
|
1070
|
+
- askUserQuestion \u306E options \u306B\u306F\u6700\u4F4E2\u4EF6\u5FC5\u8981\u30021\u4EF6\u3057\u304B\u306A\u3044\u5834\u5408\u306F askUserQuestion \u3092\u547C\u3070\u305A\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080\u3053\u3068
|
|
1071
|
+
- **\u30C6\u30FC\u30D6\u30EB\u306E\u884C\u30C7\u30FC\u30BF\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u30AF\u30A8\u30EA\u306E\u307F\u3002\u305D\u308C\u4EE5\u5916\u306E\u30AF\u30A8\u30EA\u306F\u5B9F\u884C\u7981\u6B62
|
|
1072
|
+
|
|
1073
|
+
### \u5B9F\u884C\u65B9\u91DD
|
|
1074
|
+
- \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
|
|
1075
|
+
- \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
|
|
1076
|
+
en: `## BigQuery Setup Instructions
|
|
1077
|
+
|
|
1078
|
+
Follow these steps to set up the BigQuery connection.
|
|
1079
|
+
|
|
1080
|
+
### Steps
|
|
1081
|
+
|
|
1082
|
+
#### Step 1: Project Selection
|
|
1083
|
+
1. Call \`${listProjectsToolName}\` to get the list of GCP projects accessible with the service account credentials
|
|
1084
|
+
2. Branch based on results:
|
|
1085
|
+
- **2 or more projects**: Present them to the user via \`askUserQuestion\` (multiSelect: false) and let them select a project. Format option labels as \`Project Name (id: project-id)\`
|
|
1086
|
+
- **Exactly 1 project**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected project X" in one sentence
|
|
1087
|
+
3. Call \`updateConnectionParameters\` to save the selected project:
|
|
1088
|
+
- \`parameterSlug\`: \`"project-id"\`
|
|
1089
|
+
- \`value\`: the project ID
|
|
1090
|
+
- \`displayValue\`: \`"Project Name (id: project-id)"\`
|
|
1091
|
+
4. After receiving the continuation message, proceed to Step 2
|
|
1092
|
+
|
|
1093
|
+
#### Step 2: Dataset Selection
|
|
1094
|
+
1. Run \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` to get the list of datasets
|
|
1095
|
+
2. Branch based on results:
|
|
1096
|
+
- **2 or more datasets**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which datasets to use
|
|
1097
|
+
- **Exactly 1 dataset**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected dataset X" in one sentence
|
|
1098
|
+
|
|
1099
|
+
#### Step 3: Table Selection
|
|
1100
|
+
3. For the selected dataset(s), run \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` to get the table list (use UNION ALL for multiple datasets)
|
|
1101
|
+
4. Branch based on results:
|
|
1102
|
+
- **2 or more tables**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which tables to use. Include the dataset name in the description
|
|
1103
|
+
- **Exactly 1 table**: Do NOT call askUserQuestion. Auto-select it
|
|
1104
|
+
|
|
1105
|
+
#### Step 4: Save
|
|
1106
|
+
5. Call \`updateConnectionContext\` to save:
|
|
1107
|
+
- \`dataset\`: Selected dataset name(s) (comma-separated if multiple)
|
|
1108
|
+
- \`tables\`: Selected table name(s) (comma-separated if multiple)
|
|
1109
|
+
- \`note\`: Brief description of the setup
|
|
1110
|
+
|
|
1111
|
+
### Important Constraints
|
|
1112
|
+
- askUserQuestion options requires at least 2 items. If there is only 1 item, do NOT call askUserQuestion \u2014 proceed to the next step directly
|
|
1113
|
+
- **Do NOT read table row data**. Only the metadata queries specified in the steps above are allowed. All other queries are forbidden
|
|
1114
|
+
|
|
1115
|
+
### Execution Policy
|
|
1116
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool
|
|
1117
|
+
- Skip unnecessary explanations and proceed efficiently`
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
// src/connectors/bigquery/tools/execute-query.ts
|
|
1121
|
+
import { z as z6 } from "zod";
|
|
1122
|
+
var MAX_ROWS5 = 500;
|
|
1123
|
+
var inputSchema6 = z6.object({
|
|
1124
|
+
toolUseIntent: z6.string().optional().describe(
|
|
1125
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
1126
|
+
),
|
|
1127
|
+
connectionId: z6.string().describe("ID of the BigQuery connection to use"),
|
|
1128
|
+
sql: z6.string().describe(
|
|
1129
|
+
"BigQuery SQL (GoogleSQL) query. Use backtick-quoted fully qualified names `project.dataset.table` for table references."
|
|
1130
|
+
)
|
|
1131
|
+
});
|
|
1132
|
+
var outputSchema6 = z6.discriminatedUnion("success", [
|
|
1133
|
+
z6.object({
|
|
1134
|
+
success: z6.literal(true),
|
|
1135
|
+
rowCount: z6.number(),
|
|
1136
|
+
truncated: z6.boolean(),
|
|
1137
|
+
rows: z6.array(z6.record(z6.string(), z6.unknown()))
|
|
1138
|
+
}),
|
|
1139
|
+
z6.object({
|
|
1140
|
+
success: z6.literal(false),
|
|
1141
|
+
error: z6.string()
|
|
1142
|
+
})
|
|
1143
|
+
]);
|
|
1144
|
+
var executeQueryTool5 = new ConnectorTool({
|
|
1145
|
+
name: "executeQuery",
|
|
1146
|
+
description: `Execute SQL against BigQuery. Returns up to ${MAX_ROWS5} rows.
|
|
1147
|
+
Use for: schema exploration (INFORMATION_SCHEMA), data sampling, analytical queries.
|
|
1148
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
1149
|
+
inputSchema: inputSchema6,
|
|
1150
|
+
outputSchema: outputSchema6,
|
|
1151
|
+
async execute({ connectionId, sql }, connections) {
|
|
1152
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
1153
|
+
if (!connection) {
|
|
1154
|
+
return {
|
|
1155
|
+
success: false,
|
|
1156
|
+
error: `Connection ${connectionId} not found`
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
console.log(
|
|
1160
|
+
`[connector-query] bigquery/${connection.name}: ${sql}`
|
|
1161
|
+
);
|
|
1162
|
+
try {
|
|
1163
|
+
const { BigQuery } = await import("@google-cloud/bigquery");
|
|
1164
|
+
const projectId = parameters5.projectId.getValue(connection);
|
|
1165
|
+
const serviceAccountJsonBase64 = parameters5.serviceAccountKeyJsonBase64.getValue(connection);
|
|
1166
|
+
const credentials = JSON.parse(
|
|
1167
|
+
Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
|
|
1168
|
+
);
|
|
1169
|
+
const bq = new BigQuery({ projectId, credentials });
|
|
1170
|
+
const [job] = await bq.createQueryJob({ query: sql });
|
|
1171
|
+
const [rows] = await job.getQueryResults();
|
|
1172
|
+
const allRows = rows;
|
|
1173
|
+
const truncated = allRows.length > MAX_ROWS5;
|
|
1174
|
+
return {
|
|
1175
|
+
success: true,
|
|
1176
|
+
rowCount: Math.min(allRows.length, MAX_ROWS5),
|
|
1177
|
+
truncated,
|
|
1178
|
+
rows: allRows.slice(0, MAX_ROWS5)
|
|
1179
|
+
};
|
|
1180
|
+
} catch (err) {
|
|
1181
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1182
|
+
return { success: false, error: msg };
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// src/connectors/bigquery/index.ts
|
|
1188
|
+
var tools5 = { executeQuery: executeQueryTool5, listProjects: listProjectsTool };
|
|
1189
|
+
var bigqueryConnector = new ConnectorPlugin({
|
|
1190
|
+
slug: "bigquery",
|
|
1191
|
+
authType: null,
|
|
1192
|
+
name: "BigQuery",
|
|
1193
|
+
description: "Connect to Google BigQuery for data warehouse and analytics.",
|
|
1194
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6nlehQyOmdbktG5hOYkYMr/6ca559140d5ddc7dadc5eac88858a563/bigquery.svg",
|
|
1195
|
+
parameters: parameters5,
|
|
1196
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
1197
|
+
setup: bigquerySetup,
|
|
1198
|
+
systemPrompt: `## BigQuery SQL Notes
|
|
1199
|
+
- Use backtick-quoted fully qualified names \`project.dataset.table\` for table references
|
|
1200
|
+
- Use INFORMATION_SCHEMA for schema exploration
|
|
1201
|
+
- List datasets: \`SELECT schema_name FROM \\\`project_id\\\`.INFORMATION_SCHEMA.SCHEMATA\`
|
|
1202
|
+
- List tables: \`SELECT table_name FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.TABLES\`
|
|
1203
|
+
- List columns: \`SELECT column_name, data_type FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'xxx'\`
|
|
1204
|
+
- Always specify project_id explicitly in queries`,
|
|
1205
|
+
tools: tools5,
|
|
1206
|
+
async query(params, sql, namedParams) {
|
|
1207
|
+
const { BigQuery } = await import("@google-cloud/bigquery");
|
|
1208
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
1209
|
+
const credentials = JSON.parse(
|
|
1210
|
+
Buffer.from(params[parameters5.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
|
|
1211
|
+
);
|
|
1212
|
+
const bq = new BigQuery({
|
|
1213
|
+
projectId: params[parameters5.projectId.slug],
|
|
1214
|
+
credentials
|
|
1215
|
+
});
|
|
1216
|
+
const [job] = await bq.createQueryJob({ query: resolvedSql });
|
|
1217
|
+
const [rows] = await job.getQueryResults();
|
|
1218
|
+
return { rows };
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// src/connectors/bigquery-oauth/tools/list-projects.ts
|
|
1223
|
+
import { z as z7 } from "zod";
|
|
1224
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
1225
|
+
var cachedToken = null;
|
|
1226
|
+
async function getProxyToken(config) {
|
|
1227
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
1228
|
+
return cachedToken.token;
|
|
1229
|
+
}
|
|
1230
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
1231
|
+
const res = await fetch(url, {
|
|
1232
|
+
method: "POST",
|
|
1233
|
+
headers: {
|
|
1234
|
+
"Content-Type": "application/json",
|
|
1235
|
+
"x-api-key": config.appApiKey,
|
|
1236
|
+
"project-id": config.projectId
|
|
1237
|
+
},
|
|
1238
|
+
body: JSON.stringify({
|
|
1239
|
+
sandboxId: config.sandboxId,
|
|
1240
|
+
issuedBy: "coding-agent"
|
|
1241
|
+
})
|
|
1242
|
+
});
|
|
1243
|
+
if (!res.ok) {
|
|
1244
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
1245
|
+
throw new Error(`Failed to get proxy token: HTTP ${res.status} ${errorText}`);
|
|
1246
|
+
}
|
|
1247
|
+
const data = await res.json();
|
|
1248
|
+
cachedToken = {
|
|
1249
|
+
token: data.token,
|
|
1250
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
1251
|
+
};
|
|
1252
|
+
return data.token;
|
|
1253
|
+
}
|
|
1254
|
+
var inputSchema7 = z7.object({
|
|
1255
|
+
toolUseIntent: z7.string().optional().describe(
|
|
1256
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
1257
|
+
),
|
|
1258
|
+
connectionId: z7.string().describe("ID of the BigQuery OAuth connection to use")
|
|
1259
|
+
});
|
|
1260
|
+
var outputSchema7 = z7.discriminatedUnion("success", [
|
|
1261
|
+
z7.object({
|
|
1262
|
+
success: z7.literal(true),
|
|
1263
|
+
projects: z7.array(
|
|
1264
|
+
z7.object({
|
|
1265
|
+
projectId: z7.string(),
|
|
1266
|
+
friendlyName: z7.string()
|
|
1267
|
+
})
|
|
1268
|
+
)
|
|
1269
|
+
}),
|
|
1270
|
+
z7.object({
|
|
1271
|
+
success: z7.literal(false),
|
|
1272
|
+
error: z7.string()
|
|
1273
|
+
})
|
|
1274
|
+
]);
|
|
1275
|
+
var listProjectsTool2 = new ConnectorTool({
|
|
1276
|
+
name: "listProjects",
|
|
1277
|
+
description: `List GCP projects accessible with the current OAuth credentials. Returns project IDs and friendly names.`,
|
|
1278
|
+
inputSchema: inputSchema7,
|
|
1279
|
+
outputSchema: outputSchema7,
|
|
1280
|
+
async execute({ connectionId }, connections, config) {
|
|
1281
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
1282
|
+
if (!connection) {
|
|
1283
|
+
return {
|
|
1284
|
+
success: false,
|
|
1285
|
+
error: `Connection ${connectionId} not found`
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
console.log(
|
|
1289
|
+
`[connector-query] bigquery-oauth/${connection.name}: listProjects`
|
|
1290
|
+
);
|
|
1291
|
+
try {
|
|
1292
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
1293
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1294
|
+
const controller = new AbortController();
|
|
1295
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
1296
|
+
try {
|
|
1297
|
+
const response = await fetch(proxyUrl, {
|
|
1298
|
+
method: "POST",
|
|
1299
|
+
headers: {
|
|
1300
|
+
"Content-Type": "application/json",
|
|
1301
|
+
Authorization: `Bearer ${token}`
|
|
1302
|
+
},
|
|
1303
|
+
body: JSON.stringify({
|
|
1304
|
+
url: "https://bigquery.googleapis.com/bigquery/v2/projects",
|
|
1305
|
+
method: "GET"
|
|
1306
|
+
}),
|
|
1307
|
+
signal: controller.signal
|
|
1308
|
+
});
|
|
1309
|
+
const data = await response.json();
|
|
1310
|
+
if (!response.ok) {
|
|
1311
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
1312
|
+
return { success: false, error: errorMessage };
|
|
1313
|
+
}
|
|
1314
|
+
const projects = (data.projects ?? []).map((p) => ({
|
|
1315
|
+
projectId: p.projectReference?.projectId ?? "",
|
|
1316
|
+
friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
|
|
1317
|
+
})).filter((p) => p.projectId !== "");
|
|
1318
|
+
return { success: true, projects };
|
|
1319
|
+
} finally {
|
|
1320
|
+
clearTimeout(timeout);
|
|
1321
|
+
}
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1324
|
+
return { success: false, error: msg };
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
// src/connectors/bigquery-oauth/setup.ts
|
|
1330
|
+
var listProjectsToolName2 = `bigquery-oauth_${listProjectsTool2.name}`;
|
|
1331
|
+
var bigquerySetup2 = new ConnectorSetup({
|
|
1332
|
+
ja: `## BigQuery \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
|
|
1333
|
+
|
|
1334
|
+
\u4EE5\u4E0B\u306E\u624B\u9806\u3067BigQuery\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
1335
|
+
|
|
1336
|
+
### \u624B\u9806
|
|
1337
|
+
|
|
1338
|
+
#### \u30B9\u30C6\u30C3\u30D71: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
|
|
1339
|
+
1. \`${listProjectsToolName2}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
1340
|
+
2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1341
|
+
- **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: false\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u629E\u3055\u305B\u308B\u3002\u9078\u629E\u80A2\u306E label \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u306B\u3059\u308B
|
|
1342
|
+
- **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
|
|
1343
|
+
3. \`updateConnectionParameters\` \u3067\u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
|
|
1344
|
+
- \`parameterSlug\`: \`"project-id"\`
|
|
1345
|
+
- \`value\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
|
|
1346
|
+
- \`displayValue\`: \`"\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)"\`
|
|
1347
|
+
4. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
|
|
1348
|
+
|
|
1349
|
+
#### \u30B9\u30C6\u30C3\u30D72: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
|
|
1350
|
+
1. \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` \u3092\u5B9F\u884C\u3057\u3066\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
1351
|
+
2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1352
|
+
- **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u629E\u3055\u305B\u308B
|
|
1353
|
+
- **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
|
|
1354
|
+
|
|
1355
|
+
#### \u30B9\u30C6\u30C3\u30D73: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
|
|
1356
|
+
3. \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306B\u5BFE\u3057\u3066 \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` \u3092\u5B9F\u884C\u3057\u3066\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\uFF08\u8907\u6570\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306E\u5834\u5408\u306F UNION ALL \u3067\u4E00\u62EC\u53D6\u5F97\uFF09
|
|
1357
|
+
4. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
1358
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u3055\u305B\u308B\u3002description \u306B\u306F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\u3092\u8A18\u8F09\u3059\u308B
|
|
1359
|
+
- **\u30C6\u30FC\u30D6\u30EB\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528
|
|
1360
|
+
|
|
1361
|
+
#### \u30B9\u30C6\u30C3\u30D74: \u4FDD\u5B58
|
|
1362
|
+
5. \`updateConnectionContext\` \u3067\u4EE5\u4E0B\u3092\u4FDD\u5B58\u3059\u308B:
|
|
1363
|
+
- \`dataset\`: \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
1364
|
+
- \`tables\`: \u9078\u629E\u3055\u308C\u305F\u30C6\u30FC\u30D6\u30EB\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
|
|
1365
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
1366
|
+
|
|
1367
|
+
### \u91CD\u8981\u306A\u5236\u7D04
|
|
1368
|
+
- askUserQuestion \u306E options \u306B\u306F\u6700\u4F4E2\u4EF6\u5FC5\u8981\u30021\u4EF6\u3057\u304B\u306A\u3044\u5834\u5408\u306F askUserQuestion \u3092\u547C\u3070\u305A\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080\u3053\u3068
|
|
1369
|
+
- **\u30C6\u30FC\u30D6\u30EB\u306E\u884C\u30C7\u30FC\u30BF\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u30AF\u30A8\u30EA\u306E\u307F\u3002\u305D\u308C\u4EE5\u5916\u306E\u30AF\u30A8\u30EA\u306F\u5B9F\u884C\u7981\u6B62
|
|
1370
|
+
|
|
1371
|
+
### \u5B9F\u884C\u65B9\u91DD
|
|
1372
|
+
- \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
|
|
1373
|
+
- \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
|
|
1374
|
+
en: `## BigQuery Setup Instructions
|
|
1375
|
+
|
|
1376
|
+
Follow these steps to set up the BigQuery connection.
|
|
1377
|
+
|
|
1378
|
+
### Steps
|
|
1379
|
+
|
|
1380
|
+
#### Step 1: Project Selection
|
|
1381
|
+
1. Call \`${listProjectsToolName2}\` to get the list of GCP projects accessible with the OAuth credentials
|
|
1382
|
+
2. Branch based on results:
|
|
1383
|
+
- **2 or more projects**: Present them to the user via \`askUserQuestion\` (multiSelect: false) and let them select a project. Format option labels as \`Project Name (id: project-id)\`
|
|
1384
|
+
- **Exactly 1 project**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected project X" in one sentence
|
|
1385
|
+
3. Call \`updateConnectionParameters\` to save the selected project:
|
|
1386
|
+
- \`parameterSlug\`: \`"project-id"\`
|
|
1387
|
+
- \`value\`: the project ID
|
|
1388
|
+
- \`displayValue\`: \`"Project Name (id: project-id)"\`
|
|
1389
|
+
4. After receiving the continuation message, proceed to Step 2
|
|
1390
|
+
|
|
1391
|
+
#### Step 2: Dataset Selection
|
|
1392
|
+
1. Run \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` to get the list of datasets
|
|
1393
|
+
2. Branch based on results:
|
|
1394
|
+
- **2 or more datasets**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which datasets to use
|
|
1395
|
+
- **Exactly 1 dataset**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected dataset X" in one sentence
|
|
1396
|
+
|
|
1397
|
+
#### Step 3: Table Selection
|
|
1398
|
+
3. For the selected dataset(s), run \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` to get the table list (use UNION ALL for multiple datasets)
|
|
1399
|
+
4. Branch based on results:
|
|
1400
|
+
- **2 or more tables**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which tables to use. Include the dataset name in the description
|
|
1401
|
+
- **Exactly 1 table**: Do NOT call askUserQuestion. Auto-select it
|
|
1402
|
+
|
|
1403
|
+
#### Step 4: Save
|
|
1404
|
+
5. Call \`updateConnectionContext\` to save:
|
|
1405
|
+
- \`dataset\`: Selected dataset name(s) (comma-separated if multiple)
|
|
1406
|
+
- \`tables\`: Selected table name(s) (comma-separated if multiple)
|
|
1407
|
+
- \`note\`: Brief description of the setup
|
|
1408
|
+
|
|
1409
|
+
### Important Constraints
|
|
1410
|
+
- askUserQuestion options requires at least 2 items. If there is only 1 item, do NOT call askUserQuestion \u2014 proceed to the next step directly
|
|
1411
|
+
- **Do NOT read table row data**. Only the metadata queries specified in the steps above are allowed. All other queries are forbidden
|
|
1412
|
+
|
|
1413
|
+
### Execution Policy
|
|
1414
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool
|
|
1415
|
+
- Skip unnecessary explanations and proceed efficiently`
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
// src/connectors/bigquery-oauth/parameters.ts
|
|
1419
|
+
var parameters6 = {
|
|
1420
|
+
projectId: new ParameterDefinition({
|
|
1421
|
+
slug: "project-id",
|
|
1422
|
+
name: "Google Cloud Project ID",
|
|
1423
|
+
description: "The ID of the Google Cloud project where resources will be managed.",
|
|
1424
|
+
envVarBaseKey: "BIGQUERY_OAUTH_PROJECT_ID",
|
|
1425
|
+
type: "text",
|
|
1426
|
+
secret: false,
|
|
1427
|
+
required: false
|
|
1428
|
+
})
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
// src/connectors/bigquery-oauth/tools/execute-query.ts
|
|
1432
|
+
import { z as z8 } from "zod";
|
|
1433
|
+
var MAX_ROWS6 = 500;
|
|
1434
|
+
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
1435
|
+
var cachedToken2 = null;
|
|
1436
|
+
async function getProxyToken2(config) {
|
|
1437
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
1438
|
+
return cachedToken2.token;
|
|
1439
|
+
}
|
|
1440
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
1441
|
+
const res = await fetch(url, {
|
|
1442
|
+
method: "POST",
|
|
1443
|
+
headers: {
|
|
1444
|
+
"Content-Type": "application/json",
|
|
1445
|
+
"x-api-key": config.appApiKey,
|
|
1446
|
+
"project-id": config.projectId
|
|
1447
|
+
},
|
|
1448
|
+
body: JSON.stringify({
|
|
1449
|
+
sandboxId: config.sandboxId,
|
|
1450
|
+
issuedBy: "coding-agent"
|
|
1451
|
+
})
|
|
1452
|
+
});
|
|
1453
|
+
if (!res.ok) {
|
|
1454
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
1455
|
+
throw new Error(`Failed to get proxy token: HTTP ${res.status} ${errorText}`);
|
|
1456
|
+
}
|
|
1457
|
+
const data = await res.json();
|
|
1458
|
+
cachedToken2 = {
|
|
1459
|
+
token: data.token,
|
|
1460
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
1461
|
+
};
|
|
1462
|
+
return data.token;
|
|
1463
|
+
}
|
|
1464
|
+
var inputSchema8 = z8.object({
|
|
1465
|
+
toolUseIntent: z8.string().optional().describe(
|
|
1466
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
1467
|
+
),
|
|
1468
|
+
connectionId: z8.string().describe("ID of the BigQuery OAuth connection to use"),
|
|
1469
|
+
sql: z8.string().describe(
|
|
1470
|
+
"BigQuery SQL (GoogleSQL) query. Use backtick-quoted fully qualified names `project.dataset.table` for table references."
|
|
1471
|
+
)
|
|
1472
|
+
});
|
|
1473
|
+
var outputSchema8 = z8.discriminatedUnion("success", [
|
|
1474
|
+
z8.object({
|
|
1475
|
+
success: z8.literal(true),
|
|
1476
|
+
rowCount: z8.number(),
|
|
1477
|
+
truncated: z8.boolean(),
|
|
1478
|
+
rows: z8.array(z8.record(z8.string(), z8.unknown()))
|
|
1479
|
+
}),
|
|
1480
|
+
z8.object({
|
|
1481
|
+
success: z8.literal(false),
|
|
1482
|
+
error: z8.string()
|
|
1483
|
+
})
|
|
1484
|
+
]);
|
|
1485
|
+
function parseQueryResponse(data) {
|
|
1486
|
+
const schema = data.schema;
|
|
1487
|
+
const rawRows = data.rows;
|
|
1488
|
+
if (!schema?.fields || !rawRows) return [];
|
|
1489
|
+
const fields = schema.fields;
|
|
1490
|
+
return rawRows.map((row) => {
|
|
1491
|
+
const obj = {};
|
|
1492
|
+
row.f?.forEach((cell, i) => {
|
|
1493
|
+
obj[fields[i].name] = cell.v;
|
|
1494
|
+
});
|
|
1495
|
+
return obj;
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
var executeQueryTool6 = new ConnectorTool({
|
|
1499
|
+
name: "executeQuery",
|
|
1500
|
+
description: `Execute SQL against BigQuery via OAuth. Returns up to ${MAX_ROWS6} rows.
|
|
1501
|
+
Use for: schema exploration (INFORMATION_SCHEMA), data sampling, analytical queries.
|
|
1502
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
1503
|
+
inputSchema: inputSchema8,
|
|
1504
|
+
outputSchema: outputSchema8,
|
|
1505
|
+
async execute({ connectionId, sql }, connections, config) {
|
|
1506
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
1507
|
+
if (!connection) {
|
|
1508
|
+
return {
|
|
1509
|
+
success: false,
|
|
1510
|
+
error: `Connection ${connectionId} not found`
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
const gcpProjectId = parameters6.projectId.getValue(connection);
|
|
1514
|
+
console.log(
|
|
1515
|
+
`[connector-query] bigquery-oauth/${connection.name}: ${sql}`
|
|
1516
|
+
);
|
|
1517
|
+
try {
|
|
1518
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
1519
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
1520
|
+
const queryUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${gcpProjectId}/queries`;
|
|
1521
|
+
const controller = new AbortController();
|
|
1522
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
1523
|
+
try {
|
|
1524
|
+
const response = await fetch(proxyUrl, {
|
|
1525
|
+
method: "POST",
|
|
1526
|
+
headers: {
|
|
1527
|
+
"Content-Type": "application/json",
|
|
1528
|
+
Authorization: `Bearer ${token}`
|
|
1529
|
+
},
|
|
1530
|
+
body: JSON.stringify({
|
|
1531
|
+
url: queryUrl,
|
|
1532
|
+
method: "POST",
|
|
1533
|
+
body: { query: sql, useLegacySql: false, maxResults: MAX_ROWS6 }
|
|
1534
|
+
}),
|
|
1535
|
+
signal: controller.signal
|
|
1536
|
+
});
|
|
1537
|
+
const data = await response.json();
|
|
1538
|
+
if (!response.ok) {
|
|
1539
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
1540
|
+
return { success: false, error: errorMessage };
|
|
1541
|
+
}
|
|
1542
|
+
const rows = parseQueryResponse(data);
|
|
1543
|
+
const truncated = rows.length > MAX_ROWS6;
|
|
1544
|
+
return {
|
|
1545
|
+
success: true,
|
|
1546
|
+
rowCount: Math.min(rows.length, MAX_ROWS6),
|
|
1547
|
+
truncated,
|
|
1548
|
+
rows: rows.slice(0, MAX_ROWS6)
|
|
1549
|
+
};
|
|
1550
|
+
} finally {
|
|
1551
|
+
clearTimeout(timeout);
|
|
1552
|
+
}
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1555
|
+
return { success: false, error: msg };
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
// src/connectors/bigquery-oauth/index.ts
|
|
1561
|
+
var tools6 = { executeQuery: executeQueryTool6, listProjects: listProjectsTool2 };
|
|
1562
|
+
function parseQueryResponse2(data) {
|
|
1563
|
+
const schema = data.schema;
|
|
1564
|
+
const rawRows = data.rows;
|
|
1565
|
+
if (!schema?.fields || !rawRows) return [];
|
|
1566
|
+
return rawRows.map((row) => {
|
|
1567
|
+
const obj = {};
|
|
1568
|
+
row.f?.forEach((cell, i) => {
|
|
1569
|
+
obj[schema.fields[i].name] = cell.v;
|
|
1570
|
+
});
|
|
1571
|
+
return obj;
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
var bigqueryOauthConnector = new ConnectorPlugin({
|
|
1575
|
+
slug: "bigquery",
|
|
1576
|
+
authType: AUTH_TYPES.OAUTH,
|
|
1577
|
+
name: "BigQuery (OAuth)",
|
|
1578
|
+
description: "Connect to Google BigQuery for data warehouse and analytics using OAuth.",
|
|
1579
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6nlehQyOmdbktG5hOYkYMr/6ca559140d5ddc7dadc5eac88858a563/bigquery.svg",
|
|
1580
|
+
parameters: parameters6,
|
|
1581
|
+
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
1582
|
+
setup: bigquerySetup2,
|
|
1583
|
+
proxyPolicy: {
|
|
1584
|
+
allowlist: [
|
|
1585
|
+
{ host: "bigquery.googleapis.com", methods: ["GET", "POST", "PUT", "PATCH", "DELETE"] },
|
|
1586
|
+
{ host: "www.googleapis.com", pathPrefix: "/bigquery/v2", methods: ["GET", "POST"] }
|
|
1587
|
+
]
|
|
1588
|
+
},
|
|
1589
|
+
systemPrompt: `## BigQuery SQL Notes (OAuth)
|
|
1590
|
+
- Use backtick-quoted fully qualified names \`project.dataset.table\` for table references
|
|
1591
|
+
- Use INFORMATION_SCHEMA for schema exploration
|
|
1592
|
+
- List datasets: \`SELECT schema_name FROM \\\`project_id\\\`.INFORMATION_SCHEMA.SCHEMATA\`
|
|
1593
|
+
- List tables: \`SELECT table_name FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.TABLES\`
|
|
1594
|
+
- List columns: \`SELECT column_name, data_type FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'xxx'\`
|
|
1595
|
+
- Always specify project_id explicitly in queries`,
|
|
1596
|
+
tools: tools6,
|
|
1597
|
+
async query(params, sql, namedParams, context) {
|
|
1598
|
+
const { proxyFetch } = context;
|
|
1599
|
+
const projectId = params[parameters6.projectId.slug];
|
|
1600
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
1601
|
+
const url = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries`;
|
|
1602
|
+
const res = await proxyFetch(url, {
|
|
1603
|
+
method: "POST",
|
|
1604
|
+
headers: { "Content-Type": "application/json" },
|
|
1605
|
+
body: JSON.stringify({ query: resolvedSql, useLegacySql: false })
|
|
1606
|
+
});
|
|
1607
|
+
if (!res.ok) {
|
|
1608
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
1609
|
+
throw new Error(`BigQuery query failed: HTTP ${res.status} ${errorText}`);
|
|
1610
|
+
}
|
|
1611
|
+
const data = await res.json();
|
|
1612
|
+
return { rows: parseQueryResponse2(data) };
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
// src/connectors/aws-athena/parameters.ts
|
|
1617
|
+
var parameters7 = {
|
|
1618
|
+
awsAccessKeyId: new ParameterDefinition({
|
|
1619
|
+
slug: "aws-access-key-id",
|
|
1620
|
+
name: "AWS Access Key ID",
|
|
1621
|
+
description: "The AWS Access Key ID for authentication.",
|
|
1622
|
+
envVarBaseKey: "ATHENA_AWS_ACCESS_KEY_ID",
|
|
1623
|
+
type: "text",
|
|
1624
|
+
secret: true,
|
|
1625
|
+
required: true
|
|
1626
|
+
}),
|
|
1627
|
+
awsSecretAccessKey: new ParameterDefinition({
|
|
1628
|
+
slug: "aws-secret-access-key",
|
|
1629
|
+
name: "AWS Secret Access Key",
|
|
1630
|
+
description: "The AWS Secret Access Key for authentication.",
|
|
1631
|
+
envVarBaseKey: "ATHENA_AWS_SECRET_ACCESS_KEY",
|
|
1632
|
+
type: "text",
|
|
1633
|
+
secret: true,
|
|
1634
|
+
required: true
|
|
1635
|
+
}),
|
|
1636
|
+
awsRegion: new ParameterDefinition({
|
|
1637
|
+
slug: "aws-region",
|
|
1638
|
+
name: "AWS Region",
|
|
1639
|
+
description: "The AWS region where your Athena workgroup is located (e.g., us-east-1).",
|
|
1640
|
+
envVarBaseKey: "ATHENA_AWS_REGION",
|
|
1641
|
+
type: "text",
|
|
1642
|
+
secret: false,
|
|
1643
|
+
required: true
|
|
1644
|
+
}),
|
|
1645
|
+
workgroup: new ParameterDefinition({
|
|
1646
|
+
slug: "workgroup",
|
|
1647
|
+
name: "Athena Workgroup",
|
|
1648
|
+
description: "The Athena workgroup to use for queries. Either workgroup or output-location is required.",
|
|
1649
|
+
envVarBaseKey: "ATHENA_WORKGROUP",
|
|
1650
|
+
type: "text",
|
|
1651
|
+
secret: false,
|
|
1652
|
+
required: false
|
|
1653
|
+
}),
|
|
1654
|
+
outputLocation: new ParameterDefinition({
|
|
1655
|
+
slug: "output-location",
|
|
1656
|
+
name: "S3 Output Location",
|
|
1657
|
+
description: "The S3 location for query results (e.g., s3://bucket-name/path/). Either workgroup or output-location is required.",
|
|
1658
|
+
envVarBaseKey: "ATHENA_OUTPUT_LOCATION",
|
|
1659
|
+
type: "text",
|
|
1660
|
+
secret: false,
|
|
1661
|
+
required: false
|
|
1662
|
+
})
|
|
1663
|
+
};
|
|
1664
|
+
|
|
1665
|
+
// src/connectors/aws-athena/tools/execute-query.ts
|
|
1666
|
+
import { z as z9 } from "zod";
|
|
1667
|
+
var MAX_ROWS7 = 500;
|
|
1668
|
+
var POLL_INTERVAL_MS = 1e3;
|
|
1669
|
+
var POLL_TIMEOUT_MS = 12e4;
|
|
1670
|
+
var inputSchema9 = z9.object({
|
|
1671
|
+
toolUseIntent: z9.string().optional().describe(
|
|
1672
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
1673
|
+
),
|
|
1674
|
+
connectionId: z9.string().describe("ID of the AWS Athena connection to use"),
|
|
1675
|
+
sql: z9.string().describe("Athena SQL query (Presto/Trino based)")
|
|
1676
|
+
});
|
|
1677
|
+
var outputSchema9 = z9.discriminatedUnion("success", [
|
|
1678
|
+
z9.object({
|
|
1679
|
+
success: z9.literal(true),
|
|
1680
|
+
rowCount: z9.number(),
|
|
1681
|
+
truncated: z9.boolean(),
|
|
1682
|
+
rows: z9.array(z9.record(z9.string(), z9.unknown()))
|
|
1683
|
+
}),
|
|
1684
|
+
z9.object({
|
|
1685
|
+
success: z9.literal(false),
|
|
1686
|
+
error: z9.string()
|
|
1687
|
+
})
|
|
1688
|
+
]);
|
|
1689
|
+
var executeQueryTool7 = new ConnectorTool({
|
|
1690
|
+
name: "executeQuery",
|
|
1691
|
+
description: `Execute SQL against AWS Athena. Returns up to ${MAX_ROWS7} rows.
|
|
1692
|
+
Use for: schema exploration (SHOW DATABASES/TABLES, DESCRIBE TABLE), data sampling, analytical queries on S3 data.
|
|
1693
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
1694
|
+
inputSchema: inputSchema9,
|
|
1695
|
+
outputSchema: outputSchema9,
|
|
1696
|
+
async execute({ connectionId, sql }, connections) {
|
|
1697
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
1698
|
+
if (!connection) {
|
|
1699
|
+
return {
|
|
1700
|
+
success: false,
|
|
1701
|
+
error: `Connection ${connectionId} not found`
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
console.log(
|
|
1705
|
+
`[connector-query] aws-athena/${connection.name}: ${sql}`
|
|
1706
|
+
);
|
|
1707
|
+
try {
|
|
1708
|
+
const {
|
|
1709
|
+
AthenaClient,
|
|
1710
|
+
StartQueryExecutionCommand,
|
|
1711
|
+
GetQueryExecutionCommand,
|
|
1712
|
+
GetQueryResultsCommand
|
|
1713
|
+
} = await import("@aws-sdk/client-athena");
|
|
1714
|
+
const workgroup = parameters7.workgroup.tryGetValue(connection);
|
|
1715
|
+
const outputLocation = parameters7.outputLocation.tryGetValue(connection);
|
|
1716
|
+
if (!workgroup && !outputLocation) {
|
|
1717
|
+
return {
|
|
1718
|
+
success: false,
|
|
1719
|
+
error: "Either workgroup or output-location is required"
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
const client = new AthenaClient({
|
|
1723
|
+
region: parameters7.awsRegion.getValue(connection),
|
|
1724
|
+
credentials: {
|
|
1725
|
+
accessKeyId: parameters7.awsAccessKeyId.getValue(connection),
|
|
1726
|
+
secretAccessKey: parameters7.awsSecretAccessKey.getValue(connection)
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
const startParams = { QueryString: sql };
|
|
1730
|
+
if (workgroup) startParams.WorkGroup = workgroup;
|
|
1731
|
+
if (outputLocation) {
|
|
1732
|
+
startParams.ResultConfiguration = {
|
|
1733
|
+
OutputLocation: outputLocation
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
const { QueryExecutionId } = await client.send(
|
|
1737
|
+
new StartQueryExecutionCommand(startParams)
|
|
1738
|
+
);
|
|
1739
|
+
const startTime = Date.now();
|
|
1740
|
+
while (true) {
|
|
1741
|
+
const exec = await client.send(
|
|
1742
|
+
new GetQueryExecutionCommand({ QueryExecutionId })
|
|
1743
|
+
);
|
|
1744
|
+
const state = exec.QueryExecution?.Status?.State;
|
|
1745
|
+
if (state === "SUCCEEDED") break;
|
|
1746
|
+
if (state === "FAILED" || state === "CANCELLED") {
|
|
1747
|
+
throw new Error(
|
|
1748
|
+
exec.QueryExecution?.Status?.StateChangeReason || `Query ${state}`
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
if (Date.now() - startTime > POLL_TIMEOUT_MS) {
|
|
1752
|
+
throw new Error("Query timed out after 120 seconds");
|
|
1753
|
+
}
|
|
1754
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
1755
|
+
}
|
|
1756
|
+
const result = await client.send(
|
|
1757
|
+
new GetQueryResultsCommand({ QueryExecutionId })
|
|
1758
|
+
);
|
|
1759
|
+
const resultRows = result.ResultSet?.Rows ?? [];
|
|
1760
|
+
const headers = resultRows[0]?.Data?.map((d) => d.VarCharValue ?? "") ?? [];
|
|
1761
|
+
const dataRows = resultRows.slice(1).map((row) => {
|
|
1762
|
+
const obj = {};
|
|
1763
|
+
row.Data?.forEach((d, i) => {
|
|
1764
|
+
obj[headers[i]] = d.VarCharValue ?? null;
|
|
1765
|
+
});
|
|
1766
|
+
return obj;
|
|
1767
|
+
});
|
|
1768
|
+
const truncated = dataRows.length > MAX_ROWS7;
|
|
1769
|
+
return {
|
|
1770
|
+
success: true,
|
|
1771
|
+
rowCount: Math.min(dataRows.length, MAX_ROWS7),
|
|
1772
|
+
truncated,
|
|
1773
|
+
rows: dataRows.slice(0, MAX_ROWS7)
|
|
1774
|
+
};
|
|
1775
|
+
} catch (err) {
|
|
1776
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1777
|
+
return { success: false, error: msg };
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
// src/connectors/aws-athena/index.ts
|
|
1783
|
+
var tools7 = { executeQuery: executeQueryTool7 };
|
|
1784
|
+
var awsAthenaConnector = new ConnectorPlugin({
|
|
1785
|
+
slug: "aws-athena",
|
|
1786
|
+
authType: null,
|
|
1787
|
+
name: "AWS Athena",
|
|
1788
|
+
description: "Connect to AWS Athena for serverless SQL queries on S3 data.",
|
|
1789
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5x0vIHtUHfJJMZUv4RFOYZ/5059bac389f0169542f39cdb4b387d2c/Athena.svg",
|
|
1790
|
+
parameters: parameters7,
|
|
1791
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
1792
|
+
systemPrompt: `## AWS Athena SQL Notes
|
|
1793
|
+
- Uses Presto/Trino based SQL syntax
|
|
1794
|
+
- Schema exploration:
|
|
1795
|
+
- List databases: \`SHOW DATABASES\`
|
|
1796
|
+
- List tables: \`SHOW TABLES\` or \`SHOW TABLES IN database_name\`
|
|
1797
|
+
- List columns: \`DESCRIBE table_name\`
|
|
1798
|
+
- Always include LIMIT in queries
|
|
1799
|
+
- Query execution is asynchronous and results may take time to retrieve`,
|
|
1800
|
+
tools: tools7,
|
|
1801
|
+
async query(params, sql, namedParams) {
|
|
1802
|
+
const {
|
|
1803
|
+
AthenaClient,
|
|
1804
|
+
StartQueryExecutionCommand,
|
|
1805
|
+
GetQueryExecutionCommand,
|
|
1806
|
+
GetQueryResultsCommand
|
|
1807
|
+
} = await import("@aws-sdk/client-athena");
|
|
1808
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
1809
|
+
const workgroup = params[parameters7.workgroup.slug];
|
|
1810
|
+
const outputLocation = params[parameters7.outputLocation.slug];
|
|
1811
|
+
if (!workgroup && !outputLocation) {
|
|
1812
|
+
throw new Error("Either workgroup or output-location is required");
|
|
1813
|
+
}
|
|
1814
|
+
const client = new AthenaClient({
|
|
1815
|
+
region: params[parameters7.awsRegion.slug],
|
|
1816
|
+
credentials: {
|
|
1817
|
+
accessKeyId: params[parameters7.awsAccessKeyId.slug],
|
|
1818
|
+
secretAccessKey: params[parameters7.awsSecretAccessKey.slug]
|
|
1819
|
+
}
|
|
1820
|
+
});
|
|
1821
|
+
const startParams = { QueryString: resolvedSql };
|
|
1822
|
+
if (workgroup) startParams.WorkGroup = workgroup;
|
|
1823
|
+
if (outputLocation) {
|
|
1824
|
+
startParams.ResultConfiguration = { OutputLocation: outputLocation };
|
|
1825
|
+
}
|
|
1826
|
+
const { QueryExecutionId } = await client.send(
|
|
1827
|
+
new StartQueryExecutionCommand(startParams)
|
|
1828
|
+
);
|
|
1829
|
+
const startTime = Date.now();
|
|
1830
|
+
while (true) {
|
|
1831
|
+
const exec = await client.send(
|
|
1832
|
+
new GetQueryExecutionCommand({ QueryExecutionId })
|
|
1833
|
+
);
|
|
1834
|
+
const state = exec.QueryExecution?.Status?.State;
|
|
1835
|
+
if (state === "SUCCEEDED") break;
|
|
1836
|
+
if (state === "FAILED" || state === "CANCELLED") {
|
|
1837
|
+
throw new Error(
|
|
1838
|
+
exec.QueryExecution?.Status?.StateChangeReason || `Query ${state}`
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
if (Date.now() - startTime > 12e4) {
|
|
1842
|
+
throw new Error("Query timed out after 120 seconds");
|
|
1843
|
+
}
|
|
1844
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
1845
|
+
}
|
|
1846
|
+
const result = await client.send(
|
|
1847
|
+
new GetQueryResultsCommand({ QueryExecutionId })
|
|
1848
|
+
);
|
|
1849
|
+
const resultRows = result.ResultSet?.Rows ?? [];
|
|
1850
|
+
const headers = resultRows[0]?.Data?.map((d) => d.VarCharValue ?? "") ?? [];
|
|
1851
|
+
const rows = resultRows.slice(1).map((row) => {
|
|
1852
|
+
const obj = {};
|
|
1853
|
+
row.Data?.forEach((d, i) => {
|
|
1854
|
+
obj[headers[i]] = d.VarCharValue ?? null;
|
|
1855
|
+
});
|
|
1856
|
+
return obj;
|
|
1857
|
+
});
|
|
1858
|
+
return { rows };
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
|
|
1862
|
+
// src/connectors/redshift/parameters.ts
|
|
1863
|
+
var parameters8 = {
|
|
1864
|
+
awsAccessKeyId: new ParameterDefinition({
|
|
1865
|
+
slug: "aws-access-key-id",
|
|
1866
|
+
name: "AWS Access Key ID",
|
|
1867
|
+
description: "The AWS Access Key ID for authentication.",
|
|
1868
|
+
envVarBaseKey: "REDSHIFT_AWS_ACCESS_KEY_ID",
|
|
1869
|
+
type: "text",
|
|
1870
|
+
secret: true,
|
|
1871
|
+
required: true
|
|
1872
|
+
}),
|
|
1873
|
+
awsSecretAccessKey: new ParameterDefinition({
|
|
1874
|
+
slug: "aws-secret-access-key",
|
|
1875
|
+
name: "AWS Secret Access Key",
|
|
1876
|
+
description: "The AWS Secret Access Key for authentication.",
|
|
1877
|
+
envVarBaseKey: "REDSHIFT_AWS_SECRET_ACCESS_KEY",
|
|
1878
|
+
type: "text",
|
|
1879
|
+
secret: true,
|
|
1880
|
+
required: true
|
|
1881
|
+
}),
|
|
1882
|
+
awsRegion: new ParameterDefinition({
|
|
1883
|
+
slug: "aws-region",
|
|
1884
|
+
name: "AWS Region",
|
|
1885
|
+
description: "The AWS region where your Redshift cluster is located (e.g., us-east-1).",
|
|
1886
|
+
envVarBaseKey: "REDSHIFT_AWS_REGION",
|
|
1887
|
+
type: "text",
|
|
1888
|
+
secret: false,
|
|
1889
|
+
required: true
|
|
1890
|
+
}),
|
|
1891
|
+
database: new ParameterDefinition({
|
|
1892
|
+
slug: "database",
|
|
1893
|
+
name: "Database Name",
|
|
1894
|
+
description: "The name of the database to connect to.",
|
|
1895
|
+
envVarBaseKey: "REDSHIFT_DATABASE",
|
|
1896
|
+
type: "text",
|
|
1897
|
+
secret: false,
|
|
1898
|
+
required: true
|
|
1899
|
+
}),
|
|
1900
|
+
clusterIdentifier: new ParameterDefinition({
|
|
1901
|
+
slug: "cluster-identifier",
|
|
1902
|
+
name: "Cluster Identifier",
|
|
1903
|
+
description: "The Redshift cluster identifier. Required for Provisioned Cluster (use either cluster-identifier or workgroup-name).",
|
|
1904
|
+
envVarBaseKey: "REDSHIFT_CLUSTER_IDENTIFIER",
|
|
1905
|
+
type: "text",
|
|
1906
|
+
secret: false,
|
|
1907
|
+
required: false
|
|
1908
|
+
}),
|
|
1909
|
+
workgroupName: new ParameterDefinition({
|
|
1910
|
+
slug: "workgroup-name",
|
|
1911
|
+
name: "Workgroup Name",
|
|
1912
|
+
description: "The Redshift Serverless workgroup name. Required for Serverless (use either cluster-identifier or workgroup-name).",
|
|
1913
|
+
envVarBaseKey: "REDSHIFT_WORKGROUP_NAME",
|
|
1914
|
+
type: "text",
|
|
1915
|
+
secret: false,
|
|
1916
|
+
required: false
|
|
1917
|
+
}),
|
|
1918
|
+
secretArn: new ParameterDefinition({
|
|
1919
|
+
slug: "secret-arn",
|
|
1920
|
+
name: "Secrets Manager ARN",
|
|
1921
|
+
description: "The ARN of the secret in AWS Secrets Manager for database credentials. Optional for both Provisioned Cluster and Serverless.",
|
|
1922
|
+
envVarBaseKey: "REDSHIFT_SECRET_ARN",
|
|
1923
|
+
type: "text",
|
|
1924
|
+
secret: true,
|
|
1925
|
+
required: false
|
|
1926
|
+
}),
|
|
1927
|
+
dbUser: new ParameterDefinition({
|
|
1928
|
+
slug: "db-user",
|
|
1929
|
+
name: "Database User",
|
|
1930
|
+
description: "The database user for IAM authentication. Required for Provisioned Cluster if secret-arn is not set.",
|
|
1931
|
+
envVarBaseKey: "REDSHIFT_DB_USER",
|
|
1932
|
+
type: "text",
|
|
1933
|
+
secret: false,
|
|
1934
|
+
required: false
|
|
1935
|
+
})
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
// src/connectors/redshift/tools/execute-query.ts
|
|
1939
|
+
import { z as z10 } from "zod";
|
|
1940
|
+
var MAX_ROWS8 = 500;
|
|
1941
|
+
var POLL_INTERVAL_MS2 = 1e3;
|
|
1942
|
+
var POLL_TIMEOUT_MS2 = 12e4;
|
|
1943
|
+
var inputSchema10 = z10.object({
|
|
1944
|
+
toolUseIntent: z10.string().optional().describe(
|
|
1945
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
1946
|
+
),
|
|
1947
|
+
connectionId: z10.string().describe("ID of the Redshift connection to use"),
|
|
1948
|
+
sql: z10.string().describe(
|
|
1949
|
+
"SQL query to execute against Amazon Redshift."
|
|
1950
|
+
)
|
|
1951
|
+
});
|
|
1952
|
+
var outputSchema10 = z10.discriminatedUnion("success", [
|
|
1953
|
+
z10.object({
|
|
1954
|
+
success: z10.literal(true),
|
|
1955
|
+
rowCount: z10.number(),
|
|
1956
|
+
truncated: z10.boolean(),
|
|
1957
|
+
rows: z10.array(z10.record(z10.string(), z10.unknown()))
|
|
1958
|
+
}),
|
|
1959
|
+
z10.object({
|
|
1960
|
+
success: z10.literal(false),
|
|
1961
|
+
error: z10.string()
|
|
1962
|
+
})
|
|
1963
|
+
]);
|
|
1964
|
+
var executeQueryTool8 = new ConnectorTool({
|
|
1965
|
+
name: "executeQuery",
|
|
1966
|
+
description: `Execute SQL against Amazon Redshift. Returns up to ${MAX_ROWS8} rows.
|
|
1967
|
+
Use for: schema exploration, data sampling, analytical queries.
|
|
1968
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
1969
|
+
inputSchema: inputSchema10,
|
|
1970
|
+
outputSchema: outputSchema10,
|
|
1971
|
+
async execute({ connectionId, sql }, connections) {
|
|
1972
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
1973
|
+
if (!connection) {
|
|
1974
|
+
return {
|
|
1975
|
+
success: false,
|
|
1976
|
+
error: `Connection ${connectionId} not found`
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
console.log(
|
|
1980
|
+
`[connector-query] redshift/${connection.name}: ${sql}`
|
|
1981
|
+
);
|
|
1982
|
+
try {
|
|
1983
|
+
const {
|
|
1984
|
+
RedshiftDataClient,
|
|
1985
|
+
ExecuteStatementCommand,
|
|
1986
|
+
DescribeStatementCommand,
|
|
1987
|
+
GetStatementResultCommand
|
|
1988
|
+
} = await import("@aws-sdk/client-redshift-data");
|
|
1989
|
+
const awsAccessKeyId = parameters8.awsAccessKeyId.getValue(connection);
|
|
1990
|
+
const awsSecretAccessKey = parameters8.awsSecretAccessKey.getValue(connection);
|
|
1991
|
+
const awsRegion = parameters8.awsRegion.getValue(connection);
|
|
1992
|
+
const database = parameters8.database.getValue(connection);
|
|
1993
|
+
const clusterIdentifier = parameters8.clusterIdentifier.tryGetValue(connection);
|
|
1994
|
+
const workgroupName = parameters8.workgroupName.tryGetValue(connection);
|
|
1995
|
+
const secretArn = parameters8.secretArn.tryGetValue(connection);
|
|
1996
|
+
const dbUser = parameters8.dbUser.tryGetValue(connection);
|
|
1997
|
+
if (!clusterIdentifier && !workgroupName) {
|
|
1998
|
+
return {
|
|
1999
|
+
success: false,
|
|
2000
|
+
error: "Either cluster-identifier or workgroup-name is required"
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
const client = new RedshiftDataClient({
|
|
2004
|
+
region: awsRegion,
|
|
2005
|
+
credentials: {
|
|
2006
|
+
accessKeyId: awsAccessKeyId,
|
|
2007
|
+
secretAccessKey: awsSecretAccessKey
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
2010
|
+
const { Id: statementId } = await client.send(
|
|
2011
|
+
new ExecuteStatementCommand({
|
|
2012
|
+
Database: database,
|
|
2013
|
+
Sql: sql,
|
|
2014
|
+
...clusterIdentifier && { ClusterIdentifier: clusterIdentifier },
|
|
2015
|
+
...workgroupName && { WorkgroupName: workgroupName },
|
|
2016
|
+
...secretArn && { SecretArn: secretArn },
|
|
2017
|
+
...dbUser && { DbUser: dbUser }
|
|
2018
|
+
})
|
|
2019
|
+
);
|
|
2020
|
+
const startTime = Date.now();
|
|
2021
|
+
while (true) {
|
|
2022
|
+
const desc = await client.send(
|
|
2023
|
+
new DescribeStatementCommand({ Id: statementId })
|
|
2024
|
+
);
|
|
2025
|
+
if (desc.Status === "FINISHED") break;
|
|
2026
|
+
if (desc.Status === "FAILED" || desc.Status === "ABORTED") {
|
|
2027
|
+
throw new Error(desc.Error || `Statement ${desc.Status}`);
|
|
2028
|
+
}
|
|
2029
|
+
if (Date.now() - startTime > POLL_TIMEOUT_MS2) {
|
|
2030
|
+
throw new Error(`Query timed out after ${POLL_TIMEOUT_MS2 / 1e3} seconds`);
|
|
2031
|
+
}
|
|
2032
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS2));
|
|
2033
|
+
}
|
|
2034
|
+
const result = await client.send(
|
|
2035
|
+
new GetStatementResultCommand({ Id: statementId })
|
|
2036
|
+
);
|
|
2037
|
+
const columnNames = (result.ColumnMetadata ?? []).map((c) => c.name ?? "");
|
|
2038
|
+
const allRows = (result.Records ?? []).map((record) => {
|
|
2039
|
+
const row = {};
|
|
2040
|
+
record.forEach((field, i) => {
|
|
2041
|
+
row[columnNames[i]] = field.stringValue ?? field.longValue ?? field.doubleValue ?? field.booleanValue ?? (field.isNull ? null : void 0);
|
|
2042
|
+
});
|
|
2043
|
+
return row;
|
|
2044
|
+
});
|
|
2045
|
+
const truncated = allRows.length > MAX_ROWS8;
|
|
2046
|
+
return {
|
|
2047
|
+
success: true,
|
|
2048
|
+
rowCount: Math.min(allRows.length, MAX_ROWS8),
|
|
2049
|
+
truncated,
|
|
2050
|
+
rows: allRows.slice(0, MAX_ROWS8)
|
|
2051
|
+
};
|
|
2052
|
+
} catch (err) {
|
|
2053
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2054
|
+
return { success: false, error: msg };
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
|
|
2059
|
+
// src/connectors/redshift/index.ts
|
|
2060
|
+
var tools8 = { executeQuery: executeQueryTool8 };
|
|
2061
|
+
var redshiftConnector = new ConnectorPlugin({
|
|
2062
|
+
slug: "redshift",
|
|
2063
|
+
authType: null,
|
|
2064
|
+
name: "Redshift",
|
|
2065
|
+
description: "Connect to Amazon Redshift for data warehouse analytics.",
|
|
2066
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/AEwW2psmrnZ7htTVsgA9t/a637e31707c5d760be73ce1d8ec75580/aws-redshift-logo.svg",
|
|
2067
|
+
parameters: parameters8,
|
|
2068
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2069
|
+
systemPrompt: `## Redshift SQL Notes
|
|
2070
|
+
- Uses PostgreSQL based SQL syntax
|
|
2071
|
+
- Schema exploration:
|
|
2072
|
+
- List schemas: \`SELECT DISTINCT schemaname FROM pg_tables\`
|
|
2073
|
+
- List tables: \`SELECT tablename FROM pg_tables WHERE schemaname = 'public'\`
|
|
2074
|
+
- List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
|
|
2075
|
+
- Always include LIMIT in queries
|
|
2076
|
+
- Query execution is asynchronous and results may take time to retrieve`,
|
|
2077
|
+
tools: tools8,
|
|
2078
|
+
async query(params, sql, namedParams) {
|
|
2079
|
+
const {
|
|
2080
|
+
RedshiftDataClient,
|
|
2081
|
+
ExecuteStatementCommand,
|
|
2082
|
+
DescribeStatementCommand,
|
|
2083
|
+
GetStatementResultCommand
|
|
2084
|
+
} = await import("@aws-sdk/client-redshift-data");
|
|
2085
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
2086
|
+
const clusterIdentifier = params[parameters8.clusterIdentifier.slug];
|
|
2087
|
+
const workgroupName = params[parameters8.workgroupName.slug];
|
|
2088
|
+
const secretArn = params[parameters8.secretArn.slug];
|
|
2089
|
+
const dbUser = params[parameters8.dbUser.slug];
|
|
2090
|
+
if (!clusterIdentifier && !workgroupName) {
|
|
2091
|
+
throw new Error("Either cluster-identifier or workgroup-name is required");
|
|
2092
|
+
}
|
|
2093
|
+
const client = new RedshiftDataClient({
|
|
2094
|
+
region: params[parameters8.awsRegion.slug],
|
|
2095
|
+
credentials: {
|
|
2096
|
+
accessKeyId: params[parameters8.awsAccessKeyId.slug],
|
|
2097
|
+
secretAccessKey: params[parameters8.awsSecretAccessKey.slug]
|
|
2098
|
+
}
|
|
2099
|
+
});
|
|
2100
|
+
const { Id: statementId } = await client.send(
|
|
2101
|
+
new ExecuteStatementCommand({
|
|
2102
|
+
Database: params[parameters8.database.slug],
|
|
2103
|
+
Sql: resolvedSql,
|
|
2104
|
+
...clusterIdentifier && { ClusterIdentifier: clusterIdentifier },
|
|
2105
|
+
...workgroupName && { WorkgroupName: workgroupName },
|
|
2106
|
+
...secretArn && { SecretArn: secretArn },
|
|
2107
|
+
...dbUser && { DbUser: dbUser }
|
|
2108
|
+
})
|
|
2109
|
+
);
|
|
2110
|
+
const startTime = Date.now();
|
|
2111
|
+
while (true) {
|
|
2112
|
+
const desc = await client.send(
|
|
2113
|
+
new DescribeStatementCommand({ Id: statementId })
|
|
2114
|
+
);
|
|
2115
|
+
if (desc.Status === "FINISHED") break;
|
|
2116
|
+
if (desc.Status === "FAILED" || desc.Status === "ABORTED") {
|
|
2117
|
+
throw new Error(desc.Error || `Statement ${desc.Status}`);
|
|
2118
|
+
}
|
|
2119
|
+
if (Date.now() - startTime > 12e4) {
|
|
2120
|
+
throw new Error("Query timed out after 120 seconds");
|
|
2121
|
+
}
|
|
2122
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
2123
|
+
}
|
|
2124
|
+
const result = await client.send(
|
|
2125
|
+
new GetStatementResultCommand({ Id: statementId })
|
|
2126
|
+
);
|
|
2127
|
+
const columnNames = (result.ColumnMetadata ?? []).map((c) => c.name ?? "");
|
|
2128
|
+
const rows = (result.Records ?? []).map((record) => {
|
|
2129
|
+
const row = {};
|
|
2130
|
+
record.forEach((field, i) => {
|
|
2131
|
+
row[columnNames[i]] = field.stringValue ?? field.longValue ?? field.doubleValue ?? field.booleanValue ?? (field.isNull ? null : void 0);
|
|
2132
|
+
});
|
|
2133
|
+
return row;
|
|
2134
|
+
});
|
|
2135
|
+
return { rows };
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
|
|
2139
|
+
// src/connectors/databricks/parameters.ts
|
|
2140
|
+
var parameters9 = {
|
|
2141
|
+
host: new ParameterDefinition({
|
|
2142
|
+
slug: "host",
|
|
2143
|
+
name: "Databricks Workspace Host",
|
|
2144
|
+
description: "The Databricks workspace host (e.g., your-workspace.cloud.databricks.com).",
|
|
2145
|
+
envVarBaseKey: "DATABRICKS_HOST",
|
|
2146
|
+
type: "text",
|
|
2147
|
+
secret: false,
|
|
2148
|
+
required: true
|
|
2149
|
+
}),
|
|
2150
|
+
token: new ParameterDefinition({
|
|
2151
|
+
slug: "token",
|
|
2152
|
+
name: "Databricks Access Token",
|
|
2153
|
+
description: "The personal access token for Databricks authentication.",
|
|
2154
|
+
envVarBaseKey: "DATABRICKS_TOKEN",
|
|
2155
|
+
type: "text",
|
|
2156
|
+
secret: true,
|
|
2157
|
+
required: true
|
|
2158
|
+
}),
|
|
2159
|
+
httpPath: new ParameterDefinition({
|
|
2160
|
+
slug: "http-path",
|
|
2161
|
+
name: "SQL Warehouse HTTP Path",
|
|
2162
|
+
description: "The HTTP path for the SQL warehouse (e.g., /sql/1.0/warehouses/abc123).",
|
|
2163
|
+
envVarBaseKey: "DATABRICKS_HTTP_PATH",
|
|
2164
|
+
type: "text",
|
|
2165
|
+
secret: false,
|
|
2166
|
+
required: true
|
|
2167
|
+
})
|
|
2168
|
+
};
|
|
2169
|
+
|
|
2170
|
+
// src/connectors/databricks/tools/execute-query.ts
|
|
2171
|
+
import { z as z11 } from "zod";
|
|
2172
|
+
var MAX_ROWS9 = 500;
|
|
2173
|
+
var inputSchema11 = z11.object({
|
|
2174
|
+
toolUseIntent: z11.string().optional().describe(
|
|
2175
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
2176
|
+
),
|
|
2177
|
+
connectionId: z11.string().describe("ID of the Databricks connection to use"),
|
|
2178
|
+
sql: z11.string().describe("Databricks SQL query (Spark SQL based)")
|
|
2179
|
+
});
|
|
2180
|
+
var outputSchema11 = z11.discriminatedUnion("success", [
|
|
2181
|
+
z11.object({
|
|
2182
|
+
success: z11.literal(true),
|
|
2183
|
+
rowCount: z11.number(),
|
|
2184
|
+
truncated: z11.boolean(),
|
|
2185
|
+
rows: z11.array(z11.record(z11.string(), z11.unknown()))
|
|
2186
|
+
}),
|
|
2187
|
+
z11.object({
|
|
2188
|
+
success: z11.literal(false),
|
|
2189
|
+
error: z11.string()
|
|
2190
|
+
})
|
|
2191
|
+
]);
|
|
2192
|
+
var executeQueryTool9 = new ConnectorTool({
|
|
2193
|
+
name: "executeQuery",
|
|
2194
|
+
description: `Execute SQL against Databricks. Returns up to ${MAX_ROWS9} rows.
|
|
2195
|
+
Use for: schema exploration (SHOW CATALOGS/DATABASES/TABLES, DESCRIBE TABLE), data sampling, analytical queries.
|
|
2196
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
2197
|
+
inputSchema: inputSchema11,
|
|
2198
|
+
outputSchema: outputSchema11,
|
|
2199
|
+
async execute({ connectionId, sql }, connections) {
|
|
2200
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2201
|
+
if (!connection) {
|
|
2202
|
+
return {
|
|
2203
|
+
success: false,
|
|
2204
|
+
error: `Connection ${connectionId} not found`
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
console.log(
|
|
2208
|
+
`[connector-query] databricks/${connection.name}: ${sql}`
|
|
2209
|
+
);
|
|
2210
|
+
try {
|
|
2211
|
+
const { DBSQLClient } = await import("@databricks/sql");
|
|
2212
|
+
const host = parameters9.host.getValue(connection);
|
|
2213
|
+
const token = parameters9.token.getValue(connection);
|
|
2214
|
+
const httpPath = parameters9.httpPath.getValue(connection);
|
|
2215
|
+
const client = new DBSQLClient();
|
|
2216
|
+
await client.connect({ host, path: httpPath, token });
|
|
2217
|
+
let session;
|
|
2218
|
+
let operation;
|
|
2219
|
+
try {
|
|
2220
|
+
session = await client.openSession();
|
|
2221
|
+
operation = await session.executeStatement(sql, {
|
|
2222
|
+
runAsync: true
|
|
2223
|
+
});
|
|
2224
|
+
const allRows = await operation.fetchAll();
|
|
2225
|
+
const truncated = allRows.length > MAX_ROWS9;
|
|
2226
|
+
return {
|
|
2227
|
+
success: true,
|
|
2228
|
+
rowCount: Math.min(allRows.length, MAX_ROWS9),
|
|
2229
|
+
truncated,
|
|
2230
|
+
rows: allRows.slice(0, MAX_ROWS9)
|
|
2231
|
+
};
|
|
2232
|
+
} finally {
|
|
2233
|
+
if (operation) await operation.close().catch(() => {
|
|
2234
|
+
});
|
|
2235
|
+
if (session) await session.close().catch(() => {
|
|
2236
|
+
});
|
|
2237
|
+
await client.close().catch(() => {
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
} catch (err) {
|
|
2241
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2242
|
+
return { success: false, error: msg };
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
|
|
2247
|
+
// src/connectors/databricks/index.ts
|
|
2248
|
+
var tools9 = { executeQuery: executeQueryTool9 };
|
|
2249
|
+
var databricksConnector = new ConnectorPlugin({
|
|
2250
|
+
slug: "databricks",
|
|
2251
|
+
authType: null,
|
|
2252
|
+
name: "Databricks",
|
|
2253
|
+
description: "Connect to Databricks for data lakehouse and SQL analytics.",
|
|
2254
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6QgcrfpQOKg18P7DdgKerd/af55bf0d871339049824dd167b97a29f/databricks-icon.svg",
|
|
2255
|
+
parameters: parameters9,
|
|
2256
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2257
|
+
systemPrompt: `## Databricks SQL Notes
|
|
2258
|
+
- Uses Spark SQL / Databricks SQL syntax
|
|
2259
|
+
- Schema exploration:
|
|
2260
|
+
- List catalogs: \`SHOW CATALOGS\`
|
|
2261
|
+
- List databases: \`SHOW DATABASES\` or \`SHOW SCHEMAS\`
|
|
2262
|
+
- List tables: \`SHOW TABLES\` or \`SHOW TABLES IN database_name\`
|
|
2263
|
+
- List columns: \`DESCRIBE TABLE table_name\`
|
|
2264
|
+
- Always include LIMIT in queries`,
|
|
2265
|
+
tools: tools9,
|
|
2266
|
+
async query(params, sql, namedParams) {
|
|
2267
|
+
const { DBSQLClient } = await import("@databricks/sql");
|
|
2268
|
+
const resolvedSql = replaceLiteralParams(sql, namedParams);
|
|
2269
|
+
const client = new DBSQLClient();
|
|
2270
|
+
await client.connect({
|
|
2271
|
+
host: params[parameters9.host.slug],
|
|
2272
|
+
path: params[parameters9.httpPath.slug],
|
|
2273
|
+
token: params[parameters9.token.slug]
|
|
2274
|
+
});
|
|
2275
|
+
let session;
|
|
2276
|
+
let operation;
|
|
2277
|
+
try {
|
|
2278
|
+
session = await client.openSession();
|
|
2279
|
+
operation = await session.executeStatement(resolvedSql, {
|
|
2280
|
+
runAsync: true
|
|
2281
|
+
});
|
|
2282
|
+
const rows = await operation.fetchAll();
|
|
2283
|
+
return { rows };
|
|
2284
|
+
} finally {
|
|
2285
|
+
if (operation) await operation.close().catch(() => {
|
|
2286
|
+
});
|
|
2287
|
+
if (session) await session.close().catch(() => {
|
|
2288
|
+
});
|
|
2289
|
+
await client.close().catch(() => {
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
|
|
2295
|
+
// src/connectors/airtable/parameters.ts
|
|
2296
|
+
var parameters10 = {
|
|
2297
|
+
baseId: new ParameterDefinition({
|
|
2298
|
+
slug: "base-id",
|
|
2299
|
+
name: "Airtable Base ID",
|
|
2300
|
+
description: "The Airtable Base ID (e.g., appXXXXXXXXXXXXXX).",
|
|
2301
|
+
envVarBaseKey: "AIRTABLE_BASE_ID",
|
|
2302
|
+
type: "text",
|
|
2303
|
+
secret: false,
|
|
2304
|
+
required: true
|
|
2305
|
+
}),
|
|
2306
|
+
apiKey: new ParameterDefinition({
|
|
2307
|
+
slug: "api-key",
|
|
2308
|
+
name: "Airtable API Key",
|
|
2309
|
+
description: "The Airtable API key or Personal Access Token for authentication. Required scopes: schema.bases:read, data.records:read.",
|
|
2310
|
+
envVarBaseKey: "AIRTABLE_API_KEY",
|
|
2311
|
+
type: "text",
|
|
2312
|
+
secret: true,
|
|
2313
|
+
required: true
|
|
2314
|
+
})
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2317
|
+
// src/connectors/airtable/tools/request.ts
|
|
2318
|
+
import { z as z12 } from "zod";
|
|
2319
|
+
var BASE_URL = "https://api.airtable.com/v0/";
|
|
2320
|
+
var REQUEST_TIMEOUT_MS3 = 6e4;
|
|
2321
|
+
var inputSchema12 = z12.object({
|
|
2322
|
+
toolUseIntent: z12.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
2323
|
+
connectionId: z12.string().describe("ID of the Airtable connection to use"),
|
|
2324
|
+
method: z12.enum(["GET", "POST", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
2325
|
+
path: z12.string().describe("API path (e.g., '{baseId}/{tableIdOrName}', 'meta/bases/{baseId}/tables'). {baseId} is automatically replaced."),
|
|
2326
|
+
body: z12.record(z12.string(), z12.unknown()).optional().describe("Request body (JSON)")
|
|
2327
|
+
});
|
|
2328
|
+
var outputSchema12 = z12.discriminatedUnion("success", [
|
|
2329
|
+
z12.object({
|
|
2330
|
+
success: z12.literal(true),
|
|
2331
|
+
status: z12.number(),
|
|
2332
|
+
data: z12.record(z12.string(), z12.unknown())
|
|
2333
|
+
}),
|
|
2334
|
+
z12.object({
|
|
2335
|
+
success: z12.literal(false),
|
|
2336
|
+
error: z12.string()
|
|
2337
|
+
})
|
|
2338
|
+
]);
|
|
2339
|
+
var requestTool = new ConnectorTool({
|
|
2340
|
+
name: "request",
|
|
2341
|
+
description: `Send authenticated requests to the Airtable API.
|
|
2342
|
+
Authentication is handled automatically using the API Key.
|
|
2343
|
+
{baseId} in the path is automatically replaced with the connection's base-id.`,
|
|
2344
|
+
inputSchema: inputSchema12,
|
|
2345
|
+
outputSchema: outputSchema12,
|
|
2346
|
+
async execute({ connectionId, method, path, body }, connections) {
|
|
2347
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2348
|
+
if (!connection) {
|
|
2349
|
+
return { success: false, error: `Connection ${connectionId} not found` };
|
|
2350
|
+
}
|
|
2351
|
+
console.log(`[connector-request] airtable/${connection.name}: ${method} ${path}`);
|
|
2352
|
+
try {
|
|
2353
|
+
const apiKey = parameters10.apiKey.getValue(connection);
|
|
2354
|
+
const baseId = parameters10.baseId.getValue(connection);
|
|
2355
|
+
const resolvedPath = path.replace(/\{baseId\}/g, baseId);
|
|
2356
|
+
const url = `${BASE_URL}${resolvedPath}`;
|
|
2357
|
+
const controller = new AbortController();
|
|
2358
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS3);
|
|
2359
|
+
try {
|
|
2360
|
+
const response = await fetch(url, {
|
|
2361
|
+
method,
|
|
2362
|
+
headers: {
|
|
2363
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2364
|
+
"Content-Type": "application/json"
|
|
2365
|
+
},
|
|
2366
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
2367
|
+
signal: controller.signal
|
|
2368
|
+
});
|
|
2369
|
+
const data = await response.json();
|
|
2370
|
+
if (!response.ok) {
|
|
2371
|
+
const errorObj = data?.error;
|
|
2372
|
+
return {
|
|
2373
|
+
success: false,
|
|
2374
|
+
error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
return { success: true, status: response.status, data };
|
|
2378
|
+
} finally {
|
|
2379
|
+
clearTimeout(timeout);
|
|
2380
|
+
}
|
|
2381
|
+
} catch (err) {
|
|
2382
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2383
|
+
return { success: false, error: msg };
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
|
|
2388
|
+
// src/connectors/airtable/index.ts
|
|
2389
|
+
var tools10 = { request: requestTool };
|
|
2390
|
+
var airtableConnector = new ConnectorPlugin({
|
|
2391
|
+
slug: "airtable",
|
|
2392
|
+
authType: null,
|
|
2393
|
+
name: "Airtable",
|
|
2394
|
+
description: "Connect to Airtable for spreadsheet-database hybrid data management.",
|
|
2395
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/19JUphfOZjyjTK6Zg4NGCf/8c56227b088cada52d3a2d9385a3be97/airtable.svg",
|
|
2396
|
+
parameters: parameters10,
|
|
2397
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2398
|
+
systemPrompt: `## Airtable API
|
|
2399
|
+
- Call the Airtable REST API using the authenticated request tool
|
|
2400
|
+
- {baseId} in the path is automatically replaced
|
|
2401
|
+
|
|
2402
|
+
### List Tables (Schema Exploration)
|
|
2403
|
+
- GET meta/bases/{baseId}/tables
|
|
2404
|
+
|
|
2405
|
+
### Get Records
|
|
2406
|
+
- GET {baseId}/{tableIdOrName}
|
|
2407
|
+
- Query parameter examples: ?maxRecords=100&filterByFormula={formula}&sort[0][field]=Name&sort[0][direction]=asc
|
|
2408
|
+
- Filter columns with fields[]: ?fields[]=Name&fields[]=Email
|
|
2409
|
+
|
|
2410
|
+
### Create Record
|
|
2411
|
+
- POST {baseId}/{tableIdOrName}
|
|
2412
|
+
- Body: { "records": [{ "fields": { "Name": "value", ... } }] }
|
|
2413
|
+
|
|
2414
|
+
### Pagination
|
|
2415
|
+
- If the response contains an offset, fetch the next page by appending ?offset={offset} to the next request`,
|
|
2416
|
+
tools: tools10
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2419
|
+
// src/connectors/google-analytics/parameters.ts
|
|
2420
|
+
var parameters11 = {
|
|
2421
|
+
serviceAccountKeyJsonBase64: new ParameterDefinition({
|
|
2422
|
+
slug: "service-account-key-json-base64",
|
|
2423
|
+
name: "Google Cloud Service Account JSON",
|
|
2424
|
+
description: "The service account JSON key used to authenticate with Google Cloud Platform. Ensure that the service account has the necessary permissions to access Google Analytics.",
|
|
2425
|
+
envVarBaseKey: "GA_SERVICE_ACCOUNT_JSON_BASE64",
|
|
2426
|
+
type: "base64EncodedJson",
|
|
2427
|
+
secret: true,
|
|
2428
|
+
required: true
|
|
2429
|
+
}),
|
|
2430
|
+
propertyId: new ParameterDefinition({
|
|
2431
|
+
slug: "property-id",
|
|
2432
|
+
name: "Google Analytics Property ID",
|
|
2433
|
+
description: "The Google Analytics 4 property ID (e.g., 123456789).",
|
|
2434
|
+
envVarBaseKey: "GA_PROPERTY_ID",
|
|
2435
|
+
type: "text",
|
|
2436
|
+
secret: false,
|
|
2437
|
+
required: true
|
|
2438
|
+
})
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
// src/connectors/google-analytics/tools/request.ts
|
|
2442
|
+
import { z as z13 } from "zod";
|
|
2443
|
+
var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
|
|
2444
|
+
var REQUEST_TIMEOUT_MS4 = 6e4;
|
|
2445
|
+
var inputSchema13 = z13.object({
|
|
2446
|
+
toolUseIntent: z13.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
2447
|
+
connectionId: z13.string().describe("ID of the Google Analytics connection to use"),
|
|
2448
|
+
method: z13.enum(["GET", "POST"]).describe("HTTP method"),
|
|
2449
|
+
path: z13.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
|
|
2450
|
+
body: z13.record(z13.string(), z13.unknown()).optional().describe("POST request body (JSON)")
|
|
2451
|
+
});
|
|
2452
|
+
var outputSchema13 = z13.discriminatedUnion("success", [
|
|
2453
|
+
z13.object({
|
|
2454
|
+
success: z13.literal(true),
|
|
2455
|
+
status: z13.number(),
|
|
2456
|
+
data: z13.record(z13.string(), z13.unknown())
|
|
2457
|
+
}),
|
|
2458
|
+
z13.object({
|
|
2459
|
+
success: z13.literal(false),
|
|
2460
|
+
error: z13.string()
|
|
2461
|
+
})
|
|
2462
|
+
]);
|
|
2463
|
+
var requestTool2 = new ConnectorTool({
|
|
2464
|
+
name: "request",
|
|
2465
|
+
description: `Send authenticated requests to the Google Analytics Data API.
|
|
2466
|
+
Authentication is handled automatically using a service account.
|
|
2467
|
+
{propertyId} in the path is automatically replaced with the connection's property-id.`,
|
|
2468
|
+
inputSchema: inputSchema13,
|
|
2469
|
+
outputSchema: outputSchema13,
|
|
2470
|
+
async execute({ connectionId, method, path, body }, connections) {
|
|
2471
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2472
|
+
if (!connection) {
|
|
2473
|
+
return { success: false, error: `Connection ${connectionId} not found` };
|
|
2474
|
+
}
|
|
2475
|
+
console.log(`[connector-request] google-analytics/${connection.name}: ${method} ${path}`);
|
|
2476
|
+
try {
|
|
2477
|
+
const { GoogleAuth } = await import("google-auth-library");
|
|
2478
|
+
const keyJsonBase64 = parameters11.serviceAccountKeyJsonBase64.getValue(connection);
|
|
2479
|
+
const propertyId = parameters11.propertyId.getValue(connection);
|
|
2480
|
+
const credentials = JSON.parse(
|
|
2481
|
+
Buffer.from(keyJsonBase64, "base64").toString("utf-8")
|
|
2482
|
+
);
|
|
2483
|
+
const auth = new GoogleAuth({
|
|
2484
|
+
credentials,
|
|
2485
|
+
scopes: ["https://www.googleapis.com/auth/analytics.readonly"]
|
|
2486
|
+
});
|
|
2487
|
+
const token = await auth.getAccessToken();
|
|
2488
|
+
if (!token) {
|
|
2489
|
+
return { success: false, error: "Failed to obtain access token" };
|
|
2490
|
+
}
|
|
2491
|
+
const resolvedPath = path.replace(/\{propertyId\}/g, propertyId);
|
|
2492
|
+
const url = `${BASE_URL2}${resolvedPath}`;
|
|
2493
|
+
const controller = new AbortController();
|
|
2494
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS4);
|
|
2495
|
+
try {
|
|
2496
|
+
const response = await fetch(url, {
|
|
2497
|
+
method,
|
|
2498
|
+
headers: {
|
|
2499
|
+
Authorization: `Bearer ${token}`,
|
|
2500
|
+
"Content-Type": "application/json"
|
|
2501
|
+
},
|
|
2502
|
+
body: method === "POST" && body ? JSON.stringify(body) : void 0,
|
|
2503
|
+
signal: controller.signal
|
|
2504
|
+
});
|
|
2505
|
+
const data = await response.json();
|
|
2506
|
+
if (!response.ok) {
|
|
2507
|
+
const errorObj = data?.error;
|
|
2508
|
+
return {
|
|
2509
|
+
success: false,
|
|
2510
|
+
error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
return { success: true, status: response.status, data };
|
|
2514
|
+
} finally {
|
|
2515
|
+
clearTimeout(timeout);
|
|
2516
|
+
}
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2519
|
+
return { success: false, error: msg };
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
});
|
|
2523
|
+
|
|
2524
|
+
// src/connectors/google-analytics/index.ts
|
|
2525
|
+
var tools11 = { request: requestTool2 };
|
|
2526
|
+
var googleAnalyticsConnector = new ConnectorPlugin({
|
|
2527
|
+
slug: "google-analytics",
|
|
2528
|
+
authType: null,
|
|
2529
|
+
name: "Google Analytics",
|
|
2530
|
+
description: "Connect to Google Analytics for web analytics and reporting.",
|
|
2531
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
|
|
2532
|
+
parameters: parameters11,
|
|
2533
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2534
|
+
systemPrompt: `## Google Analytics Data API
|
|
2535
|
+
- Call the GA4 Data API using the authenticated request tool
|
|
2536
|
+
- {propertyId} in the path is automatically replaced
|
|
2537
|
+
|
|
2538
|
+
### Get Metadata (Check available dimensions and metrics)
|
|
2539
|
+
- GET properties/{propertyId}/metadata
|
|
2540
|
+
|
|
2541
|
+
### Get Report
|
|
2542
|
+
- POST properties/{propertyId}:runReport
|
|
2543
|
+
- Body example:
|
|
2544
|
+
{
|
|
2545
|
+
"dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
|
|
2546
|
+
"dimensions": [{"name": "date"}],
|
|
2547
|
+
"metrics": [{"name": "activeUsers"}],
|
|
2548
|
+
"limit": 100
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
### Common Dimensions
|
|
2552
|
+
date, country, city, deviceCategory, browser, pagePath, pageTitle,
|
|
2553
|
+
sessionSource, sessionMedium, eventName
|
|
2554
|
+
|
|
2555
|
+
### Common Metrics
|
|
2556
|
+
activeUsers, sessions, screenPageViews, bounceRate,
|
|
2557
|
+
averageSessionDuration, conversions, totalRevenue
|
|
2558
|
+
|
|
2559
|
+
### Date Specification
|
|
2560
|
+
- Absolute: "2024-01-01"
|
|
2561
|
+
- Relative: "today", "yesterday", "7daysAgo", "30daysAgo"`,
|
|
2562
|
+
tools: tools11
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2565
|
+
// src/connectors/kintone/parameters.ts
|
|
2566
|
+
var parameters12 = {
|
|
2567
|
+
baseUrl: new ParameterDefinition({
|
|
2568
|
+
slug: "base-url",
|
|
2569
|
+
name: "kintone Base URL",
|
|
2570
|
+
description: "The base URL of your kintone environment (e.g., https://example.cybozu.com).",
|
|
2571
|
+
envVarBaseKey: "KINTONE_BASE_URL",
|
|
2572
|
+
type: "text",
|
|
2573
|
+
secret: false,
|
|
2574
|
+
required: true
|
|
2575
|
+
}),
|
|
2576
|
+
username: new ParameterDefinition({
|
|
2577
|
+
slug: "username",
|
|
2578
|
+
name: "kintone Username",
|
|
2579
|
+
description: "The username (login name) for kintone authentication.",
|
|
2580
|
+
envVarBaseKey: "KINTONE_USERNAME",
|
|
2581
|
+
type: "text",
|
|
2582
|
+
secret: false,
|
|
2583
|
+
required: true
|
|
2584
|
+
}),
|
|
2585
|
+
password: new ParameterDefinition({
|
|
2586
|
+
slug: "password",
|
|
2587
|
+
name: "kintone Password",
|
|
2588
|
+
description: "The password for kintone authentication.",
|
|
2589
|
+
envVarBaseKey: "KINTONE_PASSWORD",
|
|
2590
|
+
type: "text",
|
|
2591
|
+
secret: true,
|
|
2592
|
+
required: true
|
|
2593
|
+
})
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2596
|
+
// src/connectors/kintone/tools/request.ts
|
|
2597
|
+
import { z as z14 } from "zod";
|
|
2598
|
+
var REQUEST_TIMEOUT_MS5 = 6e4;
|
|
2599
|
+
var inputSchema14 = z14.object({
|
|
2600
|
+
toolUseIntent: z14.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
2601
|
+
connectionId: z14.string().describe("ID of the kintone connection to use"),
|
|
2602
|
+
method: z14.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP method"),
|
|
2603
|
+
path: z14.string().describe("API path (e.g., 'apps.json', 'records.json?app=1&query=...')"),
|
|
2604
|
+
body: z14.record(z14.string(), z14.unknown()).optional().describe("Request body (JSON)")
|
|
2605
|
+
});
|
|
2606
|
+
var outputSchema14 = z14.discriminatedUnion("success", [
|
|
2607
|
+
z14.object({
|
|
2608
|
+
success: z14.literal(true),
|
|
2609
|
+
status: z14.number(),
|
|
2610
|
+
data: z14.record(z14.string(), z14.unknown())
|
|
2611
|
+
}),
|
|
2612
|
+
z14.object({
|
|
2613
|
+
success: z14.literal(false),
|
|
2614
|
+
error: z14.string()
|
|
2615
|
+
})
|
|
2616
|
+
]);
|
|
2617
|
+
var requestTool3 = new ConnectorTool({
|
|
2618
|
+
name: "request",
|
|
2619
|
+
description: `Send authenticated requests to the kintone REST API.
|
|
2620
|
+
Authentication is handled automatically using username and password.`,
|
|
2621
|
+
inputSchema: inputSchema14,
|
|
2622
|
+
outputSchema: outputSchema14,
|
|
2623
|
+
async execute({ connectionId, method, path, body }, connections) {
|
|
2624
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2625
|
+
if (!connection) {
|
|
2626
|
+
return { success: false, error: `Connection ${connectionId} not found` };
|
|
2627
|
+
}
|
|
2628
|
+
console.log(`[connector-request] kintone/${connection.name}: ${method} ${path}`);
|
|
2629
|
+
try {
|
|
2630
|
+
const baseUrl = parameters12.baseUrl.getValue(connection);
|
|
2631
|
+
const username = parameters12.username.getValue(connection);
|
|
2632
|
+
const password = parameters12.password.getValue(connection);
|
|
2633
|
+
const authToken = Buffer.from(`${username}:${password}`).toString("base64");
|
|
2634
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/k/v1/${path}`;
|
|
2635
|
+
const controller = new AbortController();
|
|
2636
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS5);
|
|
2637
|
+
try {
|
|
2638
|
+
const response = await fetch(url, {
|
|
2639
|
+
method,
|
|
2640
|
+
headers: {
|
|
2641
|
+
"X-Cybozu-Authorization": authToken,
|
|
2642
|
+
"Content-Type": "application/json"
|
|
2643
|
+
},
|
|
2644
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
2645
|
+
signal: controller.signal
|
|
2646
|
+
});
|
|
2647
|
+
const data = await response.json();
|
|
2648
|
+
if (!response.ok) {
|
|
2649
|
+
return {
|
|
2650
|
+
success: false,
|
|
2651
|
+
error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
return { success: true, status: response.status, data };
|
|
2655
|
+
} finally {
|
|
2656
|
+
clearTimeout(timeout);
|
|
2657
|
+
}
|
|
2658
|
+
} catch (err) {
|
|
2659
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2660
|
+
return { success: false, error: msg };
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2665
|
+
// src/connectors/kintone/index.ts
|
|
2666
|
+
var tools12 = { request: requestTool3 };
|
|
2667
|
+
var kintoneConnector = new ConnectorPlugin({
|
|
2668
|
+
slug: "kintone",
|
|
2669
|
+
authType: null,
|
|
2670
|
+
name: "kintone",
|
|
2671
|
+
description: "Connect to kintone for business application data retrieval and analytics.",
|
|
2672
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/76nPGMJFZkMFE3UQNo2JFy/e71dc5f5d5cec1306ce0e17aafbfd9f0/kintone.png",
|
|
2673
|
+
parameters: parameters12,
|
|
2674
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2675
|
+
systemPrompt: `## kintone REST API
|
|
2676
|
+
- Call the kintone REST API using the authenticated request tool
|
|
2677
|
+
- The base URL (e.g., https://example.cybozu.com) is automatically resolved
|
|
2678
|
+
|
|
2679
|
+
### List Apps
|
|
2680
|
+
- GET apps.json
|
|
2681
|
+
|
|
2682
|
+
### Get Field Definitions
|
|
2683
|
+
- GET app/form/fields.json?app={appId}
|
|
2684
|
+
|
|
2685
|
+
### Get Records
|
|
2686
|
+
- GET records.json?app={appId}&query={query}
|
|
2687
|
+
- Query example: records.json?app=1&query=updatedTime > "2024-01-01" order by recordNumber asc limit 100
|
|
2688
|
+
|
|
2689
|
+
### Add Record
|
|
2690
|
+
- POST record.json
|
|
2691
|
+
- Body: { "app": 1, "record": { "fieldName": { "value": "value" } } }
|
|
2692
|
+
|
|
2693
|
+
### kintone Query Syntax
|
|
2694
|
+
- Comparison: fieldName = "value", fieldName > 100
|
|
2695
|
+
- Operators: and, or, not
|
|
2696
|
+
- Sort: order by fieldName asc/desc
|
|
2697
|
+
- Limit: limit 100 offset 0
|
|
2698
|
+
- String: like "partial match"`,
|
|
2699
|
+
tools: tools12
|
|
2700
|
+
});
|
|
2701
|
+
|
|
2702
|
+
// src/connectors/wix-store/parameters.ts
|
|
2703
|
+
var parameters13 = {
|
|
2704
|
+
accountId: new ParameterDefinition({
|
|
2705
|
+
slug: "account-id",
|
|
2706
|
+
name: "Account ID",
|
|
2707
|
+
description: "Account ID",
|
|
2708
|
+
envVarBaseKey: "WIX_ACCOUNT_ID",
|
|
2709
|
+
type: "text",
|
|
2710
|
+
secret: false,
|
|
2711
|
+
required: true
|
|
2712
|
+
}),
|
|
2713
|
+
siteId: new ParameterDefinition({
|
|
2714
|
+
slug: "site-id",
|
|
2715
|
+
name: "Site ID",
|
|
2716
|
+
description: "Site ID for Wix Store Project",
|
|
2717
|
+
envVarBaseKey: "WIX_SITE_ID",
|
|
2718
|
+
type: "text",
|
|
2719
|
+
secret: false,
|
|
2720
|
+
required: true
|
|
2721
|
+
}),
|
|
2722
|
+
apiKey: new ParameterDefinition({
|
|
2723
|
+
slug: "api-key",
|
|
2724
|
+
name: "API Key",
|
|
2725
|
+
description: "API Key to access the Wix store project",
|
|
2726
|
+
envVarBaseKey: "WIX_API_KEY",
|
|
2727
|
+
type: "text",
|
|
2728
|
+
secret: true,
|
|
2729
|
+
required: true
|
|
2730
|
+
})
|
|
2731
|
+
};
|
|
2732
|
+
|
|
2733
|
+
// src/connectors/wix-store/tools/request.ts
|
|
2734
|
+
import { z as z15 } from "zod";
|
|
2735
|
+
var BASE_URL3 = "https://www.wixapis.com/";
|
|
2736
|
+
var REQUEST_TIMEOUT_MS6 = 6e4;
|
|
2737
|
+
var inputSchema15 = z15.object({
|
|
2738
|
+
toolUseIntent: z15.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
2739
|
+
connectionId: z15.string().describe("ID of the Wix Store connection to use"),
|
|
2740
|
+
method: z15.enum(["GET", "POST"]).describe("HTTP method"),
|
|
2741
|
+
path: z15.string().describe("API path (e.g., 'stores/v1/products/query', 'stores/v2/orders/query')"),
|
|
2742
|
+
body: z15.record(z15.string(), z15.unknown()).optional().describe("Request body (JSON)")
|
|
2743
|
+
});
|
|
2744
|
+
var outputSchema15 = z15.discriminatedUnion("success", [
|
|
2745
|
+
z15.object({
|
|
2746
|
+
success: z15.literal(true),
|
|
2747
|
+
status: z15.number(),
|
|
2748
|
+
data: z15.record(z15.string(), z15.unknown())
|
|
2749
|
+
}),
|
|
2750
|
+
z15.object({
|
|
2751
|
+
success: z15.literal(false),
|
|
2752
|
+
error: z15.string()
|
|
2753
|
+
})
|
|
2754
|
+
]);
|
|
2755
|
+
var requestTool4 = new ConnectorTool({
|
|
2756
|
+
name: "request",
|
|
2757
|
+
description: `Send authenticated requests to the Wix Store API.
|
|
2758
|
+
Authentication is handled automatically using the API Key and Site ID.`,
|
|
2759
|
+
inputSchema: inputSchema15,
|
|
2760
|
+
outputSchema: outputSchema15,
|
|
2761
|
+
async execute({ connectionId, method, path, body }, connections) {
|
|
2762
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2763
|
+
if (!connection) {
|
|
2764
|
+
return { success: false, error: `Connection ${connectionId} not found` };
|
|
2765
|
+
}
|
|
2766
|
+
console.log(`[connector-request] wix-store/${connection.name}: ${method} ${path}`);
|
|
2767
|
+
try {
|
|
2768
|
+
const apiKey = parameters13.apiKey.getValue(connection);
|
|
2769
|
+
const siteId = parameters13.siteId.getValue(connection);
|
|
2770
|
+
const url = `${BASE_URL3}${path}`;
|
|
2771
|
+
const controller = new AbortController();
|
|
2772
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS6);
|
|
2773
|
+
try {
|
|
2774
|
+
const response = await fetch(url, {
|
|
2775
|
+
method,
|
|
2776
|
+
headers: {
|
|
2777
|
+
Authorization: apiKey,
|
|
2778
|
+
"wix-site-id": siteId,
|
|
2779
|
+
"Content-Type": "application/json"
|
|
2780
|
+
},
|
|
2781
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
2782
|
+
signal: controller.signal
|
|
2783
|
+
});
|
|
2784
|
+
const data = await response.json();
|
|
2785
|
+
if (!response.ok) {
|
|
2786
|
+
const errorObj = data?.message;
|
|
2787
|
+
return {
|
|
2788
|
+
success: false,
|
|
2789
|
+
error: errorObj ?? `HTTP ${response.status} ${response.statusText}`
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
return { success: true, status: response.status, data };
|
|
2793
|
+
} finally {
|
|
2794
|
+
clearTimeout(timeout);
|
|
2795
|
+
}
|
|
2796
|
+
} catch (err) {
|
|
2797
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2798
|
+
return { success: false, error: msg };
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
});
|
|
2802
|
+
|
|
2803
|
+
// src/connectors/wix-store/index.ts
|
|
2804
|
+
var tools13 = { request: requestTool4 };
|
|
2805
|
+
var wixStoreConnector = new ConnectorPlugin({
|
|
2806
|
+
slug: "wix-store",
|
|
2807
|
+
authType: null,
|
|
2808
|
+
name: "Wix Store",
|
|
2809
|
+
description: "Connect to Wix Store.",
|
|
2810
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/YyFxclQFzROIYpFam6vRK/e7e75d3feac49a1cc5e433c147216d23/Wix_logo_black.svg",
|
|
2811
|
+
parameters: parameters13,
|
|
2812
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2813
|
+
systemPrompt: `## Wix Store API
|
|
2814
|
+
- Call the Wix Store REST API using the authenticated request tool
|
|
2815
|
+
- Authentication (API Key, Site ID) is automatically configured
|
|
2816
|
+
- Wix API uses POST for query endpoints as well
|
|
2817
|
+
|
|
2818
|
+
### List Products
|
|
2819
|
+
- POST stores/v1/products/query
|
|
2820
|
+
- Body: { "query": { "paging": { "limit": 50, "offset": 0 } } }
|
|
2821
|
+
|
|
2822
|
+
### Search Products (with filter)
|
|
2823
|
+
- POST stores/v1/products/query
|
|
2824
|
+
- Body: { "query": { "filter": { "name": { "$contains": "keyword" } }, "paging": { "limit": 50 } } }
|
|
2825
|
+
|
|
2826
|
+
### List Orders
|
|
2827
|
+
- POST stores/v2/orders/query
|
|
2828
|
+
- Body: { "query": { "paging": { "limit": 50 } } }
|
|
2829
|
+
|
|
2830
|
+
### List Collections (Categories)
|
|
2831
|
+
- POST stores/v1/collections/query
|
|
2832
|
+
- Body: { "query": { "paging": { "limit": 50 } } }`,
|
|
2833
|
+
tools: tools13
|
|
2834
|
+
});
|
|
2835
|
+
|
|
2836
|
+
// src/connectors/dbt/parameters.ts
|
|
2837
|
+
var parameters14 = {
|
|
2838
|
+
host: new ParameterDefinition({
|
|
2839
|
+
slug: "host",
|
|
2840
|
+
name: "dbt Cloud Host",
|
|
2841
|
+
description: "The dbt Cloud host URL (e.g., cloud.getdbt.com).",
|
|
2842
|
+
envVarBaseKey: "DBT_HOST",
|
|
2843
|
+
type: "text",
|
|
2844
|
+
secret: false,
|
|
2845
|
+
required: true
|
|
2846
|
+
}),
|
|
2847
|
+
accountId: new ParameterDefinition({
|
|
2848
|
+
slug: "account-id",
|
|
2849
|
+
name: "dbt Account ID",
|
|
2850
|
+
description: "The dbt Cloud account ID.",
|
|
2851
|
+
envVarBaseKey: "DBT_ACCOUNT_ID",
|
|
2852
|
+
type: "text",
|
|
2853
|
+
secret: false,
|
|
2854
|
+
required: true
|
|
2855
|
+
}),
|
|
2856
|
+
prodEnvId: new ParameterDefinition({
|
|
2857
|
+
slug: "prod-env-id",
|
|
2858
|
+
name: "dbt Production Environment ID",
|
|
2859
|
+
description: "The dbt Cloud production environment ID.",
|
|
2860
|
+
envVarBaseKey: "DBT_PROD_ENV_ID",
|
|
2861
|
+
type: "text",
|
|
2862
|
+
secret: false,
|
|
2863
|
+
required: true
|
|
2864
|
+
}),
|
|
2865
|
+
token: new ParameterDefinition({
|
|
2866
|
+
slug: "token",
|
|
2867
|
+
name: "dbt API Token",
|
|
2868
|
+
description: "The API token for authenticating with dbt Cloud.",
|
|
2869
|
+
envVarBaseKey: "DBT_TOKEN",
|
|
2870
|
+
type: "text",
|
|
2871
|
+
secret: true,
|
|
2872
|
+
required: true
|
|
2873
|
+
})
|
|
2874
|
+
};
|
|
2875
|
+
|
|
2876
|
+
// src/connectors/dbt/tools/request.ts
|
|
2877
|
+
import { z as z16 } from "zod";
|
|
2878
|
+
var REQUEST_TIMEOUT_MS7 = 6e4;
|
|
2879
|
+
function resolveGraphqlEndpoint(host) {
|
|
2880
|
+
if (host.includes("emea")) return "https://metadata.emea.dbt.com/graphql";
|
|
2881
|
+
if (host.includes(".au.")) return "https://metadata.au.dbt.com/graphql";
|
|
2882
|
+
return "https://metadata.cloud.getdbt.com/graphql";
|
|
2883
|
+
}
|
|
2884
|
+
var inputSchema16 = z16.object({
|
|
2885
|
+
toolUseIntent: z16.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
|
|
2886
|
+
connectionId: z16.string().describe("ID of the dbt Cloud connection to use"),
|
|
2887
|
+
query: z16.string().describe("GraphQL query"),
|
|
2888
|
+
variables: z16.record(z16.string(), z16.unknown()).optional().describe("GraphQL variables (JSON)")
|
|
2889
|
+
});
|
|
2890
|
+
var outputSchema16 = z16.discriminatedUnion("success", [
|
|
2891
|
+
z16.object({
|
|
2892
|
+
success: z16.literal(true),
|
|
2893
|
+
data: z16.record(z16.string(), z16.unknown())
|
|
2894
|
+
}),
|
|
2895
|
+
z16.object({
|
|
2896
|
+
success: z16.literal(false),
|
|
2897
|
+
error: z16.string()
|
|
2898
|
+
})
|
|
2899
|
+
]);
|
|
2900
|
+
var requestTool5 = new ConnectorTool({
|
|
2901
|
+
name: "request",
|
|
2902
|
+
description: `Send authenticated requests to the dbt Cloud Discovery API (GraphQL).
|
|
2903
|
+
Authentication is handled automatically using the API token.
|
|
2904
|
+
{environmentId} in GraphQL variables is automatically replaced with the prod-env-id.`,
|
|
2905
|
+
inputSchema: inputSchema16,
|
|
2906
|
+
outputSchema: outputSchema16,
|
|
2907
|
+
async execute({ connectionId, query, variables }, connections) {
|
|
2908
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
2909
|
+
if (!connection) {
|
|
2910
|
+
return { success: false, error: `Connection ${connectionId} not found` };
|
|
2911
|
+
}
|
|
2912
|
+
console.log(`[connector-request] dbt/${connection.name}: GraphQL query`);
|
|
2913
|
+
try {
|
|
2914
|
+
const host = parameters14.host.getValue(connection);
|
|
2915
|
+
const token = parameters14.token.getValue(connection);
|
|
2916
|
+
const environmentId = parameters14.prodEnvId.getValue(connection);
|
|
2917
|
+
const resolvedVariables = variables ? JSON.parse(
|
|
2918
|
+
JSON.stringify(variables).replace(/\{environmentId\}/g, environmentId)
|
|
2919
|
+
) : void 0;
|
|
2920
|
+
const endpoint = resolveGraphqlEndpoint(host);
|
|
2921
|
+
const controller = new AbortController();
|
|
2922
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS7);
|
|
2923
|
+
try {
|
|
2924
|
+
const response = await fetch(endpoint, {
|
|
2925
|
+
method: "POST",
|
|
2926
|
+
headers: {
|
|
2927
|
+
Authorization: `Bearer ${token}`,
|
|
2928
|
+
"Content-Type": "application/json"
|
|
2929
|
+
},
|
|
2930
|
+
body: JSON.stringify({ query, variables: resolvedVariables }),
|
|
2931
|
+
signal: controller.signal
|
|
2932
|
+
});
|
|
2933
|
+
const data = await response.json();
|
|
2934
|
+
if (!response.ok) {
|
|
2935
|
+
return {
|
|
2936
|
+
success: false,
|
|
2937
|
+
error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
if (Array.isArray(data?.errors) && data.errors.length > 0) {
|
|
2941
|
+
const errors = data.errors;
|
|
2942
|
+
return {
|
|
2943
|
+
success: false,
|
|
2944
|
+
error: errors.map((e) => e.message).join("; ")
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
return { success: true, data };
|
|
2948
|
+
} finally {
|
|
2949
|
+
clearTimeout(timeout);
|
|
2950
|
+
}
|
|
2951
|
+
} catch (err) {
|
|
2952
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2953
|
+
return { success: false, error: msg };
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
});
|
|
2957
|
+
|
|
2958
|
+
// src/connectors/dbt/index.ts
|
|
2959
|
+
var tools14 = { request: requestTool5 };
|
|
2960
|
+
var dbtConnector = new ConnectorPlugin({
|
|
2961
|
+
slug: "dbt",
|
|
2962
|
+
authType: null,
|
|
2963
|
+
name: "dbt",
|
|
2964
|
+
description: "Connect to dbt Cloud for data transformation and analytics engineering.",
|
|
2965
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4iT6ncXtdtHdkXexU0WgfZ/0367a38d245f2568eab5eb511f9ee692/dbt.png",
|
|
2966
|
+
parameters: parameters14,
|
|
2967
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
2968
|
+
systemPrompt: `## dbt Cloud Discovery API (GraphQL)
|
|
2969
|
+
- Call the dbt Cloud Discovery API using the authenticated request tool
|
|
2970
|
+
- {environmentId} in GraphQL variables is automatically replaced with the prod-env-id
|
|
2971
|
+
|
|
2972
|
+
### List Models
|
|
2973
|
+
query:
|
|
2974
|
+
query($environmentId: BigInt!) {
|
|
2975
|
+
environment(id: $environmentId) {
|
|
2976
|
+
applied {
|
|
2977
|
+
models(first: 100) {
|
|
2978
|
+
edges {
|
|
2979
|
+
node { uniqueId name description database schema alias materializedType }
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
variables: { "environmentId": "{environmentId}" }
|
|
2986
|
+
|
|
2987
|
+
### List Sources
|
|
2988
|
+
query:
|
|
2989
|
+
query($environmentId: BigInt!) {
|
|
2990
|
+
environment(id: $environmentId) {
|
|
2991
|
+
applied {
|
|
2992
|
+
sources(first: 100) {
|
|
2993
|
+
edges {
|
|
2994
|
+
node { uniqueId name description database schema identifier }
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
variables: { "environmentId": "{environmentId}" }
|
|
3001
|
+
|
|
3002
|
+
### Model Column Information
|
|
3003
|
+
query:
|
|
3004
|
+
query($environmentId: BigInt!, $uniqueId: String!) {
|
|
3005
|
+
environment(id: $environmentId) {
|
|
3006
|
+
applied {
|
|
3007
|
+
models(filter: { uniqueId: { eq: $uniqueId } }) {
|
|
3008
|
+
edges {
|
|
3009
|
+
node { uniqueId name columns { name description type } }
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
### Lineage (Dependencies)
|
|
3017
|
+
- Traverse relationships using ancestors / children fields
|
|
3018
|
+
- Get ancestors { uniqueId name } or children { uniqueId name } within node`,
|
|
3019
|
+
tools: tools14
|
|
3020
|
+
});
|
|
3021
|
+
|
|
3022
|
+
// src/connectors/squadbase-db/parameters.ts
|
|
3023
|
+
var parameters15 = {
|
|
3024
|
+
connectionUrl: new ParameterDefinition({
|
|
3025
|
+
slug: "connection-url",
|
|
3026
|
+
name: "Connection URL",
|
|
3027
|
+
description: "PostgreSQL connection URL for Squadbase DB.",
|
|
3028
|
+
envVarBaseKey: "SQUADBASE_DB_CONNECTION_URL",
|
|
3029
|
+
type: "text",
|
|
3030
|
+
secret: true,
|
|
3031
|
+
required: true
|
|
3032
|
+
})
|
|
3033
|
+
};
|
|
3034
|
+
|
|
3035
|
+
// src/connectors/squadbase-db/tools/execute-query.ts
|
|
3036
|
+
import { z as z17 } from "zod";
|
|
3037
|
+
var MAX_ROWS10 = 500;
|
|
3038
|
+
var CONNECT_TIMEOUT_MS3 = 1e4;
|
|
3039
|
+
var STATEMENT_TIMEOUT_MS2 = 6e4;
|
|
3040
|
+
var inputSchema17 = z17.object({
|
|
3041
|
+
toolUseIntent: z17.string().optional().describe(
|
|
3042
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
3043
|
+
),
|
|
3044
|
+
connectionId: z17.string().describe("ID of the Squadbase DB connection to use"),
|
|
3045
|
+
sql: z17.string().describe("PostgreSQL SQL query. Always include LIMIT in queries.")
|
|
3046
|
+
});
|
|
3047
|
+
var outputSchema17 = z17.discriminatedUnion("success", [
|
|
3048
|
+
z17.object({
|
|
3049
|
+
success: z17.literal(true),
|
|
3050
|
+
rowCount: z17.number(),
|
|
3051
|
+
truncated: z17.boolean(),
|
|
3052
|
+
rows: z17.array(z17.record(z17.string(), z17.unknown()))
|
|
3053
|
+
}),
|
|
3054
|
+
z17.object({
|
|
3055
|
+
success: z17.literal(false),
|
|
3056
|
+
error: z17.string()
|
|
3057
|
+
})
|
|
3058
|
+
]);
|
|
3059
|
+
var executeQueryTool10 = new ConnectorTool({
|
|
3060
|
+
name: "executeQuery",
|
|
3061
|
+
description: `Execute SQL against Squadbase DB (PostgreSQL). Returns up to ${MAX_ROWS10} rows.
|
|
3062
|
+
Use for: schema exploration (information_schema), data sampling, analytical queries.
|
|
3063
|
+
Avoid loading large amounts of data; always include LIMIT in queries.`,
|
|
3064
|
+
inputSchema: inputSchema17,
|
|
3065
|
+
outputSchema: outputSchema17,
|
|
3066
|
+
async execute({ connectionId, sql }, connections) {
|
|
3067
|
+
const connection = connections.find((c) => c.id === connectionId);
|
|
3068
|
+
if (!connection) {
|
|
3069
|
+
return {
|
|
3070
|
+
success: false,
|
|
3071
|
+
error: `Connection ${connectionId} not found`
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
console.log(
|
|
3075
|
+
`[connector-query] squadbase-db/${connection.name}: ${sql}`
|
|
3076
|
+
);
|
|
3077
|
+
let connectionUrl;
|
|
3078
|
+
try {
|
|
3079
|
+
const { Pool } = await import("pg");
|
|
3080
|
+
connectionUrl = parameters15.connectionUrl.getValue(connection);
|
|
3081
|
+
const pool = new Pool({
|
|
3082
|
+
connectionString: connectionUrl,
|
|
3083
|
+
ssl: { rejectUnauthorized: false },
|
|
3084
|
+
connectionTimeoutMillis: CONNECT_TIMEOUT_MS3,
|
|
3085
|
+
statement_timeout: STATEMENT_TIMEOUT_MS2
|
|
3086
|
+
});
|
|
3087
|
+
try {
|
|
3088
|
+
const result = await pool.query(sql);
|
|
3089
|
+
const rows = result.rows;
|
|
3090
|
+
const truncated = rows.length > MAX_ROWS10;
|
|
3091
|
+
return {
|
|
3092
|
+
success: true,
|
|
3093
|
+
rowCount: Math.min(rows.length, MAX_ROWS10),
|
|
3094
|
+
truncated,
|
|
3095
|
+
rows: rows.slice(0, MAX_ROWS10)
|
|
3096
|
+
};
|
|
3097
|
+
} finally {
|
|
3098
|
+
await pool.end();
|
|
3099
|
+
}
|
|
3100
|
+
} catch (err) {
|
|
3101
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
3102
|
+
if (connectionUrl) {
|
|
3103
|
+
msg = msg.replaceAll(connectionUrl, "***");
|
|
3104
|
+
}
|
|
3105
|
+
return { success: false, error: msg };
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
});
|
|
3109
|
+
|
|
3110
|
+
// src/connectors/squadbase-db/index.ts
|
|
3111
|
+
var tools15 = { executeQuery: executeQueryTool10 };
|
|
3112
|
+
var squadbaseDbConnector = new ConnectorPlugin({
|
|
3113
|
+
slug: "squadbase-db",
|
|
3114
|
+
authType: null,
|
|
3115
|
+
name: "Squadbase DB",
|
|
3116
|
+
description: "Connect to Squadbase DB (PostgreSQL).",
|
|
3117
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/25y0XqMxIufeD3egWH3bEl/659b4ade405890654cfaf91c03a4b458/icon.svg",
|
|
3118
|
+
parameters: parameters15,
|
|
3119
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
3120
|
+
systemPrompt: `## Squadbase DB SQL Notes
|
|
3121
|
+
- Uses PostgreSQL based SQL syntax
|
|
3122
|
+
- Schema exploration:
|
|
3123
|
+
- List tables: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
|
|
3124
|
+
- List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
|
|
3125
|
+
- Always include LIMIT in queries`,
|
|
3126
|
+
tools: tools15,
|
|
3127
|
+
async query(params, sql, namedParams) {
|
|
3128
|
+
const { Pool } = await import("pg");
|
|
3129
|
+
const { text, values } = buildPositionalParams(sql, namedParams);
|
|
3130
|
+
const pool = new Pool({
|
|
3131
|
+
connectionString: params[parameters15.connectionUrl.slug],
|
|
3132
|
+
ssl: { rejectUnauthorized: false },
|
|
3133
|
+
connectionTimeoutMillis: 1e4,
|
|
3134
|
+
statement_timeout: 6e4
|
|
3135
|
+
});
|
|
3136
|
+
try {
|
|
3137
|
+
const result = await pool.query(text, values);
|
|
3138
|
+
return { rows: result.rows };
|
|
3139
|
+
} finally {
|
|
3140
|
+
await pool.end();
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
});
|
|
3144
|
+
|
|
3145
|
+
// src/connectors/registry.ts
|
|
3146
|
+
var plugins = {
|
|
3147
|
+
snowflake: snowflakeConnector,
|
|
3148
|
+
snowflakePat: snowflakePatConnector,
|
|
3149
|
+
bigquery: bigqueryConnector,
|
|
3150
|
+
bigqueryOauth: bigqueryOauthConnector,
|
|
3151
|
+
databricks: databricksConnector,
|
|
3152
|
+
redshift: redshiftConnector,
|
|
3153
|
+
dbt: dbtConnector,
|
|
3154
|
+
awsAthena: awsAthenaConnector,
|
|
3155
|
+
postgresql: postgresqlConnector,
|
|
3156
|
+
mysql: mysqlConnector,
|
|
3157
|
+
googleAnalytics: googleAnalyticsConnector,
|
|
3158
|
+
airtable: airtableConnector,
|
|
3159
|
+
squadbaseDb: squadbaseDbConnector,
|
|
3160
|
+
kintone: kintoneConnector,
|
|
3161
|
+
wixStore: wixStoreConnector
|
|
3162
|
+
};
|
|
3163
|
+
var connectors = {
|
|
3164
|
+
...plugins,
|
|
3165
|
+
/**
|
|
3166
|
+
* Return plugins that have at least one matching connection.
|
|
3167
|
+
*/
|
|
3168
|
+
pluginsFor(connections) {
|
|
3169
|
+
const keys = new Set(
|
|
3170
|
+
connections.map(
|
|
3171
|
+
(c) => ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType)
|
|
3172
|
+
)
|
|
3173
|
+
);
|
|
3174
|
+
return Object.values(plugins).filter((p) => keys.has(p.connectorKey));
|
|
3175
|
+
},
|
|
3176
|
+
/**
|
|
3177
|
+
* Find a plugin by slug and authType.
|
|
3178
|
+
*/
|
|
3179
|
+
findByKey(slug, authType) {
|
|
3180
|
+
const key = ConnectorPlugin.deriveKey(slug, authType);
|
|
3181
|
+
return Object.values(plugins).find((p) => p.connectorKey === key);
|
|
3182
|
+
}
|
|
3183
|
+
};
|
|
345
3184
|
export {
|
|
346
3185
|
AUTH_TYPES,
|
|
347
3186
|
ConnectorPlugin,
|
|
3187
|
+
ConnectorSetup,
|
|
348
3188
|
ConnectorTool,
|
|
349
3189
|
ParameterDefinition,
|
|
3190
|
+
airtableConnector,
|
|
3191
|
+
awsAthenaConnector,
|
|
3192
|
+
bigqueryConnector,
|
|
3193
|
+
bigqueryOauthConnector,
|
|
350
3194
|
connectors,
|
|
351
|
-
|
|
3195
|
+
databricksConnector,
|
|
3196
|
+
dbtConnector,
|
|
3197
|
+
googleAnalyticsConnector,
|
|
3198
|
+
kintoneConnector,
|
|
3199
|
+
mysqlConnector,
|
|
3200
|
+
postgresqlConnector,
|
|
3201
|
+
redshiftConnector,
|
|
3202
|
+
snowflakeConnector,
|
|
3203
|
+
snowflakePatConnector,
|
|
3204
|
+
squadbaseDbConnector,
|
|
3205
|
+
wixStoreConnector
|
|
352
3206
|
};
|