@squadbase/vite-server 0.1.9-dev.87dd3f7 → 0.1.9-dev.a120137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +73157 -60061
- package/dist/connectors/asana.js +15 -2
- package/dist/connectors/aws-billing.d.ts +5 -0
- package/dist/connectors/aws-billing.js +29843 -0
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +657 -0
- package/dist/connectors/clickup.d.ts +5 -0
- package/dist/connectors/clickup.js +850 -0
- package/dist/connectors/freshdesk.d.ts +5 -0
- package/dist/connectors/freshdesk.js +842 -0
- package/dist/connectors/freshsales.d.ts +5 -0
- package/dist/connectors/freshsales.js +867 -0
- package/dist/connectors/freshservice.d.ts +5 -0
- package/dist/connectors/freshservice.js +813 -0
- package/dist/connectors/github.d.ts +5 -0
- package/dist/connectors/github.js +963 -0
- package/dist/connectors/gmail-oauth.js +15 -2
- package/dist/connectors/gmail.js +23 -14
- package/dist/connectors/google-audit-log.js +25 -14
- package/dist/connectors/google-calendar-oauth.js +18 -2
- package/dist/connectors/google-calendar.js +40 -26
- package/dist/connectors/google-docs.js +18 -2
- package/dist/connectors/google-drive.js +15 -2
- package/dist/connectors/google-search-console-oauth.d.ts +5 -0
- package/dist/connectors/google-search-console-oauth.js +923 -0
- package/dist/connectors/google-sheets.js +18 -2
- package/dist/connectors/google-slides.js +18 -2
- package/dist/connectors/jdbc.d.ts +5 -0
- package/dist/connectors/jdbc.js +21097 -0
- package/dist/connectors/monday.d.ts +5 -0
- package/dist/connectors/monday.js +853 -0
- package/dist/connectors/oracle.d.ts +5 -0
- package/dist/connectors/oracle.js +665 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +812 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +656 -0
- package/dist/connectors/supabase.d.ts +5 -0
- package/dist/connectors/supabase.js +582 -0
- package/dist/connectors/tiktok-ads.js +15 -2
- package/dist/index.js +73218 -60122
- package/dist/main.js +73212 -60116
- package/dist/vite-plugin.js +73118 -60022
- package/package.json +60 -2
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
// ../connectors/src/parameter-definition.ts
|
|
2
|
+
var ParameterDefinition = class {
|
|
3
|
+
slug;
|
|
4
|
+
name;
|
|
5
|
+
description;
|
|
6
|
+
envVarBaseKey;
|
|
7
|
+
type;
|
|
8
|
+
secret;
|
|
9
|
+
required;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.slug = config.slug;
|
|
12
|
+
this.name = config.name;
|
|
13
|
+
this.description = config.description;
|
|
14
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
15
|
+
this.type = config.type;
|
|
16
|
+
this.secret = config.secret;
|
|
17
|
+
this.required = config.required;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
21
|
+
*/
|
|
22
|
+
getValue(connection2) {
|
|
23
|
+
const param = connection2.parameters.find(
|
|
24
|
+
(p) => p.parameterSlug === this.slug
|
|
25
|
+
);
|
|
26
|
+
if (!param || param.value == null) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return param.value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
35
|
+
*/
|
|
36
|
+
tryGetValue(connection2) {
|
|
37
|
+
const param = connection2.parameters.find(
|
|
38
|
+
(p) => p.parameterSlug === this.slug
|
|
39
|
+
);
|
|
40
|
+
if (!param || param.value == null) return void 0;
|
|
41
|
+
return param.value;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ../connectors/src/connectors/oracle/utils.ts
|
|
46
|
+
var JDBC_THIN_PREFIX_RE = /^jdbc:oracle:thin:/i;
|
|
47
|
+
var JDBC_OCI_PREFIX_RE = /^jdbc:oracle:oci/i;
|
|
48
|
+
var URL_PREFIX_RE = /^oracle:\/\//i;
|
|
49
|
+
function parseOracleJdbcUrl(jdbcUrl, options = {}) {
|
|
50
|
+
const trimmed = jdbcUrl.trim();
|
|
51
|
+
if (JDBC_OCI_PREFIX_RE.test(trimmed)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"Oracle OCI driver URLs are not supported. Use the thin driver form: jdbc:oracle:thin:@host:port/service"
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (URL_PREFIX_RE.test(trimmed)) {
|
|
57
|
+
const url = new URL(trimmed);
|
|
58
|
+
const path2 = url.pathname.replace(/^\//, "");
|
|
59
|
+
if (!url.hostname || !path2) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Invalid Oracle URL "${redactOracleUrl(trimmed)}". Expected oracle://[user:password@]host:port/service`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const port = url.port || "1521";
|
|
65
|
+
return {
|
|
66
|
+
connectString: `${url.hostname}:${port}/${path2}`,
|
|
67
|
+
user: url.username ? decodeURIComponent(url.username) : options.username,
|
|
68
|
+
password: url.password ? decodeURIComponent(url.password) : options.password
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (!JDBC_THIN_PREFIX_RE.test(trimmed)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Unsupported Oracle URL "${redactOracleUrl(trimmed)}". Expected prefix: jdbc:oracle:thin:@ or oracle://`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const afterPrefix = trimmed.replace(JDBC_THIN_PREFIX_RE, "");
|
|
77
|
+
const atIdx = afterPrefix.indexOf("@");
|
|
78
|
+
if (atIdx === -1) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Invalid Oracle JDBC URL "${redactOracleUrl(trimmed)}". Expected '@' separator before host.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const credentialsPart = afterPrefix.slice(0, atIdx);
|
|
84
|
+
const target = afterPrefix.slice(atIdx + 1).replace(/^\/\//, "");
|
|
85
|
+
let user = options.username;
|
|
86
|
+
let password = options.password;
|
|
87
|
+
if (credentialsPart) {
|
|
88
|
+
const slashIdx = credentialsPart.indexOf("/");
|
|
89
|
+
if (slashIdx === -1) {
|
|
90
|
+
user = credentialsPart || user;
|
|
91
|
+
} else {
|
|
92
|
+
user = credentialsPart.slice(0, slashIdx) || user;
|
|
93
|
+
password = credentialsPart.slice(slashIdx + 1) || password;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!target) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Invalid Oracle JDBC URL "${redactOracleUrl(trimmed)}". Missing host portion after '@'.`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
connectString: target,
|
|
103
|
+
user,
|
|
104
|
+
password
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function redactOracleUrl(jdbcUrl) {
|
|
108
|
+
return jdbcUrl.replace(/(:\/\/)([^@/]+)@/, "$1***@").replace(/(thin:)([^@]+)@/i, "$1***@");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ../connectors/src/lib/oracle-runner.ts
|
|
112
|
+
async function importOracleDb() {
|
|
113
|
+
const mod = await import("oracledb");
|
|
114
|
+
return mod.default ?? mod;
|
|
115
|
+
}
|
|
116
|
+
async function runOracleQuery(parsed, sql) {
|
|
117
|
+
const oracledb = await importOracleDb();
|
|
118
|
+
const connection2 = await oracledb.getConnection({
|
|
119
|
+
user: parsed.user,
|
|
120
|
+
password: parsed.password,
|
|
121
|
+
connectString: parsed.connectString
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
const result = await connection2.execute(sql, [], {
|
|
125
|
+
outFormat: oracledb.OUT_FORMAT_OBJECT,
|
|
126
|
+
// Bound by the connector's own row cap, but keep the driver from
|
|
127
|
+
// streaming arbitrarily large result sets.
|
|
128
|
+
maxRows: 5e3
|
|
129
|
+
});
|
|
130
|
+
return { rows: result.rows ?? [] };
|
|
131
|
+
} finally {
|
|
132
|
+
try {
|
|
133
|
+
await connection2.close();
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function checkOracleConnection(url, credentials) {
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = parseOracleJdbcUrl(url, credentials);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: err instanceof Error ? err.message : String(err)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await runOracleQuery(parsed, "SELECT 1 FROM DUAL");
|
|
150
|
+
return { success: true };
|
|
151
|
+
} catch (err) {
|
|
152
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
153
|
+
msg = msg.replaceAll(url, redactOracleUrl(url));
|
|
154
|
+
return { success: false, error: msg };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ../connectors/src/connectors/oracle/parameters.ts
|
|
159
|
+
var parameters = {
|
|
160
|
+
jdbcUrl: new ParameterDefinition({
|
|
161
|
+
slug: "jdbc-url",
|
|
162
|
+
name: "Oracle JDBC URL",
|
|
163
|
+
description: "JDBC-style connection URL for Oracle Database (e.g. `jdbc:oracle:thin:@host:1521/service`, or the legacy SID form `jdbc:oracle:thin:@host:1521:sid`). Only the thin driver is supported. The shorthand `oracle://user:password@host:1521/service` is also accepted.",
|
|
164
|
+
envVarBaseKey: "ORACLE_JDBC_URL",
|
|
165
|
+
type: "text",
|
|
166
|
+
secret: true,
|
|
167
|
+
required: true
|
|
168
|
+
}),
|
|
169
|
+
username: new ParameterDefinition({
|
|
170
|
+
slug: "username",
|
|
171
|
+
name: "Username",
|
|
172
|
+
description: "Oracle username. Optional when embedded in the JDBC URL (e.g. `jdbc:oracle:thin:user/pass@host:1521/service`).",
|
|
173
|
+
envVarBaseKey: "ORACLE_USERNAME",
|
|
174
|
+
type: "text",
|
|
175
|
+
secret: false,
|
|
176
|
+
required: false
|
|
177
|
+
}),
|
|
178
|
+
password: new ParameterDefinition({
|
|
179
|
+
slug: "password",
|
|
180
|
+
name: "Password",
|
|
181
|
+
description: "Oracle password. Optional when embedded in the JDBC URL.",
|
|
182
|
+
envVarBaseKey: "ORACLE_PASSWORD",
|
|
183
|
+
type: "text",
|
|
184
|
+
secret: true,
|
|
185
|
+
required: false
|
|
186
|
+
})
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// ../connectors/src/connectors/oracle/sdk/index.ts
|
|
190
|
+
function createClient(params) {
|
|
191
|
+
const jdbcUrl = params[parameters.jdbcUrl.slug];
|
|
192
|
+
if (!jdbcUrl) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`oracle: missing required parameter: ${parameters.jdbcUrl.slug}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const username = params[parameters.username.slug];
|
|
198
|
+
const password = params[parameters.password.slug];
|
|
199
|
+
const parsed = parseOracleJdbcUrl(jdbcUrl, { username, password });
|
|
200
|
+
async function runQuery(sql) {
|
|
201
|
+
try {
|
|
202
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
203
|
+
const { rows } = await runOracleQuery(parsed, cleanSql);
|
|
204
|
+
return rows;
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
207
|
+
throw new Error(msg.replaceAll(jdbcUrl, redactOracleUrl(jdbcUrl)));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return { query: runQuery };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ../connectors/src/connector-onboarding.ts
|
|
214
|
+
var ConnectorOnboarding = class {
|
|
215
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
216
|
+
connectionSetupInstructions;
|
|
217
|
+
/** Phase 2: Data overview instructions */
|
|
218
|
+
dataOverviewInstructions;
|
|
219
|
+
constructor(config) {
|
|
220
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
221
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
222
|
+
}
|
|
223
|
+
getConnectionSetupPrompt(language) {
|
|
224
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
225
|
+
}
|
|
226
|
+
getDataOverviewInstructions(language) {
|
|
227
|
+
return this.dataOverviewInstructions[language];
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// ../connectors/src/connector-tool.ts
|
|
232
|
+
var ConnectorTool = class {
|
|
233
|
+
name;
|
|
234
|
+
description;
|
|
235
|
+
inputSchema;
|
|
236
|
+
outputSchema;
|
|
237
|
+
_execute;
|
|
238
|
+
constructor(config) {
|
|
239
|
+
this.name = config.name;
|
|
240
|
+
this.description = config.description;
|
|
241
|
+
this.inputSchema = config.inputSchema;
|
|
242
|
+
this.outputSchema = config.outputSchema;
|
|
243
|
+
this._execute = config.execute;
|
|
244
|
+
}
|
|
245
|
+
createTool(connections, config) {
|
|
246
|
+
return {
|
|
247
|
+
description: this.description,
|
|
248
|
+
inputSchema: this.inputSchema,
|
|
249
|
+
outputSchema: this.outputSchema,
|
|
250
|
+
execute: (input) => this._execute(input, connections, config)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// ../connectors/src/connector-plugin.ts
|
|
256
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
257
|
+
slug;
|
|
258
|
+
authType;
|
|
259
|
+
name;
|
|
260
|
+
description;
|
|
261
|
+
iconUrl;
|
|
262
|
+
parameters;
|
|
263
|
+
releaseFlag;
|
|
264
|
+
proxyPolicy;
|
|
265
|
+
experimentalAttributes;
|
|
266
|
+
categories;
|
|
267
|
+
onboarding;
|
|
268
|
+
systemPrompt;
|
|
269
|
+
tools;
|
|
270
|
+
query;
|
|
271
|
+
checkConnection;
|
|
272
|
+
constructor(config) {
|
|
273
|
+
this.slug = config.slug;
|
|
274
|
+
this.authType = config.authType;
|
|
275
|
+
this.name = config.name;
|
|
276
|
+
this.description = config.description;
|
|
277
|
+
this.iconUrl = config.iconUrl;
|
|
278
|
+
this.parameters = config.parameters;
|
|
279
|
+
this.releaseFlag = config.releaseFlag;
|
|
280
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
281
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
282
|
+
this.categories = config.categories ?? [];
|
|
283
|
+
this.onboarding = config.onboarding;
|
|
284
|
+
this.systemPrompt = config.systemPrompt;
|
|
285
|
+
this.tools = config.tools;
|
|
286
|
+
this.query = config.query;
|
|
287
|
+
this.checkConnection = config.checkConnection;
|
|
288
|
+
}
|
|
289
|
+
get connectorKey() {
|
|
290
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Create tools for connections that belong to this connector.
|
|
294
|
+
* Filters connections by connectorKey internally.
|
|
295
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
296
|
+
*/
|
|
297
|
+
createTools(connections, config, opts) {
|
|
298
|
+
const myConnections = connections.filter(
|
|
299
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
300
|
+
);
|
|
301
|
+
const result = {};
|
|
302
|
+
for (const t of Object.values(this.tools)) {
|
|
303
|
+
const tool = t.createTool(myConnections, config);
|
|
304
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
305
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
306
|
+
...tool,
|
|
307
|
+
toModelOutput: async (options) => {
|
|
308
|
+
if (!originalToModelOutput) {
|
|
309
|
+
return opts.truncateOutput(options.output);
|
|
310
|
+
}
|
|
311
|
+
const modelOutput = await originalToModelOutput(options);
|
|
312
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
313
|
+
return opts.truncateOutput(modelOutput.value);
|
|
314
|
+
}
|
|
315
|
+
return modelOutput;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
static deriveKey(slug, authType) {
|
|
322
|
+
if (authType) return `${slug}-${authType}`;
|
|
323
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
324
|
+
// user-password
|
|
325
|
+
"postgresql": "user-password",
|
|
326
|
+
"mysql": "user-password",
|
|
327
|
+
"clickhouse": "user-password",
|
|
328
|
+
"kintone": "user-password",
|
|
329
|
+
"squadbase-db": "user-password",
|
|
330
|
+
// service-account
|
|
331
|
+
"snowflake": "service-account",
|
|
332
|
+
"bigquery": "service-account",
|
|
333
|
+
"google-analytics": "service-account",
|
|
334
|
+
"google-calendar": "service-account",
|
|
335
|
+
"aws-athena": "service-account",
|
|
336
|
+
"redshift": "service-account",
|
|
337
|
+
// api-key
|
|
338
|
+
"databricks": "api-key",
|
|
339
|
+
"dbt": "api-key",
|
|
340
|
+
"airtable": "api-key",
|
|
341
|
+
"openai": "api-key",
|
|
342
|
+
"gemini": "api-key",
|
|
343
|
+
"anthropic": "api-key",
|
|
344
|
+
"wix-store": "api-key"
|
|
345
|
+
};
|
|
346
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
347
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
348
|
+
return slug;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// ../connectors/src/auth-types.ts
|
|
353
|
+
var AUTH_TYPES = {
|
|
354
|
+
OAUTH: "oauth",
|
|
355
|
+
API_KEY: "api-key",
|
|
356
|
+
JWT: "jwt",
|
|
357
|
+
SERVICE_ACCOUNT: "service-account",
|
|
358
|
+
PAT: "pat",
|
|
359
|
+
USER_PASSWORD: "user-password"
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// ../connectors/src/connectors/oracle/setup.ts
|
|
363
|
+
var oracleOnboarding = new ConnectorOnboarding({
|
|
364
|
+
dataOverviewInstructions: {
|
|
365
|
+
en: `1. Use oracle_executeQuery to confirm the version: \`SELECT BANNER FROM V$VERSION\`
|
|
366
|
+
2. Identify the current schema (Oracle treats users as schemas): \`SELECT USER FROM DUAL\`
|
|
367
|
+
3. List user tables: \`SELECT TABLE_NAME FROM USER_TABLES\`. To explore other schemas the connection has access to, use \`ALL_TABLES\` filtered by \`OWNER\`.
|
|
368
|
+
4. For key tables, fetch column info: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\` (Oracle stores unquoted identifiers as upper-case).
|
|
369
|
+
5. Sample up to 3 tables. Oracle has no \`LIMIT\` keyword: use \`FETCH FIRST n ROWS ONLY\` or \`ROWNUM\`: \`SELECT * FROM <table> FETCH FIRST 5 ROWS ONLY\``,
|
|
370
|
+
ja: `1. oracle_executeQuery \u3067\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u78BA\u8A8D: \`SELECT BANNER FROM V$VERSION\`
|
|
371
|
+
2. \u73FE\u5728\u306E\u30B9\u30AD\u30FC\u30DE\uFF08Oracle\u3067\u306F\u30E6\u30FC\u30B6\u30FC\uFF1D\u30B9\u30AD\u30FC\u30DE\uFF09\u3092\u78BA\u8A8D: \`SELECT USER FROM DUAL\`
|
|
372
|
+
3. \u30E6\u30FC\u30B6\u30FC\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97: \`SELECT TABLE_NAME FROM USER_TABLES\`\u3002\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u4ED6\u30B9\u30AD\u30FC\u30DE\u3092\u53C2\u7167\u3059\u308B\u5834\u5408\u306F \`ALL_TABLES\` \u3092 \`OWNER\` \u3067\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\u3002
|
|
373
|
+
4. \u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30AB\u30E9\u30E0\u60C5\u5831\u3092\u53D6\u5F97: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`\uFF08Oracle \u306F\u30AF\u30A9\u30FC\u30C8\u306A\u3057\u8B58\u5225\u5B50\u3092\u5927\u6587\u5B57\u3067\u4FDD\u5B58\uFF09\u3002
|
|
374
|
+
5. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u6700\u59273\u30C6\u30FC\u30D6\u30EB\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3002Oracle \u306B\u306F \`LIMIT\` \u304C\u306A\u3044\u305F\u3081 \`FETCH FIRST n ROWS ONLY\` \u307E\u305F\u306F \`ROWNUM\` \u3092\u4F7F\u7528: \`SELECT * FROM <table> FETCH FIRST 5 ROWS ONLY\``
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// ../connectors/src/connectors/oracle/tools/execute-query.ts
|
|
379
|
+
import { z } from "zod";
|
|
380
|
+
var MAX_ROWS = 500;
|
|
381
|
+
var inputSchema = z.object({
|
|
382
|
+
toolUseIntent: z.string().optional().describe(
|
|
383
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
384
|
+
),
|
|
385
|
+
connectionId: z.string().describe("ID of the Oracle connection to use"),
|
|
386
|
+
sql: z.string().describe(
|
|
387
|
+
"PL/SQL-compatible query. Oracle has no `LIMIT`; use `FETCH FIRST n ROWS ONLY` or filter on `ROWNUM`. Don't end the statement with a semicolon \u2014 the driver rejects trailing terminators."
|
|
388
|
+
)
|
|
389
|
+
});
|
|
390
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
391
|
+
z.object({
|
|
392
|
+
success: z.literal(true),
|
|
393
|
+
rowCount: z.number(),
|
|
394
|
+
truncated: z.boolean(),
|
|
395
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
396
|
+
}),
|
|
397
|
+
z.object({
|
|
398
|
+
success: z.literal(false),
|
|
399
|
+
error: z.string()
|
|
400
|
+
})
|
|
401
|
+
]);
|
|
402
|
+
var executeQueryTool = new ConnectorTool({
|
|
403
|
+
name: "executeQuery",
|
|
404
|
+
description: `Execute a query against an Oracle Database. Returns up to ${MAX_ROWS} rows.
|
|
405
|
+
Use for: schema exploration via \`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\`, data sampling, and analytical queries.
|
|
406
|
+
Oracle uses \`FETCH FIRST n ROWS ONLY\` (12c+) or \`ROWNUM\` for row limiting \u2014 there is no \`LIMIT\` keyword.
|
|
407
|
+
Unquoted identifiers are stored upper-case (\`SELECT * FROM employees\` resolves to \`EMPLOYEES\`).
|
|
408
|
+
Do NOT terminate statements with a semicolon; the driver rejects trailing terminators.`,
|
|
409
|
+
inputSchema,
|
|
410
|
+
outputSchema,
|
|
411
|
+
async execute({ connectionId, sql }, connections) {
|
|
412
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
413
|
+
if (!connection2) {
|
|
414
|
+
return {
|
|
415
|
+
success: false,
|
|
416
|
+
error: `Connection ${connectionId} not found`
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const jdbcUrl = parameters.jdbcUrl.getValue(connection2);
|
|
420
|
+
const username = parameters.username.tryGetValue(connection2);
|
|
421
|
+
const password = parameters.password.tryGetValue(connection2);
|
|
422
|
+
console.log(
|
|
423
|
+
`[connector-query] oracle/${connection2.name} (${redactOracleUrl(jdbcUrl)}): ${sql}`
|
|
424
|
+
);
|
|
425
|
+
let parsed;
|
|
426
|
+
try {
|
|
427
|
+
parsed = parseOracleJdbcUrl(jdbcUrl, { username, password });
|
|
428
|
+
} catch (err) {
|
|
429
|
+
return {
|
|
430
|
+
success: false,
|
|
431
|
+
error: err instanceof Error ? err.message : String(err)
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
436
|
+
const { rows } = await runOracleQuery(parsed, cleanSql);
|
|
437
|
+
const truncated = rows.length > MAX_ROWS;
|
|
438
|
+
return {
|
|
439
|
+
success: true,
|
|
440
|
+
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
441
|
+
truncated,
|
|
442
|
+
rows: rows.slice(0, MAX_ROWS)
|
|
443
|
+
};
|
|
444
|
+
} catch (err) {
|
|
445
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
446
|
+
msg = msg.replaceAll(jdbcUrl, redactOracleUrl(jdbcUrl));
|
|
447
|
+
return { success: false, error: msg };
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// ../connectors/src/connectors/oracle/index.ts
|
|
453
|
+
var tools = { executeQuery: executeQueryTool };
|
|
454
|
+
var oracleConnector = new ConnectorPlugin({
|
|
455
|
+
slug: "oracle",
|
|
456
|
+
authType: AUTH_TYPES.USER_PASSWORD,
|
|
457
|
+
name: "Oracle Database",
|
|
458
|
+
description: "Connect to Oracle Database using a JDBC-style URL via the pure-JS Thin driver (no Oracle Instant Client required).",
|
|
459
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3iGEdzvGHncU5bYqFOROiV/9e7bdda7230d7ca6b34e7f6a862de876/oracle-icon.webp",
|
|
460
|
+
parameters,
|
|
461
|
+
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
462
|
+
categories: ["database"],
|
|
463
|
+
onboarding: oracleOnboarding,
|
|
464
|
+
systemPrompt: {
|
|
465
|
+
en: `### Tools
|
|
466
|
+
|
|
467
|
+
- \`oracle_executeQuery\`: Executes a SQL query against an Oracle Database and returns rows. Use it for schema exploration via \`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` and for sampling data. See the SQL Reference below for Oracle-specific syntax.
|
|
468
|
+
|
|
469
|
+
### Business Logic
|
|
470
|
+
|
|
471
|
+
The business logic type for this connector is "sql".
|
|
472
|
+
|
|
473
|
+
### SQL Reference
|
|
474
|
+
- Schema layout: in Oracle a "user" is a schema. Use \`USER_*\` views (\`USER_TABLES\`, \`USER_TAB_COLUMNS\`) for the connected user's objects, or \`ALL_*\` views to see everything the user has been granted access to.
|
|
475
|
+
- Schema exploration:
|
|
476
|
+
- List user tables: \`SELECT TABLE_NAME FROM USER_TABLES\`
|
|
477
|
+
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`
|
|
478
|
+
- Cross-schema: \`SELECT OWNER, TABLE_NAME FROM ALL_TABLES WHERE OWNER = 'ANALYTICS'\`
|
|
479
|
+
- Row limiting: there is **no** \`LIMIT\` keyword. Use \`FETCH FIRST n ROWS ONLY\` (12c+), \`OFFSET m ROWS FETCH NEXT n ROWS ONLY\`, or filter on the \`ROWNUM\` pseudo-column.
|
|
480
|
+
- Identifier case: unquoted identifiers are folded to upper-case at parse time. \`employees\` and \`EMPLOYEES\` resolve to the same object, but \`"employees"\` (double-quoted) is a distinct, case-sensitive name.
|
|
481
|
+
- Single-row table: use \`SELECT 1 FROM DUAL\` for connection probes \u2014 Oracle requires a \`FROM\` clause on every query.
|
|
482
|
+
- Do not terminate statements with a semicolon; the thin driver rejects trailing terminators.`,
|
|
483
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
484
|
+
|
|
485
|
+
- \`oracle_executeQuery\`: Oracle Database \u306B\u5BFE\u3057\u3066 SQL \u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\`USER_TABLES\` / \`USER_TAB_COLUMNS\` / \`ALL_TABLES\` \u3092\u4F7F\u3063\u305F\u30B9\u30AD\u30FC\u30DE\u63A2\u7D22\u3084\u30C7\u30FC\u30BF\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u3044\u307E\u3059\u3002Oracle \u56FA\u6709\u306E\u69CB\u6587\u306F\u4E0B\u90E8\u306E\u300CSQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
486
|
+
|
|
487
|
+
### Business Logic
|
|
488
|
+
|
|
489
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "sql" \u3067\u3059\u3002
|
|
490
|
+
|
|
491
|
+
### SQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
492
|
+
- \u30B9\u30AD\u30FC\u30DE\u69CB\u6210: Oracle \u306E "\u30E6\u30FC\u30B6\u30FC" \u306F\u30B9\u30AD\u30FC\u30DE\u3068\u7B49\u4FA1\u3067\u3059\u3002\u63A5\u7D9A\u30E6\u30FC\u30B6\u30FC\u6240\u6709\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306F \`USER_*\` \u30D3\u30E5\u30FC\uFF08\`USER_TABLES\`, \`USER_TAB_COLUMNS\`\uFF09\u3001\u4ED8\u4E0E\u6E08\u307F\u306E\u3082\u306E\u3059\u3079\u3066\u306F \`ALL_*\` \u30D3\u30E5\u30FC\u3067\u53C2\u7167\u3057\u307E\u3059\u3002
|
|
493
|
+
- \u30B9\u30AD\u30FC\u30DE\u63A2\u7D22:
|
|
494
|
+
- \u30E6\u30FC\u30B6\u30FC\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT TABLE_NAME FROM USER_TABLES\`
|
|
495
|
+
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`
|
|
496
|
+
- \u30AF\u30ED\u30B9\u30B9\u30AD\u30FC\u30DE: \`SELECT OWNER, TABLE_NAME FROM ALL_TABLES WHERE OWNER = 'ANALYTICS'\`
|
|
497
|
+
- \u884C\u6570\u5236\u9650: \`LIMIT\` \u30AD\u30FC\u30EF\u30FC\u30C9\u306F **\u5B58\u5728\u3057\u307E\u305B\u3093**\u3002\`FETCH FIRST n ROWS ONLY\`\uFF0812c\u4EE5\u964D\uFF09\u3001\`OFFSET m ROWS FETCH NEXT n ROWS ONLY\`\u3001\u307E\u305F\u306F \`ROWNUM\` \u7591\u4F3C\u5217\u3067\u30D5\u30A3\u30EB\u30BF\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
498
|
+
- \u8B58\u5225\u5B50\u306E\u5927\u6587\u5B57\u5C0F\u6587\u5B57: \u5F15\u7528\u7B26\u306A\u3057\u306E\u8B58\u5225\u5B50\u306F\u30D1\u30FC\u30B9\u6642\u306B\u5927\u6587\u5B57\u3078\u7573\u307F\u8FBC\u307E\u308C\u307E\u3059\u3002\`employees\` \u3068 \`EMPLOYEES\` \u306F\u540C\u3058\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u6307\u3057\u307E\u3059\u304C\u3001\`"employees"\`\uFF08\u30C0\u30D6\u30EB\u30AF\u30A9\u30FC\u30C8\uFF09\u306F\u5225\u306E case-sensitive \u306A\u540D\u524D\u3067\u3059\u3002
|
|
499
|
+
- \u5358\u4E00\u884C\u30C6\u30FC\u30D6\u30EB: \u63A5\u7D9A\u78BA\u8A8D\u306B\u306F \`SELECT 1 FROM DUAL\` \u3092\u4F7F\u7528\u3057\u307E\u3059\u3002Oracle \u306E\u30AF\u30A8\u30EA\u306B\u306F\u5FC5\u305A \`FROM\` \u7BC0\u304C\u5FC5\u8981\u3067\u3059\u3002
|
|
500
|
+
- \u30B9\u30C6\u30FC\u30C8\u30E1\u30F3\u30C8\u672B\u5C3E\u306B\u30BB\u30DF\u30B3\u30ED\u30F3\u3092\u4ED8\u3051\u306A\u3044\u3067\u304F\u3060\u3055\u3044\uFF08thin \u30C9\u30E9\u30A4\u30D0\u304C\u62D2\u5426\u3057\u307E\u3059\uFF09\u3002`
|
|
501
|
+
},
|
|
502
|
+
tools,
|
|
503
|
+
async checkConnection(params, _config) {
|
|
504
|
+
return checkOracleConnection(params[parameters.jdbcUrl.slug], {
|
|
505
|
+
username: params[parameters.username.slug],
|
|
506
|
+
password: params[parameters.password.slug]
|
|
507
|
+
});
|
|
508
|
+
},
|
|
509
|
+
async query(params, sql, _namedParams) {
|
|
510
|
+
const parsed = parseOracleJdbcUrl(params[parameters.jdbcUrl.slug], {
|
|
511
|
+
username: params[parameters.username.slug],
|
|
512
|
+
password: params[parameters.password.slug]
|
|
513
|
+
});
|
|
514
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
515
|
+
return runOracleQuery(parsed, cleanSql);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// src/connectors/create-connector-sdk.ts
|
|
520
|
+
import { readFileSync } from "fs";
|
|
521
|
+
import path from "path";
|
|
522
|
+
|
|
523
|
+
// src/connector-client/env.ts
|
|
524
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
525
|
+
const envVarName = entry.envVars[key];
|
|
526
|
+
if (!envVarName) {
|
|
527
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
528
|
+
}
|
|
529
|
+
const value = process.env[envVarName];
|
|
530
|
+
if (!value) {
|
|
531
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
532
|
+
}
|
|
533
|
+
return value;
|
|
534
|
+
}
|
|
535
|
+
function resolveEnvVarOptional(entry, key) {
|
|
536
|
+
const envVarName = entry.envVars[key];
|
|
537
|
+
if (!envVarName) return void 0;
|
|
538
|
+
return process.env[envVarName] || void 0;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/connector-client/proxy-fetch.ts
|
|
542
|
+
import { getContext } from "hono/context-storage";
|
|
543
|
+
import { getCookie } from "hono/cookie";
|
|
544
|
+
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
545
|
+
function normalizeHeaders(input) {
|
|
546
|
+
const out = {};
|
|
547
|
+
if (!input) return out;
|
|
548
|
+
new Headers(input).forEach((value, key) => {
|
|
549
|
+
out[key] = value;
|
|
550
|
+
});
|
|
551
|
+
return out;
|
|
552
|
+
}
|
|
553
|
+
function createSandboxProxyFetch(connectionId) {
|
|
554
|
+
return async (input, init) => {
|
|
555
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
556
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
557
|
+
if (!token || !sandboxId) {
|
|
558
|
+
throw new Error(
|
|
559
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
563
|
+
const originalMethod = init?.method ?? "GET";
|
|
564
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
565
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
566
|
+
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
567
|
+
return fetch(proxyUrl, {
|
|
568
|
+
method: "POST",
|
|
569
|
+
headers: {
|
|
570
|
+
"Content-Type": "application/json",
|
|
571
|
+
Authorization: `Bearer ${token}`
|
|
572
|
+
},
|
|
573
|
+
body: JSON.stringify({
|
|
574
|
+
url: originalUrl,
|
|
575
|
+
method: originalMethod,
|
|
576
|
+
headers: normalizeHeaders(init?.headers),
|
|
577
|
+
body: originalBody
|
|
578
|
+
})
|
|
579
|
+
});
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function createDeployedAppProxyFetch(connectionId) {
|
|
583
|
+
const projectId = process.env["SQUADBASE_PROJECT_ID"];
|
|
584
|
+
if (!projectId) {
|
|
585
|
+
throw new Error(
|
|
586
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
590
|
+
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
591
|
+
return async (input, init) => {
|
|
592
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
593
|
+
const originalMethod = init?.method ?? "GET";
|
|
594
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
595
|
+
const c = getContext();
|
|
596
|
+
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
597
|
+
if (!appSession) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
"No authentication method available for connection proxy."
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
return fetch(proxyUrl, {
|
|
603
|
+
method: "POST",
|
|
604
|
+
headers: {
|
|
605
|
+
"Content-Type": "application/json",
|
|
606
|
+
Authorization: `Bearer ${appSession}`
|
|
607
|
+
},
|
|
608
|
+
body: JSON.stringify({
|
|
609
|
+
url: originalUrl,
|
|
610
|
+
method: originalMethod,
|
|
611
|
+
headers: normalizeHeaders(init?.headers),
|
|
612
|
+
body: originalBody
|
|
613
|
+
})
|
|
614
|
+
});
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function createProxyFetch(connectionId) {
|
|
618
|
+
if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
|
|
619
|
+
return createSandboxProxyFetch(connectionId);
|
|
620
|
+
}
|
|
621
|
+
return createDeployedAppProxyFetch(connectionId);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/connectors/create-connector-sdk.ts
|
|
625
|
+
function loadConnectionsSync() {
|
|
626
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
627
|
+
try {
|
|
628
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
629
|
+
return JSON.parse(raw);
|
|
630
|
+
} catch {
|
|
631
|
+
return {};
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
635
|
+
return (connectionId) => {
|
|
636
|
+
const connections = loadConnectionsSync();
|
|
637
|
+
const entry = connections[connectionId];
|
|
638
|
+
if (!entry) {
|
|
639
|
+
throw new Error(
|
|
640
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
const params = {};
|
|
649
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
650
|
+
if (param.required) {
|
|
651
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
652
|
+
} else {
|
|
653
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
654
|
+
if (val !== void 0) params[param.slug] = val;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return createClient2(params, createProxyFetch(connectionId));
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/connectors/entries/oracle.ts
|
|
662
|
+
var connection = createConnectorSdk(oracleConnector, createClient);
|
|
663
|
+
export {
|
|
664
|
+
connection
|
|
665
|
+
};
|