@squadbase/vite-server 0.1.3-dev.6 → 0.1.3-dev.7
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 +457 -136
- package/dist/connectors/google-analytics.js +6 -12
- package/dist/connectors/google-calendar-oauth.js +1 -1
- package/dist/connectors/google-calendar.js +63 -12
- package/dist/connectors/google-sheets-oauth.js +66 -23
- package/dist/connectors/google-sheets.js +33 -7
- package/dist/connectors/grafana.d.ts +5 -0
- package/dist/connectors/grafana.js +565 -0
- package/dist/index.js +455 -134
- package/dist/main.js +455 -134
- package/dist/vite-plugin.js +455 -134
- package/package.json +5 -1
|
@@ -0,0 +1,565 @@
|
|
|
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/grafana/parameters.ts
|
|
46
|
+
var parameters = {
|
|
47
|
+
url: new ParameterDefinition({
|
|
48
|
+
slug: "url",
|
|
49
|
+
name: "Grafana URL",
|
|
50
|
+
description: "The base URL of the Grafana instance (e.g., https://your-org.grafana.net).",
|
|
51
|
+
envVarBaseKey: "GRAFANA_URL",
|
|
52
|
+
type: "text",
|
|
53
|
+
secret: false,
|
|
54
|
+
required: true
|
|
55
|
+
}),
|
|
56
|
+
apiKey: new ParameterDefinition({
|
|
57
|
+
slug: "api-key",
|
|
58
|
+
name: "Grafana API Key",
|
|
59
|
+
description: "A Grafana API Key or Service Account Token. Create one in Grafana under Administration > Service Accounts.",
|
|
60
|
+
envVarBaseKey: "GRAFANA_API_KEY",
|
|
61
|
+
type: "text",
|
|
62
|
+
secret: true,
|
|
63
|
+
required: true
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ../connectors/src/connectors/grafana/sdk/index.ts
|
|
68
|
+
function createClient(params) {
|
|
69
|
+
const baseUrl = params[parameters.url.slug]?.replace(/\/+$/, "");
|
|
70
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
71
|
+
if (!baseUrl || !apiKey) {
|
|
72
|
+
const required = [parameters.url.slug, parameters.apiKey.slug];
|
|
73
|
+
const missing = required.filter((s) => !params[s]);
|
|
74
|
+
throw new Error(
|
|
75
|
+
`grafana: missing required parameters: ${missing.join(", ")}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function request(path2, init) {
|
|
79
|
+
const url = `${baseUrl}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
80
|
+
const headers = new Headers(init?.headers);
|
|
81
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
82
|
+
headers.set("Content-Type", "application/json");
|
|
83
|
+
headers.set("Accept", "application/json");
|
|
84
|
+
return fetch(url, { ...init, headers });
|
|
85
|
+
}
|
|
86
|
+
async function listDatasources() {
|
|
87
|
+
const response = await request("/api/datasources");
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const body = await response.text();
|
|
90
|
+
throw new Error(
|
|
91
|
+
`grafana: listDatasources failed (${response.status}): ${body}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return await response.json();
|
|
95
|
+
}
|
|
96
|
+
async function getDatasource(uid) {
|
|
97
|
+
const response = await request(
|
|
98
|
+
`/api/datasources/uid/${encodeURIComponent(uid)}`
|
|
99
|
+
);
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const body = await response.text();
|
|
102
|
+
throw new Error(
|
|
103
|
+
`grafana: getDatasource failed (${response.status}): ${body}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return await response.json();
|
|
107
|
+
}
|
|
108
|
+
async function queryDatasource(options) {
|
|
109
|
+
const response = await request("/api/ds/query", {
|
|
110
|
+
method: "POST",
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
queries: options.queries,
|
|
113
|
+
from: options.from ?? "now-1h",
|
|
114
|
+
to: options.to ?? "now"
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const body = await response.text();
|
|
119
|
+
throw new Error(
|
|
120
|
+
`grafana: queryDatasource failed (${response.status}): ${body}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return await response.json();
|
|
124
|
+
}
|
|
125
|
+
async function searchDashboards(options) {
|
|
126
|
+
const searchParams = new URLSearchParams();
|
|
127
|
+
if (options?.query) searchParams.set("query", options.query);
|
|
128
|
+
if (options?.tag) {
|
|
129
|
+
for (const t of options.tag) searchParams.append("tag", t);
|
|
130
|
+
}
|
|
131
|
+
if (options?.type) searchParams.set("type", options.type);
|
|
132
|
+
if (options?.limit != null)
|
|
133
|
+
searchParams.set("limit", String(options.limit));
|
|
134
|
+
const qs = searchParams.toString();
|
|
135
|
+
const response = await request(`/api/search${qs ? `?${qs}` : ""}`);
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
const body = await response.text();
|
|
138
|
+
throw new Error(
|
|
139
|
+
`grafana: searchDashboards failed (${response.status}): ${body}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return await response.json();
|
|
143
|
+
}
|
|
144
|
+
async function getDashboard(uid) {
|
|
145
|
+
const response = await request(
|
|
146
|
+
`/api/dashboards/uid/${encodeURIComponent(uid)}`
|
|
147
|
+
);
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
const body = await response.text();
|
|
150
|
+
throw new Error(
|
|
151
|
+
`grafana: getDashboard failed (${response.status}): ${body}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return await response.json();
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
request,
|
|
158
|
+
listDatasources,
|
|
159
|
+
getDatasource,
|
|
160
|
+
queryDatasource,
|
|
161
|
+
searchDashboards,
|
|
162
|
+
getDashboard
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ../connectors/src/connector-onboarding.ts
|
|
167
|
+
var ConnectorOnboarding = class {
|
|
168
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
169
|
+
connectionSetupInstructions;
|
|
170
|
+
/** Phase 2: Data overview instructions */
|
|
171
|
+
dataOverviewInstructions;
|
|
172
|
+
constructor(config) {
|
|
173
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
174
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
175
|
+
}
|
|
176
|
+
getConnectionSetupPrompt(language) {
|
|
177
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
178
|
+
}
|
|
179
|
+
getDataOverviewInstructions(language) {
|
|
180
|
+
return this.dataOverviewInstructions[language];
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// ../connectors/src/connector-tool.ts
|
|
185
|
+
var ConnectorTool = class {
|
|
186
|
+
name;
|
|
187
|
+
description;
|
|
188
|
+
inputSchema;
|
|
189
|
+
outputSchema;
|
|
190
|
+
_execute;
|
|
191
|
+
constructor(config) {
|
|
192
|
+
this.name = config.name;
|
|
193
|
+
this.description = config.description;
|
|
194
|
+
this.inputSchema = config.inputSchema;
|
|
195
|
+
this.outputSchema = config.outputSchema;
|
|
196
|
+
this._execute = config.execute;
|
|
197
|
+
}
|
|
198
|
+
createTool(connections, config) {
|
|
199
|
+
return {
|
|
200
|
+
description: this.description,
|
|
201
|
+
inputSchema: this.inputSchema,
|
|
202
|
+
outputSchema: this.outputSchema,
|
|
203
|
+
execute: (input) => this._execute(input, connections, config)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// ../connectors/src/connector-plugin.ts
|
|
209
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
210
|
+
slug;
|
|
211
|
+
authType;
|
|
212
|
+
name;
|
|
213
|
+
description;
|
|
214
|
+
iconUrl;
|
|
215
|
+
parameters;
|
|
216
|
+
releaseFlag;
|
|
217
|
+
proxyPolicy;
|
|
218
|
+
experimentalAttributes;
|
|
219
|
+
onboarding;
|
|
220
|
+
systemPrompt;
|
|
221
|
+
tools;
|
|
222
|
+
query;
|
|
223
|
+
checkConnection;
|
|
224
|
+
constructor(config) {
|
|
225
|
+
this.slug = config.slug;
|
|
226
|
+
this.authType = config.authType;
|
|
227
|
+
this.name = config.name;
|
|
228
|
+
this.description = config.description;
|
|
229
|
+
this.iconUrl = config.iconUrl;
|
|
230
|
+
this.parameters = config.parameters;
|
|
231
|
+
this.releaseFlag = config.releaseFlag;
|
|
232
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
233
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
234
|
+
this.onboarding = config.onboarding;
|
|
235
|
+
this.systemPrompt = config.systemPrompt;
|
|
236
|
+
this.tools = config.tools;
|
|
237
|
+
this.query = config.query;
|
|
238
|
+
this.checkConnection = config.checkConnection;
|
|
239
|
+
}
|
|
240
|
+
get connectorKey() {
|
|
241
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Create tools for connections that belong to this connector.
|
|
245
|
+
* Filters connections by connectorKey internally.
|
|
246
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
247
|
+
*/
|
|
248
|
+
createTools(connections, config) {
|
|
249
|
+
const myConnections = connections.filter(
|
|
250
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
251
|
+
);
|
|
252
|
+
const result = {};
|
|
253
|
+
for (const t of Object.values(this.tools)) {
|
|
254
|
+
result[`${this.connectorKey}_${t.name}`] = t.createTool(
|
|
255
|
+
myConnections,
|
|
256
|
+
config
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
static deriveKey(slug, authType) {
|
|
262
|
+
return authType ? `${slug}-${authType}` : slug;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// ../connectors/src/auth-types.ts
|
|
267
|
+
var AUTH_TYPES = {
|
|
268
|
+
OAUTH: "oauth",
|
|
269
|
+
API_KEY: "api-key",
|
|
270
|
+
JWT: "jwt",
|
|
271
|
+
SERVICE_ACCOUNT: "service-account",
|
|
272
|
+
PAT: "pat",
|
|
273
|
+
USER_PASSWORD: "user-password"
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// ../connectors/src/connectors/grafana/setup.ts
|
|
277
|
+
var grafanaOnboarding = new ConnectorOnboarding({
|
|
278
|
+
dataOverviewInstructions: {
|
|
279
|
+
en: `1. Call grafana_request with GET /api/datasources to list all configured datasources
|
|
280
|
+
2. Call grafana_request with GET /api/search?type=dash-db&limit=10 to list dashboards
|
|
281
|
+
3. For a specific datasource, call grafana_request with POST /api/ds/query to run a test query`,
|
|
282
|
+
ja: `1. grafana_request \u3067 GET /api/datasources \u3092\u547C\u3073\u51FA\u3057\u3001\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u4E00\u89A7\u3092\u53D6\u5F97
|
|
283
|
+
2. grafana_request \u3067 GET /api/search?type=dash-db&limit=10 \u3092\u547C\u3073\u51FA\u3057\u3001\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u4E00\u89A7\u3092\u53D6\u5F97
|
|
284
|
+
3. \u7279\u5B9A\u306E\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u306B\u5BFE\u3057\u3066 grafana_request \u3067 POST /api/ds/query \u3092\u547C\u3073\u51FA\u3057\u3001\u30C6\u30B9\u30C8\u30AF\u30A8\u30EA\u3092\u5B9F\u884C`
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ../connectors/src/connectors/grafana/tools/request.ts
|
|
289
|
+
import { z } from "zod";
|
|
290
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
291
|
+
var inputSchema = z.object({
|
|
292
|
+
toolUseIntent: z.string().optional().describe(
|
|
293
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
294
|
+
),
|
|
295
|
+
connectionId: z.string().describe("ID of the Grafana connection to use"),
|
|
296
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe(
|
|
297
|
+
"HTTP method. GET for reading resources (datasources, dashboards, alerts). POST for queries and creating resources. PUT/PATCH for updates. DELETE for removal."
|
|
298
|
+
),
|
|
299
|
+
path: z.string().describe(
|
|
300
|
+
"API path relative to the Grafana base URL (e.g., '/api/datasources', '/api/search', '/api/ds/query'). Must start with '/'."
|
|
301
|
+
),
|
|
302
|
+
body: z.record(z.string(), z.unknown()).optional().describe("Request body (JSON). Required for POST/PUT/PATCH requests.")
|
|
303
|
+
});
|
|
304
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
305
|
+
z.object({
|
|
306
|
+
success: z.literal(true),
|
|
307
|
+
status: z.number(),
|
|
308
|
+
data: z.unknown()
|
|
309
|
+
}),
|
|
310
|
+
z.object({
|
|
311
|
+
success: z.literal(false),
|
|
312
|
+
error: z.string()
|
|
313
|
+
})
|
|
314
|
+
]);
|
|
315
|
+
var requestTool = new ConnectorTool({
|
|
316
|
+
name: "request",
|
|
317
|
+
description: `Send authenticated requests to the Grafana HTTP API.
|
|
318
|
+
Authentication is handled automatically using the configured API Key or Service Account Token via Bearer token.
|
|
319
|
+
Use this tool to interact with Grafana resources: datasources, dashboards, alerts, annotations, and to execute datasource queries via the unified query API (/api/ds/query).
|
|
320
|
+
The path must start with '/' and is appended to the configured Grafana instance URL.`,
|
|
321
|
+
inputSchema,
|
|
322
|
+
outputSchema,
|
|
323
|
+
async execute({ connectionId, method, path: path2, body }, connections) {
|
|
324
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
325
|
+
if (!connection2) {
|
|
326
|
+
return {
|
|
327
|
+
success: false,
|
|
328
|
+
error: `Connection ${connectionId} not found`
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
console.log(
|
|
332
|
+
`[connector-request] grafana/${connection2.name}: ${method} ${path2}`
|
|
333
|
+
);
|
|
334
|
+
try {
|
|
335
|
+
const baseUrl = parameters.url.getValue(connection2).replace(/\/+$/, "");
|
|
336
|
+
const apiKey = parameters.apiKey.getValue(connection2);
|
|
337
|
+
const url = `${baseUrl}${path2}`;
|
|
338
|
+
const controller = new AbortController();
|
|
339
|
+
const timeout = setTimeout(
|
|
340
|
+
() => controller.abort(),
|
|
341
|
+
REQUEST_TIMEOUT_MS
|
|
342
|
+
);
|
|
343
|
+
try {
|
|
344
|
+
const response = await fetch(url, {
|
|
345
|
+
method,
|
|
346
|
+
headers: {
|
|
347
|
+
Authorization: `Bearer ${apiKey}`,
|
|
348
|
+
"Content-Type": "application/json",
|
|
349
|
+
Accept: "application/json"
|
|
350
|
+
},
|
|
351
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
352
|
+
signal: controller.signal
|
|
353
|
+
});
|
|
354
|
+
const data = await response.json();
|
|
355
|
+
if (!response.ok) {
|
|
356
|
+
const errorMessage = typeof data === "object" && data !== null && "message" in data ? String(data.message) : `HTTP ${response.status} ${response.statusText}`;
|
|
357
|
+
return { success: false, error: errorMessage };
|
|
358
|
+
}
|
|
359
|
+
return { success: true, status: response.status, data };
|
|
360
|
+
} finally {
|
|
361
|
+
clearTimeout(timeout);
|
|
362
|
+
}
|
|
363
|
+
} catch (err) {
|
|
364
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
365
|
+
return { success: false, error: msg };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// ../connectors/src/connectors/grafana/index.ts
|
|
371
|
+
var tools = { request: requestTool };
|
|
372
|
+
var grafanaConnector = new ConnectorPlugin({
|
|
373
|
+
slug: "grafana",
|
|
374
|
+
authType: AUTH_TYPES.API_KEY,
|
|
375
|
+
name: "Grafana",
|
|
376
|
+
description: "Connect to Grafana for monitoring dashboards, datasource queries, and alerting.",
|
|
377
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3nGaPhV94lXQsHcCtv4mXz/0559d42f83066e8ba79e78410806750c/grafana-icon.webp",
|
|
378
|
+
parameters,
|
|
379
|
+
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
380
|
+
onboarding: grafanaOnboarding,
|
|
381
|
+
systemPrompt: {
|
|
382
|
+
en: `### Tools
|
|
383
|
+
|
|
384
|
+
- \`grafana_request\`: The only way to call the Grafana HTTP API. Use it to list datasources, search dashboards, execute datasource queries, manage alerts, and read annotations. Authentication is handled automatically via Bearer token. The path must start with \`/\`.
|
|
385
|
+
|
|
386
|
+
### Business Logic
|
|
387
|
+
|
|
388
|
+
The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
|
|
389
|
+
|
|
390
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
391
|
+
- \`client.request(path, init?)\` \u2014 low-level authenticated fetch
|
|
392
|
+
- \`client.listDatasources()\` \u2014 list all datasources
|
|
393
|
+
- \`client.getDatasource(uid)\` \u2014 get datasource by UID
|
|
394
|
+
- \`client.queryDatasource({ queries, from?, to? })\` \u2014 execute queries via unified query API
|
|
395
|
+
- \`client.searchDashboards({ query?, tag?, type?, limit? })\` \u2014 search dashboards
|
|
396
|
+
- \`client.getDashboard(uid)\` \u2014 get dashboard by UID
|
|
397
|
+
|
|
398
|
+
\`\`\`ts
|
|
399
|
+
import type { Context } from "hono";
|
|
400
|
+
import { connection } from "@squadbase/vite-server/connectors/grafana";
|
|
401
|
+
|
|
402
|
+
const grafana = connection("<connectionId>");
|
|
403
|
+
|
|
404
|
+
export default async function handler(c: Context) {
|
|
405
|
+
const datasources = await grafana.listDatasources();
|
|
406
|
+
return c.json(
|
|
407
|
+
datasources.map((ds) => ({
|
|
408
|
+
uid: ds.uid,
|
|
409
|
+
name: ds.name,
|
|
410
|
+
type: ds.type,
|
|
411
|
+
})),
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
\`\`\`
|
|
415
|
+
|
|
416
|
+
### Grafana HTTP API Reference
|
|
417
|
+
|
|
418
|
+
#### Datasources
|
|
419
|
+
- \`GET /api/datasources\` \u2014 List all datasources
|
|
420
|
+
- \`GET /api/datasources/uid/{uid}\` \u2014 Get a datasource by UID
|
|
421
|
+
|
|
422
|
+
#### Unified Query API
|
|
423
|
+
- \`POST /api/ds/query\` \u2014 Execute queries against datasources
|
|
424
|
+
- Body: \`{ "queries": [{ "refId": "A", "datasource": { "type": "prometheus", "uid": "..." }, "expr": "up" }], "from": "now-1h", "to": "now" }\`
|
|
425
|
+
|
|
426
|
+
#### Dashboard Search
|
|
427
|
+
- \`GET /api/search\` \u2014 Search dashboards and folders
|
|
428
|
+
- Query params: \`?query=keyword&tag=tagname&type=dash-db&limit=10\`
|
|
429
|
+
|
|
430
|
+
#### Dashboards
|
|
431
|
+
- \`GET /api/dashboards/uid/{uid}\` \u2014 Get dashboard by UID (includes panels and metadata)
|
|
432
|
+
|
|
433
|
+
#### Alerts
|
|
434
|
+
- \`GET /api/v1/provisioning/alert-rules\` \u2014 List alert rules
|
|
435
|
+
- \`GET /api/annotations\` \u2014 List annotations (filter with \`?from=timestamp&to=timestamp\`)
|
|
436
|
+
|
|
437
|
+
#### Organizations & Health
|
|
438
|
+
- \`GET /api/org\` \u2014 Get current organization
|
|
439
|
+
- \`GET /api/health\` \u2014 Health check`,
|
|
440
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
441
|
+
|
|
442
|
+
- \`grafana_request\`: Grafana HTTP API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u306E\u4E00\u89A7\u53D6\u5F97\u3001\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u691C\u7D22\u3001\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u30AF\u30A8\u30EA\u306E\u5B9F\u884C\u3001\u30A2\u30E9\u30FC\u30C8\u7BA1\u7406\u3001\u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3\u306E\u8AAD\u307F\u53D6\u308A\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306FBearer\u30C8\u30FC\u30AF\u30F3\u3067\u81EA\u52D5\u51E6\u7406\u3055\u308C\u307E\u3059\u3002\u30D1\u30B9\u306F \`/\` \u3067\u59CB\u3081\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002
|
|
443
|
+
|
|
444
|
+
### Business Logic
|
|
445
|
+
|
|
446
|
+
\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\u30BFSDK\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
|
|
447
|
+
|
|
448
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
449
|
+
- \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
|
|
450
|
+
- \`client.listDatasources()\` \u2014 \u5168\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u306E\u4E00\u89A7\u53D6\u5F97
|
|
451
|
+
- \`client.getDatasource(uid)\` \u2014 UID\u3067\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3092\u53D6\u5F97
|
|
452
|
+
- \`client.queryDatasource({ queries, from?, to? })\` \u2014 \u7D71\u5408\u30AF\u30A8\u30EAAPI\u3067\u30AF\u30A8\u30EA\u5B9F\u884C
|
|
453
|
+
- \`client.searchDashboards({ query?, tag?, type?, limit? })\` \u2014 \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u691C\u7D22
|
|
454
|
+
- \`client.getDashboard(uid)\` \u2014 UID\u3067\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u53D6\u5F97
|
|
455
|
+
|
|
456
|
+
\`\`\`ts
|
|
457
|
+
import type { Context } from "hono";
|
|
458
|
+
import { connection } from "@squadbase/vite-server/connectors/grafana";
|
|
459
|
+
|
|
460
|
+
const grafana = connection("<connectionId>");
|
|
461
|
+
|
|
462
|
+
export default async function handler(c: Context) {
|
|
463
|
+
const datasources = await grafana.listDatasources();
|
|
464
|
+
return c.json(
|
|
465
|
+
datasources.map((ds) => ({
|
|
466
|
+
uid: ds.uid,
|
|
467
|
+
name: ds.name,
|
|
468
|
+
type: ds.type,
|
|
469
|
+
})),
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
\`\`\`
|
|
473
|
+
|
|
474
|
+
### Grafana HTTP API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
475
|
+
|
|
476
|
+
#### \u30C7\u30FC\u30BF\u30BD\u30FC\u30B9
|
|
477
|
+
- \`GET /api/datasources\` \u2014 \u5168\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u306E\u4E00\u89A7\u53D6\u5F97
|
|
478
|
+
- \`GET /api/datasources/uid/{uid}\` \u2014 UID\u3067\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u3092\u53D6\u5F97
|
|
479
|
+
|
|
480
|
+
#### \u7D71\u5408\u30AF\u30A8\u30EAAPI
|
|
481
|
+
- \`POST /api/ds/query\` \u2014 \u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u306B\u5BFE\u3057\u3066\u30AF\u30A8\u30EA\u3092\u5B9F\u884C
|
|
482
|
+
- Body: \`{ "queries": [{ "refId": "A", "datasource": { "type": "prometheus", "uid": "..." }, "expr": "up" }], "from": "now-1h", "to": "now" }\`
|
|
483
|
+
|
|
484
|
+
#### \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u691C\u7D22
|
|
485
|
+
- \`GET /api/search\` \u2014 \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3068\u30D5\u30A9\u30EB\u30C0\u3092\u691C\u7D22
|
|
486
|
+
- \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF: \`?query=keyword&tag=tagname&type=dash-db&limit=10\`
|
|
487
|
+
|
|
488
|
+
#### \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9
|
|
489
|
+
- \`GET /api/dashboards/uid/{uid}\` \u2014 UID\u3067\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3092\u53D6\u5F97\uFF08\u30D1\u30CD\u30EB\u3068\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u542B\u3080\uFF09
|
|
490
|
+
|
|
491
|
+
#### \u30A2\u30E9\u30FC\u30C8
|
|
492
|
+
- \`GET /api/v1/provisioning/alert-rules\` \u2014 \u30A2\u30E9\u30FC\u30C8\u30EB\u30FC\u30EB\u4E00\u89A7
|
|
493
|
+
- \`GET /api/annotations\` \u2014 \u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3\u4E00\u89A7\uFF08\`?from=timestamp&to=timestamp\` \u3067\u30D5\u30A3\u30EB\u30BF\uFF09
|
|
494
|
+
|
|
495
|
+
#### \u7D44\u7E54 & \u30D8\u30EB\u30B9\u30C1\u30A7\u30C3\u30AF
|
|
496
|
+
- \`GET /api/org\` \u2014 \u73FE\u5728\u306E\u7D44\u7E54\u3092\u53D6\u5F97
|
|
497
|
+
- \`GET /api/health\` \u2014 \u30D8\u30EB\u30B9\u30C1\u30A7\u30C3\u30AF`
|
|
498
|
+
},
|
|
499
|
+
tools
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// src/connectors/create-connector-sdk.ts
|
|
503
|
+
import { readFileSync } from "fs";
|
|
504
|
+
import path from "path";
|
|
505
|
+
|
|
506
|
+
// src/connector-client/env.ts
|
|
507
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
508
|
+
const envVarName = entry.envVars[key];
|
|
509
|
+
if (!envVarName) {
|
|
510
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
511
|
+
}
|
|
512
|
+
const value = process.env[envVarName];
|
|
513
|
+
if (!value) {
|
|
514
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
515
|
+
}
|
|
516
|
+
return value;
|
|
517
|
+
}
|
|
518
|
+
function resolveEnvVarOptional(entry, key) {
|
|
519
|
+
const envVarName = entry.envVars[key];
|
|
520
|
+
if (!envVarName) return void 0;
|
|
521
|
+
return process.env[envVarName] || void 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/connectors/create-connector-sdk.ts
|
|
525
|
+
function loadConnectionsSync() {
|
|
526
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
527
|
+
try {
|
|
528
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
529
|
+
return JSON.parse(raw);
|
|
530
|
+
} catch {
|
|
531
|
+
return {};
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
535
|
+
return (connectionId) => {
|
|
536
|
+
const connections = loadConnectionsSync();
|
|
537
|
+
const entry = connections[connectionId];
|
|
538
|
+
if (!entry) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const params = {};
|
|
549
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
550
|
+
if (param.required) {
|
|
551
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
552
|
+
} else {
|
|
553
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
554
|
+
if (val !== void 0) params[param.slug] = val;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return createClient2(params);
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/connectors/entries/grafana.ts
|
|
562
|
+
var connection = createConnectorSdk(grafanaConnector, createClient);
|
|
563
|
+
export {
|
|
564
|
+
connection
|
|
565
|
+
};
|