@squadbase/vite-server 0.1.9-dev.b193824 → 0.1.9-dev.e22f810
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 +4989 -433
- package/dist/connectors/asana.js +15 -2
- package/dist/connectors/aws-billing.js +1 -1
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +682 -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 +954 -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 +21521 -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 +689 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +825 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +679 -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 +5066 -510
- package/dist/main.js +4975 -419
- package/dist/vite-plugin.js +4975 -419
- package/package.json +56 -2
|
@@ -0,0 +1,679 @@
|
|
|
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/sqlserver/utils.ts
|
|
46
|
+
var SQLSERVER_PREFIX_RE = /^(?:jdbc:)?sqlserver:\/\//i;
|
|
47
|
+
var TRUE_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes"]);
|
|
48
|
+
var FALSE_VALUES = /* @__PURE__ */ new Set(["false", "0", "no"]);
|
|
49
|
+
function parseBoolean(value) {
|
|
50
|
+
if (value == null) return void 0;
|
|
51
|
+
const lower = value.toLowerCase();
|
|
52
|
+
if (TRUE_VALUES.has(lower)) return true;
|
|
53
|
+
if (FALSE_VALUES.has(lower)) return false;
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
function parseSqlServerJdbcUrl(jdbcUrl, options = {}) {
|
|
57
|
+
const trimmed = jdbcUrl.trim();
|
|
58
|
+
if (!SQLSERVER_PREFIX_RE.test(trimmed)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Unsupported SQL Server URL "${redactSqlServerUrl(trimmed)}". Expected prefix: jdbc:sqlserver:// or sqlserver://.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const withoutPrefix = trimmed.replace(SQLSERVER_PREFIX_RE, "");
|
|
64
|
+
const [hostAndPath, ...propertySegments] = withoutPrefix.split(";");
|
|
65
|
+
const props = {};
|
|
66
|
+
for (const segment of propertySegments) {
|
|
67
|
+
if (!segment) continue;
|
|
68
|
+
const eqIdx = segment.indexOf("=");
|
|
69
|
+
if (eqIdx === -1) continue;
|
|
70
|
+
const key = segment.slice(0, eqIdx).trim().toLowerCase();
|
|
71
|
+
const value = segment.slice(eqIdx + 1).trim();
|
|
72
|
+
if (key) props[key] = value;
|
|
73
|
+
}
|
|
74
|
+
const url = new URL(`mssql://${hostAndPath}`);
|
|
75
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
76
|
+
if (!(key.toLowerCase() in props)) {
|
|
77
|
+
props[key.toLowerCase()] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const server = url.hostname;
|
|
81
|
+
const port = url.port ? Number(url.port) : 1433;
|
|
82
|
+
const pathname = url.pathname.replace(/^\//, "");
|
|
83
|
+
const database = pathname || props["database"] || props["databasename"];
|
|
84
|
+
const user = props["user"] || props["username"] || props["userid"] || options.username;
|
|
85
|
+
const password = props["password"] || options.password;
|
|
86
|
+
return {
|
|
87
|
+
server,
|
|
88
|
+
port,
|
|
89
|
+
database,
|
|
90
|
+
user,
|
|
91
|
+
password,
|
|
92
|
+
options: props
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function toMssqlConfig(parsed, defaults = {}) {
|
|
96
|
+
const encrypt = parseBoolean(parsed.options["encrypt"]) ?? defaults.encrypt ?? false;
|
|
97
|
+
const trustServerCertificate = parseBoolean(parsed.options["trustservercertificate"]) ?? !encrypt;
|
|
98
|
+
return {
|
|
99
|
+
server: parsed.server,
|
|
100
|
+
port: parsed.port,
|
|
101
|
+
database: parsed.database,
|
|
102
|
+
user: parsed.user,
|
|
103
|
+
password: parsed.password,
|
|
104
|
+
connectionTimeout: 1e4,
|
|
105
|
+
requestTimeout: 6e4,
|
|
106
|
+
options: {
|
|
107
|
+
encrypt,
|
|
108
|
+
trustServerCertificate
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function redactSqlServerUrl(jdbcUrl) {
|
|
113
|
+
return jdbcUrl.replace(/(:\/\/)([^@/;]+)@/, "$1***@").replace(/(password\s*=\s*)([^;]+)/gi, "$1***");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ../connectors/src/lib/mssql-runner.ts
|
|
117
|
+
async function importMssql() {
|
|
118
|
+
const mod = await import("mssql");
|
|
119
|
+
return mod.default ?? mod;
|
|
120
|
+
}
|
|
121
|
+
async function runMssqlQuery(parsed, sql, options = {}) {
|
|
122
|
+
const sqlMod = await importMssql();
|
|
123
|
+
const config = toMssqlConfig(parsed, {
|
|
124
|
+
encrypt: options.forceEncrypt
|
|
125
|
+
});
|
|
126
|
+
const pool = new sqlMod.ConnectionPool(config);
|
|
127
|
+
await pool.connect();
|
|
128
|
+
try {
|
|
129
|
+
const result = await pool.request().query(sql);
|
|
130
|
+
const recordset = result.recordset ?? [];
|
|
131
|
+
return { rows: recordset };
|
|
132
|
+
} finally {
|
|
133
|
+
await pool.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function checkMssqlConnection(url, credentials, options = {}) {
|
|
137
|
+
let parsed;
|
|
138
|
+
try {
|
|
139
|
+
parsed = parseSqlServerJdbcUrl(url, credentials);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
error: err instanceof Error ? err.message : String(err)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
await runMssqlQuery(parsed, "SELECT 1 AS one", options);
|
|
148
|
+
return { success: true };
|
|
149
|
+
} catch (err) {
|
|
150
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
151
|
+
msg = msg.replaceAll(url, redactSqlServerUrl(url));
|
|
152
|
+
return { success: false, error: msg };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ../connectors/src/connectors/sqlserver/parameters.ts
|
|
157
|
+
var parameters = {
|
|
158
|
+
jdbcUrl: new ParameterDefinition({
|
|
159
|
+
slug: "jdbc-url",
|
|
160
|
+
name: "SQL Server JDBC URL",
|
|
161
|
+
description: "JDBC-style connection URL for Microsoft SQL Server (e.g. `jdbc:sqlserver://host:1433;database=mydb;encrypt=true;trustServerCertificate=true`). The `jdbc:` prefix may be omitted (`sqlserver://...`).",
|
|
162
|
+
envVarBaseKey: "SQLSERVER_JDBC_URL",
|
|
163
|
+
type: "text",
|
|
164
|
+
secret: true,
|
|
165
|
+
required: true
|
|
166
|
+
}),
|
|
167
|
+
username: new ParameterDefinition({
|
|
168
|
+
slug: "username",
|
|
169
|
+
name: "Username",
|
|
170
|
+
description: "SQL Server login name. Optional when the credentials are embedded in the JDBC URL via `user=...`.",
|
|
171
|
+
envVarBaseKey: "SQLSERVER_USERNAME",
|
|
172
|
+
type: "text",
|
|
173
|
+
secret: false,
|
|
174
|
+
required: false
|
|
175
|
+
}),
|
|
176
|
+
password: new ParameterDefinition({
|
|
177
|
+
slug: "password",
|
|
178
|
+
name: "Password",
|
|
179
|
+
description: "SQL Server password. Optional when the credentials are embedded in the JDBC URL via `password=...`.",
|
|
180
|
+
envVarBaseKey: "SQLSERVER_PASSWORD",
|
|
181
|
+
type: "text",
|
|
182
|
+
secret: true,
|
|
183
|
+
required: false
|
|
184
|
+
})
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ../connectors/src/connectors/sqlserver/sdk/index.ts
|
|
188
|
+
function createClient(params) {
|
|
189
|
+
const jdbcUrl = params[parameters.jdbcUrl.slug];
|
|
190
|
+
if (!jdbcUrl) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`sqlserver: missing required parameter: ${parameters.jdbcUrl.slug}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const username = params[parameters.username.slug];
|
|
196
|
+
const password = params[parameters.password.slug];
|
|
197
|
+
const parsed = parseSqlServerJdbcUrl(jdbcUrl, { username, password });
|
|
198
|
+
async function runQuery(sql) {
|
|
199
|
+
try {
|
|
200
|
+
const { rows } = await runMssqlQuery(parsed, sql);
|
|
201
|
+
return rows;
|
|
202
|
+
} catch (err) {
|
|
203
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
204
|
+
throw new Error(msg.replaceAll(jdbcUrl, redactSqlServerUrl(jdbcUrl)));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { query: runQuery };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../connectors/src/connector-onboarding.ts
|
|
211
|
+
var ConnectorOnboarding = class {
|
|
212
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
213
|
+
connectionSetupInstructions;
|
|
214
|
+
/** Phase 2: Data overview instructions */
|
|
215
|
+
dataOverviewInstructions;
|
|
216
|
+
constructor(config) {
|
|
217
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
218
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
219
|
+
}
|
|
220
|
+
getConnectionSetupPrompt(language) {
|
|
221
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
222
|
+
}
|
|
223
|
+
getDataOverviewInstructions(language) {
|
|
224
|
+
return this.dataOverviewInstructions[language];
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// ../connectors/src/connector-tool.ts
|
|
229
|
+
var ConnectorTool = class {
|
|
230
|
+
name;
|
|
231
|
+
description;
|
|
232
|
+
inputSchema;
|
|
233
|
+
outputSchema;
|
|
234
|
+
_execute;
|
|
235
|
+
constructor(config) {
|
|
236
|
+
this.name = config.name;
|
|
237
|
+
this.description = config.description;
|
|
238
|
+
this.inputSchema = config.inputSchema;
|
|
239
|
+
this.outputSchema = config.outputSchema;
|
|
240
|
+
this._execute = config.execute;
|
|
241
|
+
}
|
|
242
|
+
createTool(connections, config) {
|
|
243
|
+
return {
|
|
244
|
+
description: this.description,
|
|
245
|
+
inputSchema: this.inputSchema,
|
|
246
|
+
outputSchema: this.outputSchema,
|
|
247
|
+
execute: (input) => this._execute(input, connections, config)
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// ../connectors/src/connector-plugin.ts
|
|
253
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
254
|
+
slug;
|
|
255
|
+
authType;
|
|
256
|
+
name;
|
|
257
|
+
description;
|
|
258
|
+
iconUrl;
|
|
259
|
+
parameters;
|
|
260
|
+
releaseFlag;
|
|
261
|
+
proxyPolicy;
|
|
262
|
+
experimentalAttributes;
|
|
263
|
+
categories;
|
|
264
|
+
onboarding;
|
|
265
|
+
systemPrompt;
|
|
266
|
+
tools;
|
|
267
|
+
query;
|
|
268
|
+
checkConnection;
|
|
269
|
+
constructor(config) {
|
|
270
|
+
this.slug = config.slug;
|
|
271
|
+
this.authType = config.authType;
|
|
272
|
+
this.name = config.name;
|
|
273
|
+
this.description = config.description;
|
|
274
|
+
this.iconUrl = config.iconUrl;
|
|
275
|
+
this.parameters = config.parameters;
|
|
276
|
+
this.releaseFlag = config.releaseFlag;
|
|
277
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
278
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
279
|
+
this.categories = config.categories ?? [];
|
|
280
|
+
this.onboarding = config.onboarding;
|
|
281
|
+
this.systemPrompt = config.systemPrompt;
|
|
282
|
+
this.tools = config.tools;
|
|
283
|
+
this.query = config.query;
|
|
284
|
+
this.checkConnection = config.checkConnection;
|
|
285
|
+
}
|
|
286
|
+
get connectorKey() {
|
|
287
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Create tools for connections that belong to this connector.
|
|
291
|
+
* Filters connections by connectorKey internally.
|
|
292
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
293
|
+
*/
|
|
294
|
+
createTools(connections, config, opts) {
|
|
295
|
+
const myConnections = connections.filter(
|
|
296
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
297
|
+
);
|
|
298
|
+
const result = {};
|
|
299
|
+
for (const t of Object.values(this.tools)) {
|
|
300
|
+
const tool = t.createTool(myConnections, config);
|
|
301
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
302
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
303
|
+
...tool,
|
|
304
|
+
toModelOutput: async (options) => {
|
|
305
|
+
if (!originalToModelOutput) {
|
|
306
|
+
return opts.truncateOutput(options.output);
|
|
307
|
+
}
|
|
308
|
+
const modelOutput = await originalToModelOutput(options);
|
|
309
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
310
|
+
return opts.truncateOutput(modelOutput.value);
|
|
311
|
+
}
|
|
312
|
+
return modelOutput;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
static deriveKey(slug, authType) {
|
|
319
|
+
if (authType) return `${slug}-${authType}`;
|
|
320
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
321
|
+
// user-password
|
|
322
|
+
"postgresql": "user-password",
|
|
323
|
+
"mysql": "user-password",
|
|
324
|
+
"clickhouse": "user-password",
|
|
325
|
+
"kintone": "user-password",
|
|
326
|
+
"squadbase-db": "user-password",
|
|
327
|
+
// service-account
|
|
328
|
+
"snowflake": "service-account",
|
|
329
|
+
"bigquery": "service-account",
|
|
330
|
+
"google-analytics": "service-account",
|
|
331
|
+
"google-calendar": "service-account",
|
|
332
|
+
"aws-athena": "service-account",
|
|
333
|
+
"redshift": "service-account",
|
|
334
|
+
// api-key
|
|
335
|
+
"databricks": "api-key",
|
|
336
|
+
"dbt": "api-key",
|
|
337
|
+
"airtable": "api-key",
|
|
338
|
+
"openai": "api-key",
|
|
339
|
+
"gemini": "api-key",
|
|
340
|
+
"anthropic": "api-key",
|
|
341
|
+
"wix-store": "api-key"
|
|
342
|
+
};
|
|
343
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
344
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
345
|
+
return slug;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// ../connectors/src/auth-types.ts
|
|
350
|
+
var AUTH_TYPES = {
|
|
351
|
+
OAUTH: "oauth",
|
|
352
|
+
API_KEY: "api-key",
|
|
353
|
+
JWT: "jwt",
|
|
354
|
+
SERVICE_ACCOUNT: "service-account",
|
|
355
|
+
PAT: "pat",
|
|
356
|
+
USER_PASSWORD: "user-password"
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// ../connectors/src/lib/unwrap-sample-limit.ts
|
|
360
|
+
var STRICT_WRAPPER_RE = /^\s*SELECT\s+\*\s+FROM\s+\(([\s\S]+)\)\s+AS\s+\w+\s+LIMIT\s+(\d+)\s*;?\s*$/i;
|
|
361
|
+
var TRAILING_LIMIT_RE = /\s+LIMIT\s+(\d+)\s*;?\s*$/i;
|
|
362
|
+
function unwrapSampleLimit(sql) {
|
|
363
|
+
const strict = sql.match(STRICT_WRAPPER_RE);
|
|
364
|
+
if (strict) {
|
|
365
|
+
return { inner: strict[1].trim(), limit: Number.parseInt(strict[2], 10) };
|
|
366
|
+
}
|
|
367
|
+
const trailing = sql.match(TRAILING_LIMIT_RE);
|
|
368
|
+
if (!trailing) return null;
|
|
369
|
+
if (!/^\s*SELECT\s+\*\s+FROM\s*\(/i.test(sql)) return null;
|
|
370
|
+
const limit = Number.parseInt(trailing[1], 10);
|
|
371
|
+
const inner = sql.slice(0, sql.length - trailing[0].length).trim();
|
|
372
|
+
return { inner, limit };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ../connectors/src/connectors/sqlserver/setup.ts
|
|
376
|
+
var sqlserverOnboarding = new ConnectorOnboarding({
|
|
377
|
+
dataOverviewInstructions: {
|
|
378
|
+
en: `1. Use sqlserver_executeQuery to discover the database flavor and version: \`SELECT @@VERSION\`
|
|
379
|
+
2. List user tables: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')\`
|
|
380
|
+
3. For key tables, fetch column info: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'xxx'\`
|
|
381
|
+
4. Sample up to 3 tables. SQL Server uses \`TOP\` rather than \`LIMIT\`: \`SELECT TOP 5 * FROM <schema>.<table_name>\``,
|
|
382
|
+
ja: `1. sqlserver_executeQuery \u3067\u30D0\u30FC\u30B8\u30E7\u30F3\u60C5\u5831\u3092\u53D6\u5F97: \`SELECT @@VERSION\`
|
|
383
|
+
2. \u30E6\u30FC\u30B6\u30FC\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')\`
|
|
384
|
+
3. \u4E3B\u8981\u30C6\u30FC\u30D6\u30EB\u306E\u30AB\u30E9\u30E0\u60C5\u5831\u3092\u53D6\u5F97: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'xxx'\`
|
|
385
|
+
4. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u6700\u59273\u30C6\u30FC\u30D6\u30EB\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3002SQL Server \u3067\u306F \`LIMIT\` \u3067\u306F\u306A\u304F \`TOP\` \u3092\u4F7F\u7528: \`SELECT TOP 5 * FROM <schema>.<table_name>\``
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// ../connectors/src/connectors/sqlserver/tools/execute-query.ts
|
|
390
|
+
import { z } from "zod";
|
|
391
|
+
var MAX_ROWS = 500;
|
|
392
|
+
var inputSchema = z.object({
|
|
393
|
+
toolUseIntent: z.string().optional().describe(
|
|
394
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
395
|
+
),
|
|
396
|
+
connectionId: z.string().describe("ID of the SQL Server connection to use"),
|
|
397
|
+
sql: z.string().describe(
|
|
398
|
+
"T-SQL query to execute. Use SQL Server syntax (e.g. `TOP n`, square-bracket identifiers). Always include `TOP` to bound result size."
|
|
399
|
+
)
|
|
400
|
+
});
|
|
401
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
402
|
+
z.object({
|
|
403
|
+
success: z.literal(true),
|
|
404
|
+
rowCount: z.number(),
|
|
405
|
+
truncated: z.boolean(),
|
|
406
|
+
rows: z.array(z.record(z.string(), z.unknown()))
|
|
407
|
+
}),
|
|
408
|
+
z.object({
|
|
409
|
+
success: z.literal(false),
|
|
410
|
+
error: z.string()
|
|
411
|
+
})
|
|
412
|
+
]);
|
|
413
|
+
var executeQueryTool = new ConnectorTool({
|
|
414
|
+
name: "executeQuery",
|
|
415
|
+
description: `Execute a T-SQL query against Microsoft SQL Server. Returns up to ${MAX_ROWS} rows.
|
|
416
|
+
Use for: schema exploration via \`INFORMATION_SCHEMA\`, data sampling, and analytical queries.
|
|
417
|
+
SQL Server uses \`TOP n\` instead of \`LIMIT n\`. Identifiers can be wrapped in square brackets (\`[schema].[table]\`).
|
|
418
|
+
Avoid loading large amounts of data; always include \`TOP\` in queries.`,
|
|
419
|
+
inputSchema,
|
|
420
|
+
outputSchema,
|
|
421
|
+
async execute({ connectionId, sql }, connections) {
|
|
422
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
423
|
+
if (!connection2) {
|
|
424
|
+
return {
|
|
425
|
+
success: false,
|
|
426
|
+
error: `Connection ${connectionId} not found`
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
const jdbcUrl = parameters.jdbcUrl.getValue(connection2);
|
|
430
|
+
const username = parameters.username.tryGetValue(connection2);
|
|
431
|
+
const password = parameters.password.tryGetValue(connection2);
|
|
432
|
+
console.log(
|
|
433
|
+
`[connector-query] sqlserver/${connection2.name} (${redactSqlServerUrl(jdbcUrl)}): ${sql}`
|
|
434
|
+
);
|
|
435
|
+
let parsed;
|
|
436
|
+
try {
|
|
437
|
+
parsed = parseSqlServerJdbcUrl(jdbcUrl, { username, password });
|
|
438
|
+
} catch (err) {
|
|
439
|
+
return {
|
|
440
|
+
success: false,
|
|
441
|
+
error: err instanceof Error ? err.message : String(err)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const { rows } = await runMssqlQuery(parsed, sql);
|
|
446
|
+
const truncated = rows.length > MAX_ROWS;
|
|
447
|
+
return {
|
|
448
|
+
success: true,
|
|
449
|
+
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
450
|
+
truncated,
|
|
451
|
+
rows: rows.slice(0, MAX_ROWS)
|
|
452
|
+
};
|
|
453
|
+
} catch (err) {
|
|
454
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
455
|
+
msg = msg.replaceAll(jdbcUrl, redactSqlServerUrl(jdbcUrl));
|
|
456
|
+
return { success: false, error: msg };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// ../connectors/src/connectors/sqlserver/index.ts
|
|
462
|
+
var tools = { executeQuery: executeQueryTool };
|
|
463
|
+
var sqlserverConnector = new ConnectorPlugin({
|
|
464
|
+
slug: "sqlserver",
|
|
465
|
+
authType: AUTH_TYPES.USER_PASSWORD,
|
|
466
|
+
name: "SQL Server",
|
|
467
|
+
description: "Connect to Microsoft SQL Server (on-premises or self-hosted) using a JDBC-style URL.",
|
|
468
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/13vQdDz2QJdWX8PPM646pz/cde512898dd32c016ee940f4b37b13bb/sql-server-icon.svg",
|
|
469
|
+
parameters,
|
|
470
|
+
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
471
|
+
categories: ["database"],
|
|
472
|
+
onboarding: sqlserverOnboarding,
|
|
473
|
+
systemPrompt: {
|
|
474
|
+
en: `### Tools
|
|
475
|
+
|
|
476
|
+
- \`sqlserver_executeQuery\`: Executes a T-SQL query against Microsoft SQL Server and returns rows. Use this for schema exploration via \`INFORMATION_SCHEMA\` and for sampling data. See the SQL Reference below for SQL Server-specific syntax.
|
|
477
|
+
|
|
478
|
+
### Business Logic
|
|
479
|
+
|
|
480
|
+
The business logic type for this connector is "sql".
|
|
481
|
+
|
|
482
|
+
### SQL Reference
|
|
483
|
+
- Schema layout: user objects live by default in the \`dbo\` schema. System objects live in \`sys\` and \`INFORMATION_SCHEMA\`.
|
|
484
|
+
- Schema exploration:
|
|
485
|
+
- List user tables: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')\`
|
|
486
|
+
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'xxx'\`
|
|
487
|
+
- Use \`TOP n\` instead of \`LIMIT n\` (e.g. \`SELECT TOP 100 * FROM ...\`).
|
|
488
|
+
- Identifiers can be quoted with square brackets, e.g. \`[Order Details]\`.
|
|
489
|
+
- Always bound results with \`TOP\` in queries.
|
|
490
|
+
- Row-limit compatibility: the platform's server-logic schema inference may wrap your query as \`SELECT * FROM (<inner>) AS _sq LIMIT N\`. T-SQL does not understand \`LIMIT\`, so the connector detects this exact wrapper at \`query()\` time, executes \`<inner>\` directly, and slices the first N rows in JS. You do not need to handle this \u2014 but do not author your own \`LIMIT\` clauses; use \`TOP\` / \`OFFSET ... FETCH NEXT\` in queries you write.`,
|
|
491
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
492
|
+
|
|
493
|
+
- \`sqlserver_executeQuery\`: Microsoft SQL Server \u306B\u5BFE\u3057\u3066 T-SQL \u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\`INFORMATION_SCHEMA\` \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\u3002SQL Server \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
|
|
494
|
+
|
|
495
|
+
### Business Logic
|
|
496
|
+
|
|
497
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "sql" \u3067\u3059\u3002
|
|
498
|
+
|
|
499
|
+
### SQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
500
|
+
- \u30B9\u30AD\u30FC\u30DE\u69CB\u6210: \u30E6\u30FC\u30B6\u30FC\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306F\u30C7\u30D5\u30A9\u30EB\u30C8\u3067 \`dbo\` \u30B9\u30AD\u30FC\u30DE\u306B\u914D\u7F6E\u3055\u308C\u307E\u3059\u3002\u30B7\u30B9\u30C6\u30E0\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306F \`sys\` \u304A\u3088\u3073 \`INFORMATION_SCHEMA\` \u306B\u5B58\u5728\u3057\u307E\u3059\u3002
|
|
501
|
+
- \u30B9\u30AD\u30FC\u30DE\u63A2\u7D22:
|
|
502
|
+
- \u30E6\u30FC\u30B6\u30FC\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')\`
|
|
503
|
+
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'xxx'\`
|
|
504
|
+
- \`LIMIT n\` \u3067\u306F\u306A\u304F \`TOP n\` \u3092\u4F7F\u7528\u3057\u307E\u3059\uFF08\u4F8B: \`SELECT TOP 100 * FROM ...\`\uFF09\u3002
|
|
505
|
+
- \u8B58\u5225\u5B50\u306F\u89D2\u62EC\u5F27\u3067\u56F2\u3081\u307E\u3059\uFF08\u4F8B: \`[Order Details]\`\uFF09\u3002
|
|
506
|
+
- \u7D50\u679C\u4EF6\u6570\u306F\u5FC5\u305A \`TOP\` \u3067\u5236\u9650\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
507
|
+
- \u884C\u6570\u5236\u9650\u306E\u4E92\u63DB\u6027: \u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u306E server-logic \u30B9\u30AD\u30FC\u30DE\u63A8\u8AD6\u306F\u3001\u30AF\u30A8\u30EA\u3092 \`SELECT * FROM (<inner>) AS _sq LIMIT N\` \u306E\u5F62\u3067\u30E9\u30C3\u30D7\u3057\u3066\u304F\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u3002T-SQL \u306B\u306F \`LIMIT\` \u304C\u7121\u3044\u305F\u3081\u3001\u30B3\u30CD\u30AF\u30BF\u306F \`query()\` \u5185\u3067\u3053\u306E\u30E9\u30C3\u30D1\u3092\u691C\u51FA\u3057\u3001\`<inner>\` \u3092\u305D\u306E\u307E\u307E\u5B9F\u884C\u3057\u3066 JS \u5074\u3067\u5148\u982D N \u884C\u306B\u5207\u308A\u8A70\u3081\u307E\u3059\u3002\u5229\u7528\u8005\u5074\u3067\u5BFE\u51E6\u3059\u308B\u5FC5\u8981\u306F\u3042\u308A\u307E\u305B\u3093\u304C\u3001\u81EA\u5206\u3067\u66F8\u304F SQL \u3067\u306F \`LIMIT\` \u3092\u4F7F\u308F\u305A \`TOP\` / \`OFFSET ... FETCH NEXT\` \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
508
|
+
},
|
|
509
|
+
tools,
|
|
510
|
+
async checkConnection(params, _config) {
|
|
511
|
+
return checkMssqlConnection(
|
|
512
|
+
params[parameters.jdbcUrl.slug],
|
|
513
|
+
{
|
|
514
|
+
username: params[parameters.username.slug],
|
|
515
|
+
password: params[parameters.password.slug]
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
},
|
|
519
|
+
async query(params, sql, _namedParams) {
|
|
520
|
+
const parsed = parseSqlServerJdbcUrl(params[parameters.jdbcUrl.slug], {
|
|
521
|
+
username: params[parameters.username.slug],
|
|
522
|
+
password: params[parameters.password.slug]
|
|
523
|
+
});
|
|
524
|
+
const sample = unwrapSampleLimit(sql);
|
|
525
|
+
if (sample) {
|
|
526
|
+
const result = await runMssqlQuery(parsed, sample.inner);
|
|
527
|
+
return { rows: result.rows.slice(0, sample.limit) };
|
|
528
|
+
}
|
|
529
|
+
return runMssqlQuery(parsed, sql);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// src/connectors/create-connector-sdk.ts
|
|
534
|
+
import { readFileSync } from "fs";
|
|
535
|
+
import path from "path";
|
|
536
|
+
|
|
537
|
+
// src/connector-client/env.ts
|
|
538
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
539
|
+
const envVarName = entry.envVars[key];
|
|
540
|
+
if (!envVarName) {
|
|
541
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
542
|
+
}
|
|
543
|
+
const value = process.env[envVarName];
|
|
544
|
+
if (!value) {
|
|
545
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
546
|
+
}
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
function resolveEnvVarOptional(entry, key) {
|
|
550
|
+
const envVarName = entry.envVars[key];
|
|
551
|
+
if (!envVarName) return void 0;
|
|
552
|
+
return process.env[envVarName] || void 0;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/connector-client/proxy-fetch.ts
|
|
556
|
+
import { getContext } from "hono/context-storage";
|
|
557
|
+
import { getCookie } from "hono/cookie";
|
|
558
|
+
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
559
|
+
function normalizeHeaders(input) {
|
|
560
|
+
const out = {};
|
|
561
|
+
if (!input) return out;
|
|
562
|
+
new Headers(input).forEach((value, key) => {
|
|
563
|
+
out[key] = value;
|
|
564
|
+
});
|
|
565
|
+
return out;
|
|
566
|
+
}
|
|
567
|
+
function createSandboxProxyFetch(connectionId) {
|
|
568
|
+
return async (input, init) => {
|
|
569
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
570
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
571
|
+
if (!token || !sandboxId) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
577
|
+
const originalMethod = init?.method ?? "GET";
|
|
578
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
579
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
580
|
+
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
581
|
+
return fetch(proxyUrl, {
|
|
582
|
+
method: "POST",
|
|
583
|
+
headers: {
|
|
584
|
+
"Content-Type": "application/json",
|
|
585
|
+
Authorization: `Bearer ${token}`
|
|
586
|
+
},
|
|
587
|
+
body: JSON.stringify({
|
|
588
|
+
url: originalUrl,
|
|
589
|
+
method: originalMethod,
|
|
590
|
+
headers: normalizeHeaders(init?.headers),
|
|
591
|
+
body: originalBody
|
|
592
|
+
})
|
|
593
|
+
});
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function createDeployedAppProxyFetch(connectionId) {
|
|
597
|
+
const projectId = process.env["SQUADBASE_PROJECT_ID"];
|
|
598
|
+
if (!projectId) {
|
|
599
|
+
throw new Error(
|
|
600
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
604
|
+
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
605
|
+
return async (input, init) => {
|
|
606
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
607
|
+
const originalMethod = init?.method ?? "GET";
|
|
608
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
609
|
+
const c = getContext();
|
|
610
|
+
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
611
|
+
if (!appSession) {
|
|
612
|
+
throw new Error(
|
|
613
|
+
"No authentication method available for connection proxy."
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
return fetch(proxyUrl, {
|
|
617
|
+
method: "POST",
|
|
618
|
+
headers: {
|
|
619
|
+
"Content-Type": "application/json",
|
|
620
|
+
Authorization: `Bearer ${appSession}`
|
|
621
|
+
},
|
|
622
|
+
body: JSON.stringify({
|
|
623
|
+
url: originalUrl,
|
|
624
|
+
method: originalMethod,
|
|
625
|
+
headers: normalizeHeaders(init?.headers),
|
|
626
|
+
body: originalBody
|
|
627
|
+
})
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function createProxyFetch(connectionId) {
|
|
632
|
+
if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
|
|
633
|
+
return createSandboxProxyFetch(connectionId);
|
|
634
|
+
}
|
|
635
|
+
return createDeployedAppProxyFetch(connectionId);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/connectors/create-connector-sdk.ts
|
|
639
|
+
function loadConnectionsSync() {
|
|
640
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
641
|
+
try {
|
|
642
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
643
|
+
return JSON.parse(raw);
|
|
644
|
+
} catch {
|
|
645
|
+
return {};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
649
|
+
return (connectionId) => {
|
|
650
|
+
const connections = loadConnectionsSync();
|
|
651
|
+
const entry = connections[connectionId];
|
|
652
|
+
if (!entry) {
|
|
653
|
+
throw new Error(
|
|
654
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
const params = {};
|
|
663
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
664
|
+
if (param.required) {
|
|
665
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
666
|
+
} else {
|
|
667
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
668
|
+
if (val !== void 0) params[param.slug] = val;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return createClient2(params, createProxyFetch(connectionId));
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// src/connectors/entries/sqlserver.ts
|
|
676
|
+
var connection = createConnectorSdk(sqlserverConnector, createClient);
|
|
677
|
+
export {
|
|
678
|
+
connection
|
|
679
|
+
};
|