@squadbase/vite-server 0.1.5-dev.0 → 0.1.6

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.
@@ -0,0 +1,767 @@
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/influxdb/parameters.ts
46
+ var parameters = {
47
+ url: new ParameterDefinition({
48
+ slug: "url",
49
+ name: "InfluxDB URL",
50
+ description: "The base URL of your InfluxDB instance (e.g., 'https://us-east-1-1.aws.cloud2.influxdata.com' for InfluxDB Cloud). Do not include a trailing slash.",
51
+ envVarBaseKey: "INFLUXDB_URL",
52
+ type: "text",
53
+ secret: false,
54
+ required: true
55
+ }),
56
+ token: new ParameterDefinition({
57
+ slug: "token",
58
+ name: "API Token",
59
+ description: "The API token used to authenticate against your InfluxDB instance. Generate one from the InfluxDB UI (Load Data \u2192 API Tokens).",
60
+ envVarBaseKey: "INFLUXDB_TOKEN",
61
+ type: "text",
62
+ secret: true,
63
+ required: true
64
+ }),
65
+ database: new ParameterDefinition({
66
+ slug: "database",
67
+ name: "Database",
68
+ description: "The default database to query. For InfluxDB 3 this is the database name; for InfluxDB 2 use the bucket name.",
69
+ envVarBaseKey: "INFLUXDB_DATABASE",
70
+ type: "text",
71
+ secret: false,
72
+ required: true
73
+ }),
74
+ org: new ParameterDefinition({
75
+ slug: "org",
76
+ name: "Organization",
77
+ description: "The InfluxDB organization name. Required for InfluxDB 2.x Flux queries and writes; optional for InfluxDB 3.",
78
+ envVarBaseKey: "INFLUXDB_ORG",
79
+ type: "text",
80
+ secret: false,
81
+ required: false
82
+ })
83
+ };
84
+
85
+ // ../connectors/src/connectors/influxdb/sdk/index.ts
86
+ function createClient(params) {
87
+ const url = (params[parameters.url.slug] ?? "").replace(/\/$/, "");
88
+ const token = params[parameters.token.slug];
89
+ const database = params[parameters.database.slug];
90
+ const org = params[parameters.org.slug];
91
+ if (!url) {
92
+ throw new Error(
93
+ `influxdb: missing required parameter: ${parameters.url.slug}`
94
+ );
95
+ }
96
+ if (!token) {
97
+ throw new Error(
98
+ `influxdb: missing required parameter: ${parameters.token.slug}`
99
+ );
100
+ }
101
+ if (!database) {
102
+ throw new Error(
103
+ `influxdb: missing required parameter: ${parameters.database.slug}`
104
+ );
105
+ }
106
+ function authHeaders(extra) {
107
+ const headers = new Headers(extra);
108
+ headers.set("Authorization", `Token ${token}`);
109
+ if (!headers.has("Accept")) headers.set("Accept", "application/json");
110
+ return headers;
111
+ }
112
+ async function assertOk(res, label) {
113
+ if (!res.ok) {
114
+ const body = await res.text().catch(() => "(unreadable body)");
115
+ throw new Error(
116
+ `influxdb ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
117
+ );
118
+ }
119
+ }
120
+ return {
121
+ database,
122
+ org,
123
+ request(path2, init) {
124
+ const fullUrl = `${url}${path2.startsWith("/") ? "" : "/"}${path2}`;
125
+ const headers = new Headers(init?.headers);
126
+ headers.set("Authorization", `Token ${token}`);
127
+ if (!headers.has("Accept")) headers.set("Accept", "application/json");
128
+ return fetch(fullUrl, { ...init, headers });
129
+ },
130
+ async querySql(sql, options) {
131
+ const res = await fetch(`${url}/api/v3/query_sql`, {
132
+ method: "POST",
133
+ headers: authHeaders({ "Content-Type": "application/json" }),
134
+ body: JSON.stringify({
135
+ db: options?.database ?? database,
136
+ q: sql
137
+ })
138
+ });
139
+ await assertOk(res, "querySql");
140
+ return await res.json();
141
+ },
142
+ async queryInfluxql(influxql, options) {
143
+ const res = await fetch(`${url}/api/v3/query_influxql`, {
144
+ method: "POST",
145
+ headers: authHeaders({ "Content-Type": "application/json" }),
146
+ body: JSON.stringify({
147
+ db: options?.database ?? database,
148
+ q: influxql
149
+ })
150
+ });
151
+ await assertOk(res, "queryInfluxql");
152
+ return await res.json();
153
+ },
154
+ async queryFlux(flux, options) {
155
+ const resolvedOrg = options?.org ?? org;
156
+ if (!resolvedOrg) {
157
+ throw new Error(
158
+ `influxdb queryFlux: 'org' parameter is required for Flux queries but was not configured`
159
+ );
160
+ }
161
+ const res = await fetch(
162
+ `${url}/api/v2/query?org=${encodeURIComponent(resolvedOrg)}`,
163
+ {
164
+ method: "POST",
165
+ headers: authHeaders({
166
+ "Content-Type": "application/vnd.flux",
167
+ Accept: "application/csv"
168
+ }),
169
+ body: flux
170
+ }
171
+ );
172
+ await assertOk(res, "queryFlux");
173
+ return await res.text();
174
+ },
175
+ async writeLineProtocol(lineProtocol, options) {
176
+ const resolvedDatabase = options?.database ?? database;
177
+ const resolvedOrg = options?.org ?? org;
178
+ const precision = options?.precision ?? "ns";
179
+ const endpoint = resolvedOrg ? `${url}/api/v2/write?org=${encodeURIComponent(resolvedOrg)}&bucket=${encodeURIComponent(resolvedDatabase)}&precision=${precision}` : `${url}/api/v3/write_lp?db=${encodeURIComponent(resolvedDatabase)}&precision=${precision}`;
180
+ const res = await fetch(endpoint, {
181
+ method: "POST",
182
+ headers: authHeaders({ "Content-Type": "text/plain; charset=utf-8" }),
183
+ body: lineProtocol
184
+ });
185
+ await assertOk(res, "writeLineProtocol");
186
+ }
187
+ };
188
+ }
189
+
190
+ // ../connectors/src/connector-onboarding.ts
191
+ var ConnectorOnboarding = class {
192
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
193
+ connectionSetupInstructions;
194
+ /** Phase 2: Data overview instructions */
195
+ dataOverviewInstructions;
196
+ constructor(config) {
197
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
198
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
199
+ }
200
+ getConnectionSetupPrompt(language) {
201
+ return this.connectionSetupInstructions?.[language] ?? null;
202
+ }
203
+ getDataOverviewInstructions(language) {
204
+ return this.dataOverviewInstructions[language];
205
+ }
206
+ };
207
+
208
+ // ../connectors/src/connector-tool.ts
209
+ var ConnectorTool = class {
210
+ name;
211
+ description;
212
+ inputSchema;
213
+ outputSchema;
214
+ _execute;
215
+ constructor(config) {
216
+ this.name = config.name;
217
+ this.description = config.description;
218
+ this.inputSchema = config.inputSchema;
219
+ this.outputSchema = config.outputSchema;
220
+ this._execute = config.execute;
221
+ }
222
+ createTool(connections, config) {
223
+ return {
224
+ description: this.description,
225
+ inputSchema: this.inputSchema,
226
+ outputSchema: this.outputSchema,
227
+ execute: (input) => this._execute(input, connections, config)
228
+ };
229
+ }
230
+ };
231
+
232
+ // ../connectors/src/connector-plugin.ts
233
+ var ConnectorPlugin = class _ConnectorPlugin {
234
+ slug;
235
+ authType;
236
+ name;
237
+ description;
238
+ iconUrl;
239
+ parameters;
240
+ releaseFlag;
241
+ proxyPolicy;
242
+ experimentalAttributes;
243
+ onboarding;
244
+ systemPrompt;
245
+ tools;
246
+ query;
247
+ checkConnection;
248
+ constructor(config) {
249
+ this.slug = config.slug;
250
+ this.authType = config.authType;
251
+ this.name = config.name;
252
+ this.description = config.description;
253
+ this.iconUrl = config.iconUrl;
254
+ this.parameters = config.parameters;
255
+ this.releaseFlag = config.releaseFlag;
256
+ this.proxyPolicy = config.proxyPolicy;
257
+ this.experimentalAttributes = config.experimentalAttributes;
258
+ this.onboarding = config.onboarding;
259
+ this.systemPrompt = config.systemPrompt;
260
+ this.tools = config.tools;
261
+ this.query = config.query;
262
+ this.checkConnection = config.checkConnection;
263
+ }
264
+ get connectorKey() {
265
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
266
+ }
267
+ /**
268
+ * Create tools for connections that belong to this connector.
269
+ * Filters connections by connectorKey internally.
270
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
271
+ */
272
+ createTools(connections, config, opts) {
273
+ const myConnections = connections.filter(
274
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
275
+ );
276
+ const result = {};
277
+ for (const t of Object.values(this.tools)) {
278
+ const tool = t.createTool(myConnections, config);
279
+ const originalToModelOutput = tool.toModelOutput;
280
+ result[`${this.connectorKey}_${t.name}`] = {
281
+ ...tool,
282
+ toModelOutput: async (options) => {
283
+ if (!originalToModelOutput) {
284
+ return opts.truncateOutput(options.output);
285
+ }
286
+ const modelOutput = await originalToModelOutput(options);
287
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
288
+ return opts.truncateOutput(modelOutput.value);
289
+ }
290
+ return modelOutput;
291
+ }
292
+ };
293
+ }
294
+ return result;
295
+ }
296
+ static deriveKey(slug, authType) {
297
+ if (authType) return `${slug}-${authType}`;
298
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
299
+ // user-password
300
+ "postgresql": "user-password",
301
+ "mysql": "user-password",
302
+ "clickhouse": "user-password",
303
+ "kintone": "user-password",
304
+ "squadbase-db": "user-password",
305
+ // service-account
306
+ "snowflake": "service-account",
307
+ "bigquery": "service-account",
308
+ "google-analytics": "service-account",
309
+ "google-calendar": "service-account",
310
+ "aws-athena": "service-account",
311
+ "redshift": "service-account",
312
+ // api-key
313
+ "databricks": "api-key",
314
+ "dbt": "api-key",
315
+ "airtable": "api-key",
316
+ "openai": "api-key",
317
+ "gemini": "api-key",
318
+ "anthropic": "api-key",
319
+ "wix-store": "api-key"
320
+ };
321
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
322
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
323
+ return slug;
324
+ }
325
+ };
326
+
327
+ // ../connectors/src/auth-types.ts
328
+ var AUTH_TYPES = {
329
+ OAUTH: "oauth",
330
+ API_KEY: "api-key",
331
+ JWT: "jwt",
332
+ SERVICE_ACCOUNT: "service-account",
333
+ PAT: "pat",
334
+ USER_PASSWORD: "user-password"
335
+ };
336
+
337
+ // ../connectors/src/connectors/influxdb/setup.ts
338
+ var influxdbOnboarding = new ConnectorOnboarding({
339
+ connectionSetupInstructions: {
340
+ en: `#### Generate an API Token
341
+ 1. Sign in to your InfluxDB Cloud / OSS instance
342
+ 2. Go to Load Data \u2192 API Tokens \u2192 Generate API Token
343
+ 3. Create an All-Access or Custom token with at least read permission on your target database/bucket
344
+ 4. Copy the token into the API Token parameter
345
+
346
+ #### Confirm the Database (or Bucket) Name
347
+ - InfluxDB 3: use the database name
348
+ - InfluxDB 2: use the bucket name (buckets act as databases in the v1/v3 compatibility endpoints)
349
+
350
+ #### Organization (InfluxDB 2 only)
351
+ - If you are on InfluxDB 2, set the Organization parameter to the org that owns the bucket
352
+ - For InfluxDB 3 Cloud you can leave Organization blank`,
353
+ ja: `#### API \u30C8\u30FC\u30AF\u30F3\u306E\u767A\u884C
354
+ 1. InfluxDB Cloud / OSS \u306B\u30B5\u30A4\u30F3\u30A4\u30F3
355
+ 2. Load Data \u2192 API Tokens \u2192 Generate API Token
356
+ 3. \u5BFE\u8C61\u306E database / bucket \u306B\u5BFE\u3057\u3066\u6700\u4F4E\u9650 read \u6A29\u9650\u3092\u6301\u3064 All-Access \u307E\u305F\u306F Custom \u30C8\u30FC\u30AF\u30F3\u3092\u4F5C\u6210
357
+ 4. \u767A\u884C\u3057\u305F\u30C8\u30FC\u30AF\u30F3\u3092 API Token \u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u8CBC\u308A\u4ED8\u3051
358
+
359
+ #### Database (\u307E\u305F\u306F Bucket) \u540D\u306E\u78BA\u8A8D
360
+ - InfluxDB 3: database \u540D\u3092\u4F7F\u7528
361
+ - InfluxDB 2: bucket \u540D\u3092\u4F7F\u7528\uFF08v1/v3 \u4E92\u63DB\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067\u306F bucket \u304C database \u3068\u3057\u3066\u6271\u308F\u308C\u308B\uFF09
362
+
363
+ #### Organization\uFF08InfluxDB 2 \u306E\u307F\uFF09
364
+ - InfluxDB 2 \u306E\u5834\u5408\u3001bucket \u3092\u4FDD\u6709\u3059\u308B\u7D44\u7E54\u540D\u3092 Organization \u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u8A2D\u5B9A
365
+ - InfluxDB 3 Cloud \u306E\u5834\u5408\u306F\u7A7A\u306E\u307E\u307E\u3067\u554F\u984C\u306A\u3044`
366
+ },
367
+ dataOverviewInstructions: {
368
+ en: `1. For InfluxDB 3: call influxdb_request with POST /api/v3/query_sql, body { "db": "<database>", "q": "SHOW TABLES" } to list measurements
369
+ 2. For InfluxDB 3: inspect a sample measurement with POST /api/v3/query_sql, body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" }
370
+ 3. For InfluxDB 2: call influxdb_request with POST /api/v2/query?org=<org>, contentType 'application/vnd.flux', body 'buckets()' to list buckets, then 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' to inspect data`,
371
+ ja: `1. InfluxDB 3 \u306E\u5834\u5408: influxdb_request \u3067 POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SHOW TABLES" } \u3092\u5B9F\u884C\u3057 measurement \u4E00\u89A7\u3092\u53D6\u5F97
372
+ 2. InfluxDB 3 \u306E\u5834\u5408: POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" } \u3067\u4EE3\u8868\u7684\u306A measurement \u306E\u69CB\u9020\u3092\u78BA\u8A8D
373
+ 3. InfluxDB 2 \u306E\u5834\u5408: POST /api/v2/query?org=<org>\u3001contentType 'application/vnd.flux'\u3001body 'buckets()' \u3067 bucket \u4E00\u89A7\u3092\u53D6\u5F97\u3057\u3001\u7D9A\u3044\u3066 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' \u3067\u30C7\u30FC\u30BF\u3092\u78BA\u8A8D`
374
+ }
375
+ });
376
+
377
+ // ../connectors/src/connectors/influxdb/tools/request.ts
378
+ import { z } from "zod";
379
+ var REQUEST_TIMEOUT_MS = 6e4;
380
+ var inputSchema = z.object({
381
+ toolUseIntent: z.string().optional().describe(
382
+ "Brief description of what you intend to accomplish with this tool call"
383
+ ),
384
+ connectionId: z.string().describe("ID of the InfluxDB connection to use"),
385
+ method: z.enum(["GET", "POST", "DELETE"]).describe(
386
+ "HTTP method. POST for SQL/InfluxQL/Flux queries and writes, GET for metadata endpoints, DELETE for delete endpoints."
387
+ ),
388
+ path: z.string().describe(
389
+ "API path appended to the InfluxDB base URL. Use '/api/v3/query_sql' for InfluxDB 3 SQL, '/api/v3/query_influxql' for InfluxDB 3 InfluxQL, '/api/v2/query?org={org}' for InfluxDB 2 Flux, '/api/v2/write?org={org}&bucket={bucket}' for writes."
390
+ ),
391
+ body: z.union([z.record(z.string(), z.unknown()), z.string()]).optional().describe(
392
+ "Request body. JSON object for query endpoints ({ db, q } for InfluxDB 3 SQL; { query, type } for v2 query); raw string for Flux queries and line-protocol writes."
393
+ ),
394
+ contentType: z.string().optional().describe(
395
+ "Content-Type header override. Use 'application/vnd.flux' for Flux queries and 'text/plain; charset=utf-8' for line-protocol writes. Defaults to 'application/json'."
396
+ )
397
+ });
398
+ var outputSchema = z.discriminatedUnion("success", [
399
+ z.object({
400
+ success: z.literal(true),
401
+ status: z.number(),
402
+ data: z.unknown()
403
+ }),
404
+ z.object({
405
+ success: z.literal(false),
406
+ error: z.string()
407
+ })
408
+ ]);
409
+ var requestTool = new ConnectorTool({
410
+ name: "request",
411
+ description: `Send authenticated requests to the InfluxDB HTTP API.
412
+ Authentication is handled automatically using the API token (\`Authorization: Token {token}\`). The instance URL is resolved from the connection.
413
+ Use this tool for all InfluxDB interactions: running SQL / InfluxQL / Flux queries, writing line protocol, and managing buckets / databases.
414
+ For read-only data exploration prefer SQL (InfluxDB 3) or InfluxQL queries \u2014 they return JSON that is straightforward to parse. Flux queries return annotated CSV.`,
415
+ inputSchema,
416
+ outputSchema,
417
+ async execute({ connectionId, method, path: path2, body, contentType }, connections) {
418
+ const connection2 = connections.find((c) => c.id === connectionId);
419
+ if (!connection2) {
420
+ return {
421
+ success: false,
422
+ error: `Connection ${connectionId} not found`
423
+ };
424
+ }
425
+ console.log(
426
+ `[connector-request] influxdb/${connection2.name}: ${method} ${path2}`
427
+ );
428
+ try {
429
+ const url = parameters.url.getValue(connection2).replace(/\/$/, "");
430
+ const token = parameters.token.getValue(connection2);
431
+ const fullUrl = `${url}${path2.startsWith("/") ? "" : "/"}${path2}`;
432
+ const controller = new AbortController();
433
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
434
+ const resolvedContentType = contentType ?? (typeof body === "string" ? "text/plain; charset=utf-8" : "application/json");
435
+ const serializedBody = body === void 0 ? void 0 : typeof body === "string" ? body : JSON.stringify(body);
436
+ try {
437
+ const response = await fetch(fullUrl, {
438
+ method,
439
+ headers: {
440
+ Authorization: `Token ${token}`,
441
+ "Content-Type": resolvedContentType,
442
+ Accept: "application/json"
443
+ },
444
+ body: serializedBody,
445
+ signal: controller.signal
446
+ });
447
+ const text = await response.text();
448
+ let data;
449
+ const resContentType = response.headers.get("content-type") ?? "";
450
+ if (resContentType.includes("application/json")) {
451
+ try {
452
+ data = text ? JSON.parse(text) : null;
453
+ } catch {
454
+ data = { raw: text };
455
+ }
456
+ } else {
457
+ data = text;
458
+ }
459
+ if (!response.ok) {
460
+ let errorMessage = `HTTP ${response.status} ${response.statusText}`;
461
+ if (data && typeof data === "object" && !Array.isArray(data) && typeof data.message === "string") {
462
+ errorMessage = data.message;
463
+ } else if (typeof data === "string" && data) {
464
+ errorMessage = data;
465
+ }
466
+ return { success: false, error: errorMessage };
467
+ }
468
+ return { success: true, status: response.status, data };
469
+ } finally {
470
+ clearTimeout(timeout);
471
+ }
472
+ } catch (err) {
473
+ const msg = err instanceof Error ? err.message : String(err);
474
+ return { success: false, error: msg };
475
+ }
476
+ }
477
+ });
478
+
479
+ // ../connectors/src/connectors/influxdb/index.ts
480
+ var tools = { request: requestTool };
481
+ var influxdbConnector = new ConnectorPlugin({
482
+ slug: "influxdb",
483
+ authType: AUTH_TYPES.API_KEY,
484
+ name: "InfluxDB",
485
+ description: "Connect to InfluxDB (Cloud or OSS) to query time-series data with SQL, InfluxQL, or Flux and to write line protocol.",
486
+ iconUrl: "https://upload.wikimedia.org/wikipedia/commons/b/b2/Influxdb_logo.svg",
487
+ parameters,
488
+ releaseFlag: { dev1: true, dev2: false, prod: false },
489
+ onboarding: influxdbOnboarding,
490
+ systemPrompt: {
491
+ en: `### Tools
492
+
493
+ - \`influxdb_request\`: The only way to call the InfluxDB HTTP API. Use it to run SQL / InfluxQL / Flux queries, write line protocol, and inspect buckets / databases. Authentication (\`Authorization: Token {token}\`) and the instance URL are configured automatically. For InfluxDB 3 prefer SQL (\`POST /api/v3/query_sql\`) \u2014 it returns JSON rows that are directly usable. For InfluxDB 2 use Flux (\`POST /api/v2/query?org={org}\`) \u2014 the response is annotated CSV. Writes use \`POST /api/v3/write_lp?db={db}\` (v3) or \`POST /api/v2/write?org={org}&bucket={bucket}\` (v2) with a line-protocol body and \`contentType\` set to \`text/plain; charset=utf-8\`.
494
+
495
+ ### Business Logic
496
+
497
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
498
+
499
+ SDK methods (client created via \`connection(connectionId)\`):
500
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch against the InfluxDB base URL
501
+ - \`client.querySql<T>(sql, options?)\` \u2014 InfluxDB 3 SQL query; returns an array of row objects, defaults \`db\` to the configured database
502
+ - \`client.queryInfluxql<T>(influxql, options?)\` \u2014 InfluxDB 3 InfluxQL query; same shape as \`querySql\`
503
+ - \`client.queryFlux(flux, options?)\` \u2014 InfluxDB 2 Flux query; returns raw annotated CSV (parse as needed)
504
+ - \`client.writeLineProtocol(lineProtocol, options?)\` \u2014 append-only write; uses v3 or v2 endpoint depending on whether \`org\` is configured
505
+ - \`client.database\` / \`client.org\` \u2014 connection-level defaults
506
+
507
+ \`\`\`ts
508
+ import type { Context } from "hono";
509
+ import { connection } from "@squadbase/vite-server/connectors/influxdb";
510
+
511
+ const influx = connection("<connectionId>");
512
+
513
+ export default async function handler(c: Context) {
514
+ const { measurement = "cpu", limit = 60 } = await c.req.json<{
515
+ measurement?: string;
516
+ limit?: number;
517
+ }>();
518
+
519
+ const rows = await influx.querySql<{
520
+ time: string;
521
+ usage_user: number;
522
+ host: string;
523
+ }>(
524
+ \`SELECT time, usage_user, host FROM \${measurement} ORDER BY time DESC LIMIT \${limit}\`,
525
+ );
526
+
527
+ return c.json({ series: rows });
528
+ }
529
+ \`\`\`
530
+
531
+ ### InfluxDB HTTP API Reference
532
+
533
+ - Base URL: the \`url\` parameter (e.g., \`https://us-east-1-1.aws.cloud2.influxdata.com\`)
534
+ - Authentication: \`Authorization: Token {token}\` (handled automatically)
535
+ - Response content types: \`application/json\` for SQL/InfluxQL, annotated CSV for Flux
536
+
537
+ #### InfluxDB 3 Endpoints
538
+ - POST \`/api/v3/query_sql\` \u2014 Body: \`{ "db": "<database>", "q": "<sql>" }\` \u2192 JSON rows
539
+ - POST \`/api/v3/query_influxql\` \u2014 Body: \`{ "db": "<database>", "q": "<influxql>" }\` \u2192 JSON rows
540
+ - POST \`/api/v3/write_lp?db=<database>&precision=ns\` \u2014 Body: raw line protocol
541
+ - GET \`/api/v3/configure/database\` \u2014 List databases
542
+
543
+ #### InfluxDB 2 Endpoints
544
+ - POST \`/api/v2/query?org=<org>\` \u2014 Body: Flux script (Content-Type: \`application/vnd.flux\`) \u2192 annotated CSV
545
+ - POST \`/api/v2/write?org=<org>&bucket=<bucket>&precision=ns\` \u2014 Body: raw line protocol
546
+ - GET \`/api/v2/buckets?org=<org>\` \u2014 List buckets
547
+ - GET \`/api/v2/orgs\` \u2014 List organizations
548
+
549
+ #### SQL Reference (InfluxDB 3)
550
+ - \`SELECT field_list FROM measurement WHERE time >= now() - INTERVAL '1 hour' AND tag = 'x' ORDER BY time DESC LIMIT 100\`
551
+ - Time filtering uses standard SQL \`time\` column comparisons (\`time >= now() - INTERVAL '...' \`)
552
+ - Aggregates: \`AVG\`, \`SUM\`, \`MIN\`, \`MAX\`, \`COUNT\`; bucket time with \`date_bin('5 minutes', time)\`
553
+ - List measurements: \`SHOW TABLES\`; list columns: \`SHOW COLUMNS FROM <measurement>\``,
554
+ ja: `### \u30C4\u30FC\u30EB
555
+
556
+ - \`influxdb_request\`: InfluxDB HTTP API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002SQL / InfluxQL / Flux \u30AF\u30A8\u30EA\u306E\u5B9F\u884C\u3001line protocol \u66F8\u304D\u8FBC\u307F\u3001bucket / database \u306E\u78BA\u8A8D\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08\`Authorization: Token {token}\`\uFF09\u3068 instance URL \u306F\u81EA\u52D5\u3067\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002InfluxDB 3 \u3067\u306F SQL (\`POST /api/v3/query_sql\`) \u304C JSON \u884C\u3092\u8FD4\u3059\u305F\u3081\u6700\u3082\u6271\u3044\u3084\u3059\u3044\u3067\u3059\u3002InfluxDB 2 \u3067\u306F Flux (\`POST /api/v2/query?org={org}\`) \u3092\u4F7F\u7528\u3057\u3001\u30EC\u30B9\u30DD\u30F3\u30B9\u306F\u6CE8\u91C8\u4ED8\u304D CSV \u3067\u3059\u3002\u66F8\u304D\u8FBC\u307F\u306F \`POST /api/v3/write_lp?db={db}\` (v3) \u307E\u305F\u306F \`POST /api/v2/write?org={org}&bucket={bucket}\` (v2) \u306B line protocol \u3092\u9001\u308A\u307E\u3059\uFF08\`contentType\` \u306F \`text/plain; charset=utf-8\`\uFF09\u3002
557
+
558
+ ### Business Logic
559
+
560
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BF SDK \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
561
+
562
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
563
+ - \`client.request(path, init?)\` \u2014 InfluxDB \u30D9\u30FC\u30B9 URL \u306B\u5BFE\u3059\u308B\u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304D fetch
564
+ - \`client.querySql<T>(sql, options?)\` \u2014 InfluxDB 3 \u306E SQL \u30AF\u30A8\u30EA\u3002\u884C\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u914D\u5217\u3092\u8FD4\u5374\u3057\u3001\`db\` \u306F\u65E2\u5B9A\u3067\u63A5\u7D9A\u306E database
565
+ - \`client.queryInfluxql<T>(influxql, options?)\` \u2014 InfluxDB 3 \u306E InfluxQL \u30AF\u30A8\u30EA\uFF08\`querySql\` \u3068\u540C\u3058\u5F62\u5F0F\uFF09
566
+ - \`client.queryFlux(flux, options?)\` \u2014 InfluxDB 2 \u306E Flux \u30AF\u30A8\u30EA\u3002\u6CE8\u91C8\u4ED8\u304D CSV \u3092\u6587\u5B57\u5217\u3067\u8FD4\u5374\uFF08\u5FC5\u8981\u306B\u5FDC\u3058\u3066\u30D1\u30FC\u30B9\uFF09
567
+ - \`client.writeLineProtocol(lineProtocol, options?)\` \u2014 append-only \u66F8\u304D\u8FBC\u307F\u3002\`org\` \u306E\u6709\u7121\u3067 v3 / v2 \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3092\u5207\u308A\u66FF\u3048
568
+ - \`client.database\` / \`client.org\` \u2014 \u63A5\u7D9A\u5358\u4F4D\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u5024
569
+
570
+ \`\`\`ts
571
+ import type { Context } from "hono";
572
+ import { connection } from "@squadbase/vite-server/connectors/influxdb";
573
+
574
+ const influx = connection("<connectionId>");
575
+
576
+ export default async function handler(c: Context) {
577
+ const { measurement = "cpu", limit = 60 } = await c.req.json<{
578
+ measurement?: string;
579
+ limit?: number;
580
+ }>();
581
+
582
+ const rows = await influx.querySql<{
583
+ time: string;
584
+ usage_user: number;
585
+ host: string;
586
+ }>(
587
+ \`SELECT time, usage_user, host FROM \${measurement} ORDER BY time DESC LIMIT \${limit}\`,
588
+ );
589
+
590
+ return c.json({ series: rows });
591
+ }
592
+ \`\`\`
593
+
594
+ ### InfluxDB HTTP API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
595
+
596
+ - \u30D9\u30FC\u30B9 URL: \`url\` \u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u6307\u5B9A\uFF08\u4F8B: \`https://us-east-1-1.aws.cloud2.influxdata.com\`\uFF09
597
+ - \u8A8D\u8A3C: \`Authorization: Token {token}\`\uFF08\u81EA\u52D5\u8A2D\u5B9A\uFF09
598
+ - \u30EC\u30B9\u30DD\u30F3\u30B9\u5F62\u5F0F: SQL/InfluxQL \u306F JSON\u3001Flux \u306F\u6CE8\u91C8\u4ED8\u304D CSV
599
+
600
+ #### InfluxDB 3 \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
601
+ - POST \`/api/v3/query_sql\` \u2014 Body: \`{ "db": "<database>", "q": "<sql>" }\` \u2192 JSON \u884C
602
+ - POST \`/api/v3/query_influxql\` \u2014 Body: \`{ "db": "<database>", "q": "<influxql>" }\` \u2192 JSON \u884C
603
+ - POST \`/api/v3/write_lp?db=<database>&precision=ns\` \u2014 Body: line protocol \u751F\u6587\u5B57\u5217
604
+ - GET \`/api/v3/configure/database\` \u2014 database \u4E00\u89A7
605
+
606
+ #### InfluxDB 2 \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
607
+ - POST \`/api/v2/query?org=<org>\` \u2014 Body: Flux \u30B9\u30AF\u30EA\u30D7\u30C8\uFF08Content-Type: \`application/vnd.flux\`\uFF09\u2192 \u6CE8\u91C8\u4ED8\u304D CSV
608
+ - POST \`/api/v2/write?org=<org>&bucket=<bucket>&precision=ns\` \u2014 Body: line protocol \u751F\u6587\u5B57\u5217
609
+ - GET \`/api/v2/buckets?org=<org>\` \u2014 bucket \u4E00\u89A7
610
+ - GET \`/api/v2/orgs\` \u2014 organization \u4E00\u89A7
611
+
612
+ #### SQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9 (InfluxDB 3)
613
+ - \`SELECT field_list FROM measurement WHERE time >= now() - INTERVAL '1 hour' AND tag = 'x' ORDER BY time DESC LIMIT 100\`
614
+ - \u6642\u523B\u30D5\u30A3\u30EB\u30BF\u306F\u6A19\u6E96 SQL \u306E \`time\` \u5217\u6BD4\u8F03\uFF08\`time >= now() - INTERVAL '...'\`\uFF09
615
+ - \u96C6\u8A08: \`AVG\`, \`SUM\`, \`MIN\`, \`MAX\`, \`COUNT\`\u3002\u6642\u9593\u30D0\u30B1\u30C3\u30C8: \`date_bin('5 minutes', time)\`
616
+ - measurement \u4E00\u89A7: \`SHOW TABLES\`\u3001\u5217\u4E00\u89A7: \`SHOW COLUMNS FROM <measurement>\``
617
+ },
618
+ tools
619
+ });
620
+
621
+ // src/connectors/create-connector-sdk.ts
622
+ import { readFileSync } from "fs";
623
+ import path from "path";
624
+
625
+ // src/connector-client/env.ts
626
+ function resolveEnvVar(entry, key, connectionId) {
627
+ const envVarName = entry.envVars[key];
628
+ if (!envVarName) {
629
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
630
+ }
631
+ const value = process.env[envVarName];
632
+ if (!value) {
633
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
634
+ }
635
+ return value;
636
+ }
637
+ function resolveEnvVarOptional(entry, key) {
638
+ const envVarName = entry.envVars[key];
639
+ if (!envVarName) return void 0;
640
+ return process.env[envVarName] || void 0;
641
+ }
642
+
643
+ // src/connector-client/proxy-fetch.ts
644
+ import { getContext } from "hono/context-storage";
645
+ import { getCookie } from "hono/cookie";
646
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
647
+ function normalizeHeaders(input) {
648
+ const out = {};
649
+ if (!input) return out;
650
+ new Headers(input).forEach((value, key) => {
651
+ out[key] = value;
652
+ });
653
+ return out;
654
+ }
655
+ function createSandboxProxyFetch(connectionId) {
656
+ return async (input, init) => {
657
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
658
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
659
+ if (!token || !sandboxId) {
660
+ throw new Error(
661
+ "Connection proxy is not configured. Please check your deployment settings."
662
+ );
663
+ }
664
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
665
+ const originalMethod = init?.method ?? "GET";
666
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
667
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
668
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
669
+ return fetch(proxyUrl, {
670
+ method: "POST",
671
+ headers: {
672
+ "Content-Type": "application/json",
673
+ Authorization: `Bearer ${token}`
674
+ },
675
+ body: JSON.stringify({
676
+ url: originalUrl,
677
+ method: originalMethod,
678
+ headers: normalizeHeaders(init?.headers),
679
+ body: originalBody
680
+ })
681
+ });
682
+ };
683
+ }
684
+ function createDeployedAppProxyFetch(connectionId) {
685
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
686
+ if (!projectId) {
687
+ throw new Error(
688
+ "Connection proxy is not configured. Please check your deployment settings."
689
+ );
690
+ }
691
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
692
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
693
+ return async (input, init) => {
694
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
695
+ const originalMethod = init?.method ?? "GET";
696
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
697
+ const c = getContext();
698
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
699
+ if (!appSession) {
700
+ throw new Error(
701
+ "No authentication method available for connection proxy."
702
+ );
703
+ }
704
+ return fetch(proxyUrl, {
705
+ method: "POST",
706
+ headers: {
707
+ "Content-Type": "application/json",
708
+ Authorization: `Bearer ${appSession}`
709
+ },
710
+ body: JSON.stringify({
711
+ url: originalUrl,
712
+ method: originalMethod,
713
+ headers: normalizeHeaders(init?.headers),
714
+ body: originalBody
715
+ })
716
+ });
717
+ };
718
+ }
719
+ function createProxyFetch(connectionId) {
720
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
721
+ return createSandboxProxyFetch(connectionId);
722
+ }
723
+ return createDeployedAppProxyFetch(connectionId);
724
+ }
725
+
726
+ // src/connectors/create-connector-sdk.ts
727
+ function loadConnectionsSync() {
728
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
729
+ try {
730
+ const raw = readFileSync(filePath, "utf-8");
731
+ return JSON.parse(raw);
732
+ } catch {
733
+ return {};
734
+ }
735
+ }
736
+ function createConnectorSdk(plugin, createClient2) {
737
+ return (connectionId) => {
738
+ const connections = loadConnectionsSync();
739
+ const entry = connections[connectionId];
740
+ if (!entry) {
741
+ throw new Error(
742
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
743
+ );
744
+ }
745
+ if (entry.connector.slug !== plugin.slug) {
746
+ throw new Error(
747
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
748
+ );
749
+ }
750
+ const params = {};
751
+ for (const param of Object.values(plugin.parameters)) {
752
+ if (param.required) {
753
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
754
+ } else {
755
+ const val = resolveEnvVarOptional(entry, param.slug);
756
+ if (val !== void 0) params[param.slug] = val;
757
+ }
758
+ }
759
+ return createClient2(params, createProxyFetch(connectionId));
760
+ };
761
+ }
762
+
763
+ // src/connectors/entries/influxdb.ts
764
+ var connection = createConnectorSdk(influxdbConnector, createClient);
765
+ export {
766
+ connection
767
+ };