@squadbase/vite-server 0.1.8 → 0.1.9-dev.3841401

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.
Files changed (86) hide show
  1. package/dist/cli/index.js +73422 -59588
  2. package/dist/connectors/airtable-oauth.js +3 -0
  3. package/dist/connectors/airtable.js +3 -0
  4. package/dist/connectors/amplitude.js +3 -0
  5. package/dist/connectors/anthropic.js +3 -0
  6. package/dist/connectors/asana.js +18 -2
  7. package/dist/connectors/attio.js +3 -0
  8. package/dist/connectors/aws-billing.d.ts +5 -0
  9. package/dist/connectors/aws-billing.js +29843 -0
  10. package/dist/connectors/azure-sql.d.ts +5 -0
  11. package/dist/connectors/azure-sql.js +668 -0
  12. package/dist/connectors/backlog-api-key.js +3 -0
  13. package/dist/connectors/clickup.d.ts +5 -0
  14. package/dist/connectors/clickup.js +850 -0
  15. package/dist/connectors/customerio.js +3 -0
  16. package/dist/connectors/dbt.js +3 -0
  17. package/dist/connectors/freshdesk.d.ts +5 -0
  18. package/dist/connectors/freshdesk.js +842 -0
  19. package/dist/connectors/freshsales.d.ts +5 -0
  20. package/dist/connectors/freshsales.js +867 -0
  21. package/dist/connectors/freshservice.d.ts +5 -0
  22. package/dist/connectors/freshservice.js +813 -0
  23. package/dist/connectors/gamma.js +3 -0
  24. package/dist/connectors/gemini.js +3 -0
  25. package/dist/connectors/github.d.ts +5 -0
  26. package/dist/connectors/github.js +963 -0
  27. package/dist/connectors/gmail-oauth.js +18 -2
  28. package/dist/connectors/gmail.js +34 -16
  29. package/dist/connectors/google-ads.js +3 -0
  30. package/dist/connectors/google-analytics-oauth.js +3 -0
  31. package/dist/connectors/google-analytics.js +3 -0
  32. package/dist/connectors/google-audit-log.d.ts +5 -0
  33. package/dist/connectors/google-audit-log.js +813 -0
  34. package/dist/connectors/google-calendar-oauth.js +21 -2
  35. package/dist/connectors/google-calendar.js +140 -73
  36. package/dist/connectors/google-docs.js +21 -2
  37. package/dist/connectors/google-drive.js +18 -2
  38. package/dist/connectors/google-search-console-oauth.d.ts +5 -0
  39. package/dist/connectors/google-search-console-oauth.js +954 -0
  40. package/dist/connectors/google-sheets.js +21 -2
  41. package/dist/connectors/google-slides.js +21 -2
  42. package/dist/connectors/grafana.js +3 -0
  43. package/dist/connectors/hubspot-oauth.js +3 -0
  44. package/dist/connectors/hubspot.js +3 -0
  45. package/dist/connectors/influxdb.js +3 -0
  46. package/dist/connectors/intercom-oauth.js +3 -0
  47. package/dist/connectors/intercom.js +3 -0
  48. package/dist/connectors/jdbc.d.ts +5 -0
  49. package/dist/connectors/jdbc.js +21509 -0
  50. package/dist/connectors/jira-api-key.js +3 -0
  51. package/dist/connectors/kintone-api-token.js +3 -0
  52. package/dist/connectors/kintone.js +3 -0
  53. package/dist/connectors/linear.js +3 -0
  54. package/dist/connectors/linkedin-ads.js +3 -0
  55. package/dist/connectors/mailchimp-oauth.js +3 -0
  56. package/dist/connectors/mailchimp.js +3 -0
  57. package/dist/connectors/meta-ads-oauth.js +3 -0
  58. package/dist/connectors/meta-ads.js +3 -0
  59. package/dist/connectors/mixpanel.js +3 -0
  60. package/dist/connectors/monday.d.ts +5 -0
  61. package/dist/connectors/monday.js +853 -0
  62. package/dist/connectors/notion-oauth.js +3 -0
  63. package/dist/connectors/notion.js +3 -0
  64. package/dist/connectors/openai.js +3 -0
  65. package/dist/connectors/oracle.d.ts +5 -0
  66. package/dist/connectors/oracle.js +676 -0
  67. package/dist/connectors/salesforce.js +3 -0
  68. package/dist/connectors/semrush.d.ts +5 -0
  69. package/dist/connectors/semrush.js +812 -0
  70. package/dist/connectors/sentry.js +3 -0
  71. package/dist/connectors/shopify-oauth.js +3 -0
  72. package/dist/connectors/shopify.js +3 -0
  73. package/dist/connectors/sqlserver.d.ts +5 -0
  74. package/dist/connectors/sqlserver.js +667 -0
  75. package/dist/connectors/stripe-api-key.js +3 -0
  76. package/dist/connectors/stripe-oauth.js +3 -0
  77. package/dist/connectors/supabase.d.ts +5 -0
  78. package/dist/connectors/supabase.js +582 -0
  79. package/dist/connectors/tiktok-ads.js +18 -2
  80. package/dist/connectors/wix-store.js +3 -0
  81. package/dist/connectors/zendesk-oauth.js +3 -0
  82. package/dist/connectors/zendesk.js +3 -0
  83. package/dist/index.js +70184 -56350
  84. package/dist/main.js +70178 -56344
  85. package/dist/vite-plugin.js +71935 -58101
  86. package/package.json +64 -2
