@squadbase/vite-server 0.1.9-dev.87dd3f7 → 0.1.9-dev.a57a0ac
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 +74937 -61829
- package/dist/connectors/asana.js +15 -2
- package/dist/connectors/aws-billing.d.ts +5 -0
- package/dist/connectors/aws-billing.js +29843 -0
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +657 -0
- package/dist/connectors/clickup.d.ts +5 -0
- package/dist/connectors/clickup.js +850 -0
- package/dist/connectors/freshdesk.d.ts +5 -0
- package/dist/connectors/freshdesk.js +842 -0
- package/dist/connectors/freshsales.d.ts +5 -0
- package/dist/connectors/freshsales.js +867 -0
- package/dist/connectors/freshservice.d.ts +5 -0
- package/dist/connectors/freshservice.js +813 -0
- package/dist/connectors/github.d.ts +5 -0
- package/dist/connectors/github.js +963 -0
- package/dist/connectors/gmail-oauth.js +15 -2
- package/dist/connectors/gmail.js +23 -14
- package/dist/connectors/google-audit-log.js +25 -14
- package/dist/connectors/google-calendar-oauth.js +18 -2
- package/dist/connectors/google-calendar.js +40 -26
- package/dist/connectors/google-docs.js +18 -2
- package/dist/connectors/google-drive.js +15 -2
- package/dist/connectors/google-search-console-oauth.d.ts +5 -0
- package/dist/connectors/google-search-console-oauth.js +954 -0
- package/dist/connectors/google-sheets.js +18 -2
- package/dist/connectors/google-slides.js +18 -2
- package/dist/connectors/jdbc.d.ts +5 -0
- package/dist/connectors/jdbc.js +21097 -0
- package/dist/connectors/monday.d.ts +5 -0
- package/dist/connectors/monday.js +853 -0
- package/dist/connectors/oracle.d.ts +5 -0
- package/dist/connectors/oracle.js +665 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +812 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +656 -0
- package/dist/connectors/supabase.d.ts +5 -0
- package/dist/connectors/supabase.js +582 -0
- package/dist/connectors/tiktok-ads.js +15 -2
- package/dist/index.js +73506 -60398
- package/dist/main.js +73500 -60392
- package/dist/vite-plugin.js +73405 -60297
- package/package.json +60 -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
|
+
};
|