@@ -0,0 +1,812 @@
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/semrush/parameters.ts
46
+ var parameters = {
47
+ apiKey: new ParameterDefinition({
48
+ slug: "api-key",
49
+ name: "Semrush API Key",
50
+ description: "Your Semrush API key from Profile > Subscription info > API units > API key. A subscription with API units is required.",
51
+ envVarBaseKey: "SEMRUSH_API_KEY",
52
+ type: "text",
53
+ secret: true,
54
+ required: true
55
+ })
56
+ };
57
+
58
+ // ../connectors/src/connectors/semrush/sdk/index.ts
59
+ var BASE_URL = "https://api.semrush.com";
60
+ function createClient(params) {
61
+ const apiKey = params[parameters.apiKey.slug];
62
+ if (!apiKey) {
63
+ throw new Error(
64
+ `semrush: missing required parameter: ${parameters.apiKey.slug}`
65
+ );
66
+ }
67
+ function buildUrl(path2, query) {
68
+ const url = new URL(`${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`);
69
+ if (query) {
70
+ for (const [k, v] of Object.entries(query)) {
71
+ if (k === "key") continue;
72
+ url.searchParams.set(k, v);
73
+ }
74
+ }
75
+ url.searchParams.set("key", apiKey);
76
+ return url.toString();
77
+ }
78
+ function parseCsv(text) {
79
+ const trimmed = text.trim();
80
+ if (trimmed.length === 0) {
81
+ return { columns: [], rows: [], raw: text };
82
+ }
83
+ const lines = trimmed.split(/\r?\n/);
84
+ const columns = lines[0].split(";");
85
+ const rows = [];
86
+ for (let i = 1; i < lines.length; i++) {
87
+ const cells = lines[i].split(";");
88
+ const row = {};
89
+ for (let c = 0; c < columns.length; c++) {
90
+ row[columns[c]] = cells[c] ?? "";
91
+ }
92
+ rows.push(row);
93
+ }
94
+ return { columns, rows, raw: text };
95
+ }
96
+ async function request(path2, init) {
97
+ const { query, ...rest } = init ?? {};
98
+ return fetch(buildUrl(path2, query), rest);
99
+ }
100
+ return {
101
+ request,
102
+ async report(type, query) {
103
+ const res = await request("/", { query: { type, ...query ?? {} } });
104
+ const text = await res.text();
105
+ if (text.startsWith("ERROR ")) {
106
+ throw new Error(`semrush: ${text.trim()}`);
107
+ }
108
+ if (!res.ok) {
109
+ throw new Error(
110
+ `semrush: ${res.status} ${res.statusText}: ${text}`
111
+ );
112
+ }
113
+ return parseCsv(text);
114
+ },
115
+ async trends(path2, query) {
116
+ const trendsPath = path2.startsWith("/analytics/") ? path2 : `/analytics/v1/${path2.replace(/^\//, "")}`;
117
+ const res = await request(trendsPath, { query });
118
+ const text = await res.text();
119
+ if (!res.ok) {
120
+ throw new Error(
121
+ `semrush trends: ${res.status} ${res.statusText}: ${text}`
122
+ );
123
+ }
124
+ try {
125
+ return JSON.parse(text);
126
+ } catch {
127
+ throw new Error(
128
+ `semrush trends: response is not JSON: ${text.slice(0, 200)}`
129
+ );
130
+ }
131
+ },
132
+ async projects(path2, init) {
133
+ const projectsPath = path2.startsWith("/management/") ? path2 : `/management/v1/${path2.replace(/^\//, "")}`;
134
+ const res = await request(projectsPath, init);
135
+ const text = await res.text();
136
+ if (!res.ok) {
137
+ throw new Error(
138
+ `semrush projects: ${res.status} ${res.statusText}: ${text}`
139
+ );
140
+ }
141
+ try {
142
+ return JSON.parse(text);
143
+ } catch {
144
+ throw new Error(
145
+ `semrush projects: response is not JSON: ${text.slice(0, 200)}`
146
+ );
147
+ }
148
+ }
149
+ };
150
+ }
151
+
152
+ // ../connectors/src/connector-onboarding.ts
153
+ var ConnectorOnboarding = class {
154
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
155
+ connectionSetupInstructions;
156
+ /** Phase 2: Data overview instructions */
157
+ dataOverviewInstructions;
158
+ constructor(config) {
159
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
160
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
161
+ }
162
+ getConnectionSetupPrompt(language) {
163
+ return this.connectionSetupInstructions?.[language] ?? null;
164
+ }
165
+ getDataOverviewInstructions(language) {
166
+ return this.dataOverviewInstructions[language];
167
+ }
168
+ };
169
+
170
+ // ../connectors/src/connector-tool.ts
171
+ var ConnectorTool = class {
172
+ name;
173
+ description;
174
+ inputSchema;
175
+ outputSchema;
176
+ _execute;
177
+ constructor(config) {
178
+ this.name = config.name;
179
+ this.description = config.description;
180
+ this.inputSchema = config.inputSchema;
181
+ this.outputSchema = config.outputSchema;
182
+ this._execute = config.execute;
183
+ }
184
+ createTool(connections, config) {
185
+ return {
186
+ description: this.description,
187
+ inputSchema: this.inputSchema,
188
+ outputSchema: this.outputSchema,
189
+ execute: (input) => this._execute(input, connections, config)
190
+ };
191
+ }
192
+ };
193
+
194
+ // ../connectors/src/connector-plugin.ts
195
+ var ConnectorPlugin = class _ConnectorPlugin {
196
+ slug;
197
+ authType;
198
+ name;
199
+ description;
200
+ iconUrl;
201
+ parameters;
202
+ releaseFlag;
203
+ proxyPolicy;
204
+ experimentalAttributes;
205
+ categories;
206
+ onboarding;
207
+ systemPrompt;
208
+ tools;
209
+ query;
210
+ checkConnection;
211
+ constructor(config) {
212
+ this.slug = config.slug;
213
+ this.authType = config.authType;
214
+ this.name = config.name;
215
+ this.description = config.description;
216
+ this.iconUrl = config.iconUrl;
217
+ this.parameters = config.parameters;
218
+ this.releaseFlag = config.releaseFlag;
219
+ this.proxyPolicy = config.proxyPolicy;
220
+ this.experimentalAttributes = config.experimentalAttributes;
221
+ this.categories = config.categories ?? [];
222
+ this.onboarding = config.onboarding;
223
+ this.systemPrompt = config.systemPrompt;
224
+ this.tools = config.tools;
225
+ this.query = config.query;
226
+ this.checkConnection = config.checkConnection;
227
+ }
228
+ get connectorKey() {
229
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
230
+ }
231
+ /**
232
+ * Create tools for connections that belong to this connector.
233
+ * Filters connections by connectorKey internally.
234
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
235
+ */
236
+ createTools(connections, config, opts) {
237
+ const myConnections = connections.filter(
238
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
239
+ );
240
+ const result = {};
241
+ for (const t of Object.values(this.tools)) {
242
+ const tool = t.createTool(myConnections, config);
243
+ const originalToModelOutput = tool.toModelOutput;
244
+ result[`${this.connectorKey}_${t.name}`] = {
245
+ ...tool,
246
+ toModelOutput: async (options) => {
247
+ if (!originalToModelOutput) {
248
+ return opts.truncateOutput(options.output);
249
+ }
250
+ const modelOutput = await originalToModelOutput(options);
251
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
252
+ return opts.truncateOutput(modelOutput.value);
253
+ }
254
+ return modelOutput;
255
+ }
256
+ };
257
+ }
258
+ return result;
259
+ }
260
+ static deriveKey(slug, authType) {
261
+ if (authType) return `${slug}-${authType}`;
262
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
263
+ // user-password
264
+ "postgresql": "user-password",
265
+ "mysql": "user-password",
266
+ "clickhouse": "user-password",
267
+ "kintone": "user-password",
268
+ "squadbase-db": "user-password",
269
+ // service-account
270
+ "snowflake": "service-account",
271
+ "bigquery": "service-account",
272
+ "google-analytics": "service-account",
273
+ "google-calendar": "service-account",
274
+ "aws-athena": "service-account",
275
+ "redshift": "service-account",
276
+ // api-key
277
+ "databricks": "api-key",
278
+ "dbt": "api-key",
279
+ "airtable": "api-key",
280
+ "openai": "api-key",
281
+ "gemini": "api-key",
282
+ "anthropic": "api-key",
283
+ "wix-store": "api-key"
284
+ };
285
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
286
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
287
+ return slug;
288
+ }
289
+ };
290
+
291
+ // ../connectors/src/auth-types.ts
292
+ var AUTH_TYPES = {
293
+ OAUTH: "oauth",
294
+ API_KEY: "api-key",
295
+ JWT: "jwt",
296
+ SERVICE_ACCOUNT: "service-account",
297
+ PAT: "pat",
298
+ USER_PASSWORD: "user-password"
299
+ };
300
+
301
+ // ../connectors/src/connectors/semrush/tools/request.ts
302
+ import { z } from "zod";
303
+ var BASE_URL2 = "https://api.semrush.com";
304
+ var REQUEST_TIMEOUT_MS = 6e4;
305
+ var inputSchema = z.object({
306
+ toolUseIntent: z.string().optional().describe(
307
+ "Brief description of what you intend to accomplish with this tool call"
308
+ ),
309
+ connectionId: z.string().describe("ID of the Semrush connection to use"),
310
+ method: z.enum(["GET", "POST"]).describe(
311
+ "HTTP method. GET for almost all Semrush endpoints. POST is used for a few Projects API mutations."
312
+ ),
313
+ path: z.string().describe(
314
+ "API path appended to https://api.semrush.com (e.g., '/' for the Standard Analytics API, '/analytics/v1/' for the Trends API, '/management/v1/projects/' for the Projects API)."
315
+ ),
316
+ queryParams: z.record(z.string(), z.string()).optional().describe(
317
+ "Query parameters to append to the URL. The 'key' parameter is injected automatically \u2014 do not include it. Common params: 'type' (report type, e.g. 'domain_overview'), 'domain', 'phrase', 'database' (e.g. 'us', 'uk', 'jp'), 'export_columns' (comma-separated), 'display_limit', 'display_offset', 'display_date' (YYYYMM15)."
318
+ ),
319
+ body: z.string().optional().describe(
320
+ "Request body for POST requests. Pass a raw string (JSON for the Projects API, form-encoded otherwise). Set 'contentType' to control the Content-Type header."
321
+ ),
322
+ contentType: z.enum(["application/json", "application/x-www-form-urlencoded"]).optional().describe(
323
+ "Content-Type header for POST requests. Defaults to application/json."
324
+ ),
325
+ responseFormat: z.enum(["text", "json"]).optional().describe(
326
+ "How to parse the response body. Defaults to 'text' because the Standard Analytics API returns semicolon-separated CSV. Use 'json' for the Trends API and Projects API which return JSON."
327
+ )
328
+ });
329
+ var outputSchema = z.discriminatedUnion("success", [
330
+ z.object({
331
+ success: z.literal(true),
332
+ status: z.number(),
333
+ contentType: z.string().optional(),
334
+ data: z.unknown()
335
+ }),
336
+ z.object({
337
+ success: z.literal(false),
338
+ error: z.string()
339
+ })
340
+ ]);
341
+ var requestTool = new ConnectorTool({
342
+ name: "request",
343
+ description: `Send authenticated requests to the Semrush API (https://api.semrush.com).
344
+ Use this tool for all Semrush interactions across the Standard Analytics API (domain/keyword/backlink reports), the Trends API (\`/analytics/v1/\`), and the Projects API (\`/management/v1/projects/\`).
345
+ Authentication is handled automatically \u2014 the API key is appended as the \`key\` query parameter on every request. Do NOT include \`key\` in queryParams.
346
+
347
+ The Standard Analytics API returns semicolon-separated CSV with the first row being the header (use responseFormat="text"). The Trends and Projects APIs return JSON (use responseFormat="json").
348
+ Errors from the Standard API are returned as a plain text body starting with "ERROR" and HTTP 200, so always inspect the response body even on success.`,
349
+ inputSchema,
350
+ outputSchema,
351
+ async execute({
352
+ connectionId,
353
+ method,
354
+ path: path2,
355
+ queryParams,
356
+ body,
357
+ contentType,
358
+ responseFormat
359
+ }, connections) {
360
+ const connection2 = connections.find((c) => c.id === connectionId);
361
+ if (!connection2) {
362
+ return {
363
+ success: false,
364
+ error: `Connection ${connectionId} not found`
365
+ };
366
+ }
367
+ console.log(
368
+ `[connector-request] semrush/${connection2.name}: ${method} ${path2}`
369
+ );
370
+ try {
371
+ const apiKey = parameters.apiKey.getValue(connection2);
372
+ const url = new URL(
373
+ `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`
374
+ );
375
+ if (queryParams) {
376
+ for (const [k, v] of Object.entries(queryParams)) {
377
+ if (k === "key") continue;
378
+ url.searchParams.set(k, v);
379
+ }
380
+ }
381
+ url.searchParams.set("key", apiKey);
382
+ const controller = new AbortController();
383
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
384
+ try {
385
+ const headers = {};
386
+ if (body) {
387
+ headers["Content-Type"] = contentType ?? "application/json";
388
+ }
389
+ const response = await fetch(url.toString(), {
390
+ method,
391
+ headers,
392
+ body: body ?? void 0,
393
+ signal: controller.signal
394
+ });
395
+ const responseContentType = response.headers.get("content-type") ?? void 0;
396
+ const text = await response.text();
397
+ if (text.startsWith("ERROR ")) {
398
+ return { success: false, error: text.trim() };
399
+ }
400
+ if (!response.ok) {
401
+ return {
402
+ success: false,
403
+ error: text || `HTTP ${response.status} ${response.statusText}`
404
+ };
405
+ }
406
+ const wantJson = responseFormat === "json" || responseFormat === void 0 && (responseContentType?.includes("application/json") ?? false);
407
+ let data = text;
408
+ if (wantJson && text.length > 0) {
409
+ try {
410
+ data = JSON.parse(text);
411
+ } catch {
412
+ data = text;
413
+ }
414
+ }
415
+ return {
416
+ success: true,
417
+ status: response.status,
418
+ contentType: responseContentType,
419
+ data
420
+ };
421
+ } finally {
422
+ clearTimeout(timeout);
423
+ }
424
+ } catch (err) {
425
+ const msg = err instanceof Error ? err.message : String(err);
426
+ return { success: false, error: msg };
427
+ }
428
+ }
429
+ });
430
+
431
+ // ../connectors/src/connectors/semrush/setup.ts
432
+ var requestToolName = `semrush-api-key_${requestTool.name}`;
433
+ var semrushOnboarding = new ConnectorOnboarding({
434
+ connectionSetupInstructions: {
435
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Semrush\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
436
+
437
+ 1. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001API\u6B8B\u91CF\u3092\u78BA\u8A8D\u3059\u308B:
438
+ - \`method\`: \`"GET"\`
439
+ - \`path\`: \`"/"\`
440
+ - \`queryParams\`: \`{ "type": "user_units" }\`
441
+ 2. \u30EC\u30B9\u30DD\u30F3\u30B9\u672C\u6587\u304C \`ERROR\` \u3067\u59CB\u307E\u308B\u3001\u307E\u305F\u306FAPI\u30E6\u30CB\u30C3\u30C8\u6B8B\u91CF\u304C0\u306E\u5834\u5408\u3001\u30E6\u30FC\u30B6\u30FC\u306BAPI\u30AD\u30FC\u3068\u30B5\u30D6\u30B9\u30AF\u30EA\u30D7\u30B7\u30E7\u30F3\u72B6\u614B\uFF08API units \u304C\u4ED8\u4E0E\u3055\u308C\u3066\u3044\u308B\u304B\uFF09\u306E\u78BA\u8A8D\u3092\u4F9D\u983C\u3059\u308B
442
+
443
+ #### \u5236\u7D04
444
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306BAPI\u30E6\u30CB\u30C3\u30C8\u3092\u5927\u91CF\u6D88\u8CBB\u3059\u308B\u30EC\u30DD\u30FC\u30C8\u3092\u5B9F\u884C\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u307F
445
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3002\u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
446
+ en: `Follow these steps to set up the Semrush connection.
447
+
448
+ 1. Call \`${requestToolName}\` to check the remaining API units:
449
+ - \`method\`: \`"GET"\`
450
+ - \`path\`: \`"/"\`
451
+ - \`queryParams\`: \`{ "type": "user_units" }\`
452
+ 2. If the response body starts with \`ERROR\` or API units are 0, ask the user to verify the API key and subscription status (API units must be granted to the account)
453
+
454
+ #### Constraints
455
+ - **Do NOT run reports that consume large amounts of API units during setup**. Only the metadata request specified above is allowed
456
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
457
+ },
458
+ dataOverviewInstructions: {
459
+ en: `1. Call ${requestToolName} with path "/" and queryParams \`{ "type": "domain_overview", "domain": "<example.com>", "database": "us" }\` to inspect the domain overview report (CSV)
460
+ 2. Call ${requestToolName} with path "/" and queryParams \`{ "type": "domain_organic", "domain": "<example.com>", "database": "us", "display_limit": "5" }\` to sample organic keywords
461
+ 3. Call ${requestToolName} with path "/" and queryParams \`{ "type": "phrase_this", "phrase": "<keyword>", "database": "us" }\` to inspect a keyword overview
462
+ 4. Explore other report types (backlinks_overview, domain_adwords, phrase_related) and the Trends API ("/analytics/v1/...") with responseFormat="json" as needed
463
+ 5. Remember: the Standard Analytics API returns semicolon-separated CSV with the first row as the header`,
464
+ ja: `1. ${requestToolName} \u3067 path "/" \u3068 queryParams \`{ "type": "domain_overview", "domain": "<example.com>", "database": "us" }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30C9\u30E1\u30A4\u30F3\u30AA\u30FC\u30D0\u30FC\u30D3\u30E5\u30FC\uFF08CSV\uFF09\u3092\u78BA\u8A8D
465
+ 2. ${requestToolName} \u3067 path "/" \u3068 queryParams \`{ "type": "domain_organic", "domain": "<example.com>", "database": "us", "display_limit": "5" }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0
466
+ 3. ${requestToolName} \u3067 path "/" \u3068 queryParams \`{ "type": "phrase_this", "phrase": "<keyword>", "database": "us" }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30AD\u30FC\u30EF\u30FC\u30C9\u6982\u8981\u3092\u78BA\u8A8D
467
+ 4. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u4ED6\u306E\u30EC\u30DD\u30FC\u30C8\u30BF\u30A4\u30D7\uFF08backlinks_overview\u3001domain_adwords\u3001phrase_related\uFF09\u3084 Trends API ("/analytics/v1/...", responseFormat="json") \u3092\u63A2\u7D22
468
+ 5. \u6CE8\u610F: Standard Analytics API \u306F\u30BB\u30DF\u30B3\u30ED\u30F3\u533A\u5207\u308ACSV\u3092\u8FD4\u3057\u30011\u884C\u76EE\u304C\u30D8\u30C3\u30C0\u30FC`
469
+ }
470
+ });
471
+
472
+ // ../connectors/src/connectors/semrush/index.ts
473
+ var tools = { request: requestTool };
474
+ var semrushConnector = new ConnectorPlugin({
475
+ slug: "semrush",
476
+ authType: AUTH_TYPES.API_KEY,
477
+ name: "Semrush",
478
+ description: "Connect to Semrush for SEO, paid search, content, and competitive intelligence data via the Semrush API (Standard Analytics, Trends, and Projects).",
479
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4mrVYrg72PeaEv5F6hEF6g/b6cd050f9d97b9998ab3371cfe656988/semrush-icon.png",
480
+ parameters,
481
+ releaseFlag: { dev1: true, dev2: true, prod: true },
482
+ categories: ["marketing"],
483
+ onboarding: semrushOnboarding,
484
+ systemPrompt: {
485
+ en: `### Tools
486
+
487
+ - \`semrush-api-key_request\`: The only way to call the Semrush API. Use it for the Standard Analytics API (\`/?type=...\` reports), the Trends API (\`/analytics/v1/...\`), and the Projects API (\`/management/v1/...\`). Authentication is handled automatically \u2014 the API key is appended as the \`key\` query parameter; never include it yourself. The Standard Analytics API returns semicolon-separated CSV with the first row as the header (use \`responseFormat="text"\`); the Trends and Projects APIs return JSON (use \`responseFormat="json"\`). Errors from the Standard API are returned as a body that starts with \`ERROR\`, often with HTTP 200 \u2014 the tool surfaces these as \`success: false\` automatically.
488
+
489
+ ### Business Logic
490
+
491
+ The business logic type for this connector is "typescript". Write handler code using the connector SDK shown below. Do NOT access credentials directly from environment variables.
492
+
493
+ SDK methods (client created via \`connection(connectionId)\`):
494
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch. Pass \`init.query\` to set query parameters; \`key\` is injected automatically
495
+ - \`client.report(type, query?)\` \u2014 call a Standard Analytics report and parse the CSV into \`{ columns, rows, raw }\`
496
+ - \`client.trends(path, query?)\` \u2014 call a Trends API endpoint (\`/analytics/v1/...\`) and return parsed JSON
497
+ - \`client.projects(path, init?)\` \u2014 call a Projects API endpoint (\`/management/v1/...\`) and return parsed JSON
498
+
499
+ \`\`\`ts
500
+ import type { Context } from "hono";
501
+ import { connection } from "@squadbase/vite-server/connectors/semrush";
502
+
503
+ const semrush = connection("<connectionId>");
504
+
505
+ export default async function handler(c: Context) {
506
+ const { domain = "example.com", database = "us" } = await c.req.json<{
507
+ domain?: string;
508
+ database?: string;
509
+ }>();
510
+
511
+ const overview = await semrush.report("domain_overview", { domain, database });
512
+
513
+ return c.json({ columns: overview.columns, rows: overview.rows });
514
+ }
515
+ \`\`\`
516
+
517
+ ### Semrush API Reference
518
+
519
+ - Standard Analytics API: \`https://api.semrush.com/?type={report}&key={apiKey}&...\` \u2014 semicolon-separated CSV
520
+ - Trends API: \`https://api.semrush.com/analytics/v1/...\` \u2014 JSON
521
+ - Projects (Management) API: \`https://api.semrush.com/management/v1/...\` \u2014 JSON
522
+
523
+ Authentication: API key passed as the \`key\` query parameter on every request (handled automatically).
524
+
525
+ #### Common Standard Analytics report types
526
+ - \`domain_overview\` \u2014 domain summary (organic/paid traffic, keywords, backlinks)
527
+ - \`domain_organic\` \u2014 organic keywords for a domain
528
+ - \`domain_adwords\` \u2014 paid keywords for a domain
529
+ - \`domain_organic_organic\` / \`domain_adwords_adwords\` \u2014 organic / paid competitors
530
+ - \`phrase_this\` \u2014 keyword overview (search volume, CPC, competition, trend)
531
+ - \`phrase_related\` \u2014 related keywords
532
+ - \`phrase_fullsearch\` \u2014 full-text keyword research
533
+ - \`phrase_questions\` \u2014 question keywords
534
+ - \`phrase_kdi\` \u2014 keyword difficulty index
535
+ - \`backlinks_overview\` \u2014 backlinks summary
536
+ - \`backlinks\` \u2014 list of backlinks
537
+ - \`backlinks_refdomains\` \u2014 referring domains
538
+ - \`url_organic\` / \`url_adwords\` \u2014 keywords ranking for a specific URL
539
+ - \`user_units\` \u2014 remaining API units for the account
540
+
541
+ #### Common query parameters
542
+ - \`type\` \u2014 report type (required for the Standard API)
543
+ - \`domain\` / \`phrase\` / \`url\` \u2014 entity to query
544
+ - \`database\` \u2014 regional database (e.g. \`us\`, \`uk\`, \`de\`, \`fr\`, \`jp\`, \`br\`); required for most reports
545
+ - \`display_limit\` \u2014 page size (default 10000, max 100000 depending on report)
546
+ - \`display_offset\` \u2014 pagination offset
547
+ - \`display_date\` \u2014 historical date in \`YYYYMM15\` format (always day 15)
548
+ - \`export_columns\` \u2014 comma-separated columns to return (e.g. \`Ph,Po,Nq,Cp\`)
549
+ - \`display_sort\` \u2014 sort directive (e.g. \`tr_desc\`)
550
+ - \`display_filter\` \u2014 filter directive (e.g. \`+|Ph|Co|keyword\`)
551
+
552
+ #### Tips
553
+ - Each report consumes API units; check \`type=user_units\` first if you suspect a quota issue
554
+ - The CSV separator is \`;\` (semicolon), NOT \`,\`. Some cells may contain commas inside them.
555
+ - An HTTP 200 response with a body starting with \`ERROR\` indicates an API error (auth, parameters, or quota)
556
+ - The Trends API requires a separate Trends subscription; calls without it will fail with an authorization error
557
+ - Date strings in historical endpoints must be the 15th of the month (\`YYYYMM15\`)`,
558
+ ja: `### \u30C4\u30FC\u30EB
559
+
560
+ - \`semrush-api-key_request\`: Semrush API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002Standard Analytics API\uFF08\`/?type=...\` \u5F62\u5F0F\u306E\u30EC\u30DD\u30FC\u30C8\uFF09\u3001Trends API\uFF08\`/analytics/v1/...\`\uFF09\u3001Projects API\uFF08\`/management/v1/...\`\uFF09\u3059\u3079\u3066\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u884C\u308F\u308C\u3001API\u30AD\u30FC\u306F \`key\` \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3068\u3057\u3066\u4ED8\u4E0E\u3055\u308C\u308B\u305F\u3081\u3001\u81EA\u5206\u3067\u542B\u3081\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002Standard Analytics API \u306F1\u884C\u76EE\u304C\u30D8\u30C3\u30C0\u30FC\u306E\u30BB\u30DF\u30B3\u30ED\u30F3\u533A\u5207\u308ACSV\u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="text"\` \u3092\u4F7F\u7528\u3057\u3001Trends/Projects API \u306F JSON \u3092\u8FD4\u3059\u305F\u3081 \`responseFormat="json"\` \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002Standard API \u306E\u30A8\u30E9\u30FC\u306F HTTP 200 \u3067\u3082\u672C\u6587\u304C \`ERROR\` \u3067\u59CB\u307E\u308B\u5F62\u5F0F\u3067\u8FD4\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u304C\u3001\u30C4\u30FC\u30EB\u306F\u81EA\u52D5\u7684\u306B \`success: false\` \u3068\u3057\u3066\u8FD4\u3057\u307E\u3059\u3002
561
+
562
+ ### Business Logic
563
+
564
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
565
+
566
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
567
+ - \`client.request(path, init?)\` \u2014 \u8A8D\u8A3C\u4ED8\u304D\u306E\u4F4E\u30EC\u30D9\u30EBfetch\u3002\`init.query\` \u3067\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u6307\u5B9A\u3002\`key\` \u306F\u81EA\u52D5\u4ED8\u4E0E
568
+ - \`client.report(type, query?)\` \u2014 Standard Analytics \u306E\u30EC\u30DD\u30FC\u30C8\u3092\u547C\u3073\u51FA\u3057\u3001CSV\u3092 \`{ columns, rows, raw }\` \u306B\u30D1\u30FC\u30B9
569
+ - \`client.trends(path, query?)\` \u2014 Trends API \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08\`/analytics/v1/...\`\uFF09\u3092\u547C\u3073\u51FA\u3057 JSON \u3092\u8FD4\u3059
570
+ - \`client.projects(path, init?)\` \u2014 Projects API \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08\`/management/v1/...\`\uFF09\u3092\u547C\u3073\u51FA\u3057 JSON \u3092\u8FD4\u3059
571
+
572
+ \`\`\`ts
573
+ import type { Context } from "hono";
574
+ import { connection } from "@squadbase/vite-server/connectors/semrush";
575
+
576
+ const semrush = connection("<connectionId>");
577
+
578
+ export default async function handler(c: Context) {
579
+ const { domain = "example.com", database = "us" } = await c.req.json<{
580
+ domain?: string;
581
+ database?: string;
582
+ }>();
583
+
584
+ const overview = await semrush.report("domain_overview", { domain, database });
585
+
586
+ return c.json({ columns: overview.columns, rows: overview.rows });
587
+ }
588
+ \`\`\`
589
+
590
+ ### Semrush API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
591
+
592
+ - Standard Analytics API: \`https://api.semrush.com/?type={report}&key={apiKey}&...\` \u2014 \u30BB\u30DF\u30B3\u30ED\u30F3\u533A\u5207\u308ACSV
593
+ - Trends API: \`https://api.semrush.com/analytics/v1/...\` \u2014 JSON
594
+ - Projects (Management) API: \`https://api.semrush.com/management/v1/...\` \u2014 JSON
595
+
596
+ \u8A8D\u8A3C: API\u30AD\u30FC\u3092\u3059\u3079\u3066\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306B \`key\` \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3068\u3057\u3066\u4ED8\u4E0E\uFF08\u81EA\u52D5\uFF09\u3002
597
+
598
+ #### \u4E3B\u8981\u306A Standard Analytics \u30EC\u30DD\u30FC\u30C8\u30BF\u30A4\u30D7
599
+ - \`domain_overview\` \u2014 \u30C9\u30E1\u30A4\u30F3\u306E\u30B5\u30DE\u30EA\u30FC\uFF08\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF/\u6709\u6599\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u3001\u30AD\u30FC\u30EF\u30FC\u30C9\u3001\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\uFF09
600
+ - \`domain_organic\` \u2014 \u30C9\u30E1\u30A4\u30F3\u306E\u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\u30AD\u30FC\u30EF\u30FC\u30C9
601
+ - \`domain_adwords\` \u2014 \u30C9\u30E1\u30A4\u30F3\u306E\u6709\u6599\u30AD\u30FC\u30EF\u30FC\u30C9
602
+ - \`domain_organic_organic\` / \`domain_adwords_adwords\` \u2014 \u30AA\u30FC\u30AC\u30CB\u30C3\u30AF\uFF0F\u6709\u6599\u306E\u7AF6\u5408
603
+ - \`phrase_this\` \u2014 \u30AD\u30FC\u30EF\u30FC\u30C9\u6982\u8981\uFF08\u691C\u7D22\u30DC\u30EA\u30E5\u30FC\u30E0\u3001CPC\u3001\u7AF6\u5408\u5EA6\u3001\u30C8\u30EC\u30F3\u30C9\uFF09
604
+ - \`phrase_related\` \u2014 \u95A2\u9023\u30AD\u30FC\u30EF\u30FC\u30C9
605
+ - \`phrase_fullsearch\` \u2014 \u30D5\u30EB\u30C6\u30AD\u30B9\u30C8\u30AD\u30FC\u30EF\u30FC\u30C9\u30EA\u30B5\u30FC\u30C1
606
+ - \`phrase_questions\` \u2014 \u8CEA\u554F\u5F62\u5F0F\u30AD\u30FC\u30EF\u30FC\u30C9
607
+ - \`phrase_kdi\` \u2014 \u30AD\u30FC\u30EF\u30FC\u30C9\u96E3\u6613\u5EA6\uFF08KDI\uFF09
608
+ - \`backlinks_overview\` \u2014 \u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u6982\u8981
609
+ - \`backlinks\` \u2014 \u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u4E00\u89A7
610
+ - \`backlinks_refdomains\` \u2014 \u53C2\u7167\u30C9\u30E1\u30A4\u30F3
611
+ - \`url_organic\` / \`url_adwords\` \u2014 \u7279\u5B9AURL\u3067\u30E9\u30F3\u30AF\u30A4\u30F3\u3057\u3066\u3044\u308B\u30AD\u30FC\u30EF\u30FC\u30C9
612
+ - \`user_units\` \u2014 \u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u6B8BAPI\u30E6\u30CB\u30C3\u30C8\u6570
613
+
614
+ #### \u4E3B\u8981\u306A\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF
615
+ - \`type\` \u2014 \u30EC\u30DD\u30FC\u30C8\u7A2E\u5225\uFF08Standard API \u3067\u306F\u5FC5\u9808\uFF09
616
+ - \`domain\` / \`phrase\` / \`url\` \u2014 \u30AF\u30A8\u30EA\u5BFE\u8C61\u306E\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3
617
+ - \`database\` \u2014 \u5730\u57DF\u5225\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\uFF08\`us\`, \`uk\`, \`de\`, \`fr\`, \`jp\`, \`br\` \u306A\u3069\uFF09\u3002\u591A\u304F\u306E\u30EC\u30DD\u30FC\u30C8\u3067\u5FC5\u9808
618
+ - \`display_limit\` \u2014 \u30DA\u30FC\u30B8\u30B5\u30A4\u30BA\uFF08\u30C7\u30D5\u30A9\u30EB\u30C810000\u3001\u30EC\u30DD\u30FC\u30C8\u306B\u3088\u3063\u3066\u306F\u6700\u5927100000\uFF09
619
+ - \`display_offset\` \u2014 \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u30AA\u30D5\u30BB\u30C3\u30C8
620
+ - \`display_date\` \u2014 \u5C65\u6B74\u306E\u65E5\u4ED8\u3002\`YYYYMM15\` \u5F62\u5F0F\uFF08\u5FC5\u305A\u6708\u306E15\u65E5\uFF09
621
+ - \`export_columns\` \u2014 \u8FD4\u5374\u30AB\u30E9\u30E0\u3092\u30AB\u30F3\u30DE\u533A\u5207\u308A\u3067\u6307\u5B9A\uFF08\u4F8B: \`Ph,Po,Nq,Cp\`\uFF09
622
+ - \`display_sort\` \u2014 \u30BD\u30FC\u30C8\u6307\u5B9A\uFF08\u4F8B: \`tr_desc\`\uFF09
623
+ - \`display_filter\` \u2014 \u30D5\u30A3\u30EB\u30BF\u6307\u5B9A\uFF08\u4F8B: \`+|Ph|Co|keyword\`\uFF09
624
+
625
+ #### \u30D2\u30F3\u30C8
626
+ - \u5404\u30EC\u30DD\u30FC\u30C8\u306F API \u30E6\u30CB\u30C3\u30C8\u3092\u6D88\u8CBB\u3059\u308B\u3002\u30AF\u30A9\u30FC\u30BF\u304C\u7591\u308F\u3057\u3044\u5834\u5408\u306F \`type=user_units\` \u3067\u6B8B\u91CF\u3092\u5148\u306B\u78BA\u8A8D\u3059\u308B
627
+ - CSV \u306E\u30BB\u30D1\u30EC\u30FC\u30BF\u306F \`;\`\uFF08\u30BB\u30DF\u30B3\u30ED\u30F3\uFF09\u3067\u3042\u308A \`,\` \u3067\u306F\u306A\u3044\u3002\u30BB\u30EB\u5185\u306B\u30AB\u30F3\u30DE\u304C\u542B\u307E\u308C\u308B\u3053\u3068\u304C\u3042\u308B
628
+ - HTTP 200 \u3067\u3082\u672C\u6587\u304C \`ERROR\` \u3067\u59CB\u307E\u308B\u5834\u5408\u306F API\u30A8\u30E9\u30FC\uFF08\u8A8D\u8A3C\u3001\u30D1\u30E9\u30E1\u30FC\u30BF\u3001\u30AF\u30A9\u30FC\u30BF\uFF09
629
+ - Trends API \u306F\u5225\u9014 Trends \u30B5\u30D6\u30B9\u30AF\u30EA\u30D7\u30B7\u30E7\u30F3\u304C\u5FC5\u8981\u3002\u672A\u5951\u7D04\u3060\u3068\u8A8D\u53EF\u30A8\u30E9\u30FC\u306B\u306A\u308B
630
+ - \u5C65\u6B74\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306E\u65E5\u4ED8\u306F\u5FC5\u305A\u6708\u306E15\u65E5\uFF08\`YYYYMM15\`\uFF09`
631
+ },
632
+ tools,
633
+ async checkConnection(params) {
634
+ try {
635
+ const apiKey = params["api-key"];
636
+ if (!apiKey) {
637
+ return {
638
+ success: false,
639
+ error: "API Key is not configured"
640
+ };
641
+ }
642
+ const url = new URL("https://api.semrush.com/");
643
+ url.searchParams.set("type", "user_units");
644
+ url.searchParams.set("key", apiKey);
645
+ const res = await fetch(url.toString(), { method: "GET" });
646
+ const text = await res.text();
647
+ if (text.startsWith("ERROR ")) {
648
+ return { success: false, error: text.trim() };
649
+ }
650
+ if (!res.ok) {
651
+ return {
652
+ success: false,
653
+ error: `Semrush API failed: HTTP ${res.status} ${text || res.statusText}`
654
+ };
655
+ }
656
+ return { success: true };
657
+ } catch (error) {
658
+ return {
659
+ success: false,
660
+ error: error instanceof Error ? error.message : String(error)
661
+ };
662
+ }
663
+ }
664
+ });
665
+
666
+ // src/connectors/create-connector-sdk.ts
667
+ import { readFileSync } from "fs";
668
+ import path from "path";
669
+
670
+ // src/connector-client/env.ts
671
+ function resolveEnvVar(entry, key, connectionId) {
672
+ const envVarName = entry.envVars[key];
673
+ if (!envVarName) {
674
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
675
+ }
676
+ const value = process.env[envVarName];
677
+ if (!value) {
678
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
679
+ }
680
+ return value;
681
+ }
682
+ function resolveEnvVarOptional(entry, key) {
683
+ const envVarName = entry.envVars[key];
684
+ if (!envVarName) return void 0;
685
+ return process.env[envVarName] || void 0;
686
+ }
687
+
688
+ // src/connector-client/proxy-fetch.ts
689
+ import { getContext } from "hono/context-storage";
690
+ import { getCookie } from "hono/cookie";
691
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
692
+ function normalizeHeaders(input) {
693
+ const out = {};
694
+ if (!input) return out;
695
+ new Headers(input).forEach((value, key) => {
696
+ out[key] = value;
697
+ });
698
+ return out;
699
+ }
700
+ function createSandboxProxyFetch(connectionId) {
701
+ return async (input, init) => {
702
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
703
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
704
+ if (!token || !sandboxId) {
705
+ throw new Error(
706
+ "Connection proxy is not configured. Please check your deployment settings."
707
+ );
708
+ }
709
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
710
+ const originalMethod = init?.method ?? "GET";
711
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
712
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
713
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
714
+ return fetch(proxyUrl, {
715
+ method: "POST",
716
+ headers: {
717
+ "Content-Type": "application/json",
718
+ Authorization: `Bearer ${token}`
719
+ },
720
+ body: JSON.stringify({
721
+ url: originalUrl,
722
+ method: originalMethod,
723
+ headers: normalizeHeaders(init?.headers),
724
+ body: originalBody
725
+ })
726
+ });
727
+ };
728
+ }
729
+ function createDeployedAppProxyFetch(connectionId) {
730
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
731
+ if (!projectId) {
732
+ throw new Error(
733
+ "Connection proxy is not configured. Please check your deployment settings."
734
+ );
735
+ }
736
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
737
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
738
+ return async (input, init) => {
739
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
740
+ const originalMethod = init?.method ?? "GET";
741
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
742
+ const c = getContext();
743
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
744
+ if (!appSession) {
745
+ throw new Error(
746
+ "No authentication method available for connection proxy."
747
+ );
748
+ }
749
+ return fetch(proxyUrl, {
750
+ method: "POST",
751
+ headers: {
752
+ "Content-Type": "application/json",
753
+ Authorization: `Bearer ${appSession}`
754
+ },
755
+ body: JSON.stringify({
756
+ url: originalUrl,
757
+ method: originalMethod,
758
+ headers: normalizeHeaders(init?.headers),
759
+ body: originalBody
760
+ })
761
+ });
762
+ };
763
+ }
764
+ function createProxyFetch(connectionId) {
765
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
766
+ return createSandboxProxyFetch(connectionId);
767
+ }
768
+ return createDeployedAppProxyFetch(connectionId);
769
+ }
770
+
771
+ // src/connectors/create-connector-sdk.ts
772
+ function loadConnectionsSync() {
773
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
774
+ try {
775
+ const raw = readFileSync(filePath, "utf-8");
776
+ return JSON.parse(raw);
777
+ } catch {
778
+ return {};
779
+ }
780
+ }
781
+ function createConnectorSdk(plugin, createClient2) {
782
+ return (connectionId) => {
783
+ const connections = loadConnectionsSync();
784
+ const entry = connections[connectionId];
785
+ if (!entry) {
786
+ throw new Error(
787
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
788
+ );
789
+ }
790
+ if (entry.connector.slug !== plugin.slug) {
791
+ throw new Error(
792
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
793
+ );
794
+ }
795
+ const params = {};
796
+ for (const param of Object.values(plugin.parameters)) {
797
+ if (param.required) {
798
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
799
+ } else {
800
+ const val = resolveEnvVarOptional(entry, param.slug);
801
+ if (val !== void 0) params[param.slug] = val;
802
+ }
803
+ }
804
+ return createClient2(params, createProxyFetch(connectionId));
805
+ };
806
+ }
807
+
808
+ // src/connectors/entries/semrush.ts
809
+ var connection = createConnectorSdk(semrushConnector, createClient);
810
+ export {
811
+ connection
812
+ };