@squadbase/vite-server 0.1.3-dev.12 → 0.1.3-dev.14
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 +3511 -3119
- package/dist/connectors/gamma.js +10 -3
- package/dist/connectors/google-ads.d.ts +1 -1
- package/dist/connectors/google-ads.js +343 -320
- package/dist/connectors/{linkedin-ads-oauth.d.ts → google-drive-oauth.d.ts} +1 -1
- package/dist/connectors/google-drive-oauth.js +879 -0
- package/dist/connectors/google-drive.d.ts +5 -0
- package/dist/connectors/google-drive.js +753 -0
- package/dist/connectors/google-sheets-oauth.js +127 -24
- package/dist/connectors/google-slides-oauth.d.ts +5 -0
- package/dist/connectors/google-slides-oauth.js +742 -0
- package/dist/connectors/google-slides.d.ts +5 -0
- package/dist/connectors/google-slides.js +748 -0
- package/dist/connectors/linkedin-ads.js +145 -162
- package/dist/connectors/{google-ads-oauth.d.ts → mixpanel.d.ts} +1 -1
- package/dist/connectors/mixpanel.js +742 -0
- package/dist/connectors/sentry.d.ts +5 -0
- package/dist/connectors/sentry.js +724 -0
- package/dist/index.js +3506 -3114
- package/dist/main.js +3506 -3114
- package/dist/vite-plugin.js +3506 -3114
- package/package.json +25 -9
- package/dist/connectors/google-ads-oauth.js +0 -890
- package/dist/connectors/linkedin-ads-oauth.js +0 -848
|
@@ -42,29 +42,8 @@ var ParameterDefinition = class {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// ../connectors/src/connectors/google-ads/sdk/index.ts
|
|
46
|
-
import * as crypto from "crypto";
|
|
47
|
-
|
|
48
45
|
// ../connectors/src/connectors/google-ads/parameters.ts
|
|
49
46
|
var parameters = {
|
|
50
|
-
serviceAccountKeyJsonBase64: new ParameterDefinition({
|
|
51
|
-
slug: "service-account-key-json-base64",
|
|
52
|
-
name: "Google Cloud Service Account JSON",
|
|
53
|
-
description: "The service account JSON key used to authenticate with Google Cloud Platform. Ensure that the service account has the necessary permissions to access Google Ads.",
|
|
54
|
-
envVarBaseKey: "GOOGLE_ADS_SERVICE_ACCOUNT_JSON_BASE64",
|
|
55
|
-
type: "base64EncodedJson",
|
|
56
|
-
secret: true,
|
|
57
|
-
required: true
|
|
58
|
-
}),
|
|
59
|
-
developerToken: new ParameterDefinition({
|
|
60
|
-
slug: "developer-token",
|
|
61
|
-
name: "Google Ads Developer Token",
|
|
62
|
-
description: "The developer token for accessing the Google Ads API. Required for all API requests.",
|
|
63
|
-
envVarBaseKey: "GOOGLE_ADS_DEVELOPER_TOKEN",
|
|
64
|
-
type: "text",
|
|
65
|
-
secret: true,
|
|
66
|
-
required: true
|
|
67
|
-
}),
|
|
68
47
|
customerId: new ParameterDefinition({
|
|
69
48
|
slug: "customer-id",
|
|
70
49
|
name: "Google Ads Customer ID",
|
|
@@ -74,176 +53,88 @@ var parameters = {
|
|
|
74
53
|
secret: false,
|
|
75
54
|
required: false
|
|
76
55
|
}),
|
|
77
|
-
|
|
78
|
-
slug: "
|
|
79
|
-
name: "
|
|
80
|
-
description: "The
|
|
81
|
-
envVarBaseKey: "
|
|
56
|
+
developerToken: new ParameterDefinition({
|
|
57
|
+
slug: "developer-token",
|
|
58
|
+
name: "Google Ads Developer Token",
|
|
59
|
+
description: "The developer token for accessing the Google Ads API. Required for all API requests.",
|
|
60
|
+
envVarBaseKey: "GOOGLE_ADS_DEVELOPER_TOKEN",
|
|
82
61
|
type: "text",
|
|
83
|
-
secret:
|
|
84
|
-
required:
|
|
62
|
+
secret: true,
|
|
63
|
+
required: true
|
|
85
64
|
})
|
|
86
65
|
};
|
|
87
66
|
|
|
88
67
|
// ../connectors/src/connectors/google-ads/sdk/index.ts
|
|
89
|
-
var TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
90
68
|
var BASE_URL = "https://googleads.googleapis.com/v18/";
|
|
91
|
-
|
|
92
|
-
function base64url(input) {
|
|
93
|
-
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
94
|
-
return buf.toString("base64url");
|
|
95
|
-
}
|
|
96
|
-
function buildJwt(clientEmail, privateKey, nowSec) {
|
|
97
|
-
const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
98
|
-
const payload = base64url(
|
|
99
|
-
JSON.stringify({
|
|
100
|
-
iss: clientEmail,
|
|
101
|
-
scope: SCOPE,
|
|
102
|
-
aud: TOKEN_URL,
|
|
103
|
-
iat: nowSec,
|
|
104
|
-
exp: nowSec + 3600
|
|
105
|
-
})
|
|
106
|
-
);
|
|
107
|
-
const signingInput = `${header}.${payload}`;
|
|
108
|
-
const sign = crypto.createSign("RSA-SHA256");
|
|
109
|
-
sign.update(signingInput);
|
|
110
|
-
sign.end();
|
|
111
|
-
const signature = base64url(sign.sign(privateKey));
|
|
112
|
-
return `${signingInput}.${signature}`;
|
|
113
|
-
}
|
|
114
|
-
function createClient(params) {
|
|
115
|
-
const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
116
|
-
const developerToken = params[parameters.developerToken.slug];
|
|
69
|
+
function createClient(params, fetchFn = fetch) {
|
|
117
70
|
const rawCustomerId = params[parameters.customerId.slug];
|
|
118
71
|
const defaultCustomerId = rawCustomerId?.replace(/-/g, "") ?? "";
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (!serviceAccountKeyJsonBase64 || !developerToken) {
|
|
122
|
-
const required = [
|
|
123
|
-
parameters.serviceAccountKeyJsonBase64.slug,
|
|
124
|
-
parameters.developerToken.slug
|
|
125
|
-
];
|
|
126
|
-
const missing = required.filter((s) => !params[s]);
|
|
127
|
-
throw new Error(
|
|
128
|
-
`google-ads: missing required parameters: ${missing.join(", ")}`
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
let serviceAccountKey;
|
|
132
|
-
try {
|
|
133
|
-
const decoded = Buffer.from(
|
|
134
|
-
serviceAccountKeyJsonBase64,
|
|
135
|
-
"base64"
|
|
136
|
-
).toString("utf-8");
|
|
137
|
-
serviceAccountKey = JSON.parse(decoded);
|
|
138
|
-
} catch {
|
|
72
|
+
const developerToken = params[parameters.developerToken.slug];
|
|
73
|
+
if (!developerToken) {
|
|
139
74
|
throw new Error(
|
|
140
|
-
|
|
75
|
+
`google-ads: missing required parameter: ${parameters.developerToken.slug}`
|
|
141
76
|
);
|
|
142
77
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
78
|
+
function resolveCustomerId(override) {
|
|
79
|
+
const id = override?.replace(/-/g, "") ?? defaultCustomerId;
|
|
80
|
+
if (!id) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
"google-ads: customerId is required. Either configure a default or pass it explicitly."
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return id;
|
|
147
86
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
87
|
+
function request(path2, init) {
|
|
88
|
+
const resolvedPath = defaultCustomerId ? path2.replace(/\{customerId\}/g, defaultCustomerId) : path2;
|
|
89
|
+
const url = `${BASE_URL}${resolvedPath}`;
|
|
90
|
+
const headers = new Headers(init?.headers);
|
|
91
|
+
headers.set("developer-token", developerToken);
|
|
92
|
+
if (defaultCustomerId) {
|
|
93
|
+
headers.set("login-customer-id", defaultCustomerId);
|
|
154
94
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const
|
|
95
|
+
return fetchFn(url, { ...init, headers });
|
|
96
|
+
}
|
|
97
|
+
async function search(query, customerId) {
|
|
98
|
+
const cid = resolveCustomerId(customerId);
|
|
99
|
+
const url = `${BASE_URL}customers/${cid}/googleAds:searchStream`;
|
|
100
|
+
const headers = new Headers();
|
|
101
|
+
headers.set("Content-Type", "application/json");
|
|
102
|
+
headers.set("developer-token", developerToken);
|
|
103
|
+
headers.set("login-customer-id", cid);
|
|
104
|
+
const response = await fetchFn(url, {
|
|
161
105
|
method: "POST",
|
|
162
|
-
headers
|
|
163
|
-
body:
|
|
164
|
-
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
165
|
-
assertion: jwt
|
|
166
|
-
})
|
|
106
|
+
headers,
|
|
107
|
+
body: JSON.stringify({ query })
|
|
167
108
|
});
|
|
168
109
|
if (!response.ok) {
|
|
169
|
-
const
|
|
110
|
+
const body = await response.text();
|
|
170
111
|
throw new Error(
|
|
171
|
-
`google-ads:
|
|
112
|
+
`google-ads: search failed (${response.status}): ${body}`
|
|
172
113
|
);
|
|
173
114
|
}
|
|
174
115
|
const data = await response.json();
|
|
175
|
-
|
|
176
|
-
tokenExpiresAt = nowSec + data.expires_in;
|
|
177
|
-
return cachedToken;
|
|
116
|
+
return data.flatMap((chunk) => chunk.results ?? []);
|
|
178
117
|
}
|
|
179
|
-
function
|
|
180
|
-
const
|
|
181
|
-
|
|
118
|
+
async function listAccessibleCustomers() {
|
|
119
|
+
const url = `${BASE_URL}customers:listAccessibleCustomers`;
|
|
120
|
+
const headers = new Headers();
|
|
121
|
+
headers.set("developer-token", developerToken);
|
|
122
|
+
const response = await fetchFn(url, { method: "GET", headers });
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const body = await response.text();
|
|
182
125
|
throw new Error(
|
|
183
|
-
|
|
126
|
+
`google-ads: listAccessibleCustomers failed (${response.status}): ${body}`
|
|
184
127
|
);
|
|
185
128
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
129
|
+
const data = await response.json();
|
|
130
|
+
return (data.resourceNames ?? []).map(
|
|
131
|
+
(rn) => rn.replace(/^customers\//, "")
|
|
132
|
+
);
|
|
190
133
|
}
|
|
191
134
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const url = `${BASE_URL}${resolvedPath}`;
|
|
196
|
-
const headers = new Headers(init?.headers);
|
|
197
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
198
|
-
headers.set("developer-token", developerToken);
|
|
199
|
-
const lid = getLoginCustomerId();
|
|
200
|
-
if (lid) {
|
|
201
|
-
headers.set("login-customer-id", lid);
|
|
202
|
-
}
|
|
203
|
-
return fetch(url, { ...init, headers });
|
|
204
|
-
},
|
|
205
|
-
async search(query, customerId) {
|
|
206
|
-
const cid = resolveCustomerId(customerId);
|
|
207
|
-
const accessToken = await getAccessToken();
|
|
208
|
-
const url = `${BASE_URL}customers/${cid}/googleAds:searchStream`;
|
|
209
|
-
const headers = new Headers();
|
|
210
|
-
headers.set("Content-Type", "application/json");
|
|
211
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
212
|
-
headers.set("developer-token", developerToken);
|
|
213
|
-
const lid = loginCustomerId || cid;
|
|
214
|
-
headers.set("login-customer-id", lid);
|
|
215
|
-
const response = await fetch(url, {
|
|
216
|
-
method: "POST",
|
|
217
|
-
headers,
|
|
218
|
-
body: JSON.stringify({ query })
|
|
219
|
-
});
|
|
220
|
-
if (!response.ok) {
|
|
221
|
-
const body = await response.text();
|
|
222
|
-
throw new Error(
|
|
223
|
-
`google-ads: search failed (${response.status}): ${body}`
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
const data = await response.json();
|
|
227
|
-
return data.flatMap((chunk) => chunk.results ?? []);
|
|
228
|
-
},
|
|
229
|
-
async listAccessibleCustomers() {
|
|
230
|
-
const accessToken = await getAccessToken();
|
|
231
|
-
const url = `${BASE_URL}customers:listAccessibleCustomers`;
|
|
232
|
-
const headers = new Headers();
|
|
233
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
234
|
-
headers.set("developer-token", developerToken);
|
|
235
|
-
const response = await fetch(url, { method: "GET", headers });
|
|
236
|
-
if (!response.ok) {
|
|
237
|
-
const body = await response.text();
|
|
238
|
-
throw new Error(
|
|
239
|
-
`google-ads: listAccessibleCustomers failed (${response.status}): ${body}`
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
const data = await response.json();
|
|
243
|
-
return (data.resourceNames ?? []).map(
|
|
244
|
-
(rn) => rn.replace(/^customers\//, "")
|
|
245
|
-
);
|
|
246
|
-
}
|
|
135
|
+
request,
|
|
136
|
+
search,
|
|
137
|
+
listAccessibleCustomers
|
|
247
138
|
};
|
|
248
139
|
}
|
|
249
140
|
|
|
@@ -357,50 +248,68 @@ var AUTH_TYPES = {
|
|
|
357
248
|
USER_PASSWORD: "user-password"
|
|
358
249
|
};
|
|
359
250
|
|
|
360
|
-
// ../connectors/src/connectors/google-ads/
|
|
361
|
-
var googleAdsOnboarding = new ConnectorOnboarding({
|
|
362
|
-
dataOverviewInstructions: {
|
|
363
|
-
en: `1. Call google-ads_request with POST customers/{customerId}/googleAds:searchStream to explore available campaign data using GAQL: SELECT campaign.id, campaign.name, campaign.status FROM campaign LIMIT 10
|
|
364
|
-
2. Explore ad group and keyword data as needed to understand the data structure`,
|
|
365
|
-
ja: `1. google-ads_request \u3067 POST customers/{customerId}/googleAds:searchStream \u3092\u547C\u3073\u51FA\u3057\u3001GAQL\u3067\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30C7\u30FC\u30BF\u3092\u63A2\u7D22: SELECT campaign.id, campaign.name, campaign.status FROM campaign LIMIT 10
|
|
366
|
-
2. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u5E83\u544A\u30B0\u30EB\u30FC\u30D7\u3084\u30AD\u30FC\u30EF\u30FC\u30C9\u30C7\u30FC\u30BF\u3092\u63A2\u7D22\u3057\u3001\u30C7\u30FC\u30BF\u69CB\u9020\u3092\u628A\u63E1`
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// ../connectors/src/connectors/google-ads/tools/request.ts
|
|
251
|
+
// ../connectors/src/connectors/google-ads/tools/list-customers.ts
|
|
371
252
|
import { z } from "zod";
|
|
372
253
|
var BASE_URL2 = "https://googleads.googleapis.com/v18/";
|
|
373
254
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
255
|
+
var cachedToken = null;
|
|
256
|
+
async function getProxyToken(config) {
|
|
257
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
258
|
+
return cachedToken.token;
|
|
259
|
+
}
|
|
260
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
261
|
+
const res = await fetch(url, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: {
|
|
264
|
+
"Content-Type": "application/json",
|
|
265
|
+
"x-api-key": config.appApiKey,
|
|
266
|
+
"project-id": config.projectId
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
sandboxId: config.sandboxId,
|
|
270
|
+
issuedBy: "coding-agent"
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
if (!res.ok) {
|
|
274
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
const data = await res.json();
|
|
280
|
+
cachedToken = {
|
|
281
|
+
token: data.token,
|
|
282
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
283
|
+
};
|
|
284
|
+
return data.token;
|
|
285
|
+
}
|
|
374
286
|
var inputSchema = z.object({
|
|
375
287
|
toolUseIntent: z.string().optional().describe(
|
|
376
288
|
"Brief description of what you intend to accomplish with this tool call"
|
|
377
289
|
),
|
|
378
|
-
connectionId: z.string().describe("ID of the Google Ads connection to use")
|
|
379
|
-
method: z.enum(["GET", "POST"]).describe("HTTP method"),
|
|
380
|
-
path: z.string().describe(
|
|
381
|
-
"API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
|
|
382
|
-
),
|
|
383
|
-
body: z.record(z.string(), z.unknown()).optional().describe("POST request body (JSON)")
|
|
290
|
+
connectionId: z.string().describe("ID of the Google Ads OAuth connection to use")
|
|
384
291
|
});
|
|
385
292
|
var outputSchema = z.discriminatedUnion("success", [
|
|
386
293
|
z.object({
|
|
387
294
|
success: z.literal(true),
|
|
388
|
-
|
|
389
|
-
|
|
295
|
+
customers: z.array(
|
|
296
|
+
z.object({
|
|
297
|
+
customerId: z.string(),
|
|
298
|
+
descriptiveName: z.string()
|
|
299
|
+
})
|
|
300
|
+
)
|
|
390
301
|
}),
|
|
391
302
|
z.object({
|
|
392
303
|
success: z.literal(false),
|
|
393
304
|
error: z.string()
|
|
394
305
|
})
|
|
395
306
|
]);
|
|
396
|
-
var
|
|
397
|
-
name: "
|
|
398
|
-
description:
|
|
399
|
-
Authentication is handled automatically using a service account.
|
|
400
|
-
{customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
|
|
307
|
+
var listCustomersTool = new ConnectorTool({
|
|
308
|
+
name: "listCustomers",
|
|
309
|
+
description: "List Google Ads customer accounts accessible with the current OAuth credentials.",
|
|
401
310
|
inputSchema,
|
|
402
311
|
outputSchema,
|
|
403
|
-
async execute({ connectionId
|
|
312
|
+
async execute({ connectionId }, connections, config) {
|
|
404
313
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
405
314
|
if (!connection2) {
|
|
406
315
|
return {
|
|
@@ -409,57 +318,82 @@ Authentication is handled automatically using a service account.
|
|
|
409
318
|
};
|
|
410
319
|
}
|
|
411
320
|
console.log(
|
|
412
|
-
`[connector-request] google-ads/${connection2.name}:
|
|
321
|
+
`[connector-request] google-ads/${connection2.name}: listCustomers`
|
|
413
322
|
);
|
|
414
323
|
try {
|
|
415
|
-
const { GoogleAuth } = await import("google-auth-library");
|
|
416
|
-
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
417
324
|
const developerToken = parameters.developerToken.getValue(connection2);
|
|
418
|
-
const
|
|
419
|
-
const
|
|
420
|
-
const rawLoginCustomerId = parameters.loginCustomerId.tryGetValue(connection2);
|
|
421
|
-
const loginCustomerId = rawLoginCustomerId?.replace(/-/g, "") ?? "";
|
|
422
|
-
const credentials = JSON.parse(
|
|
423
|
-
Buffer.from(keyJsonBase64, "base64").toString("utf-8")
|
|
424
|
-
);
|
|
425
|
-
const auth = new GoogleAuth({
|
|
426
|
-
credentials,
|
|
427
|
-
scopes: ["https://www.googleapis.com/auth/adwords"]
|
|
428
|
-
});
|
|
429
|
-
const token = await auth.getAccessToken();
|
|
430
|
-
if (!token) {
|
|
431
|
-
return {
|
|
432
|
-
success: false,
|
|
433
|
-
error: "Failed to obtain access token"
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
const resolvedPath = customerId ? path2.replace(/\{customerId\}/g, customerId) : path2;
|
|
437
|
-
const url = `${BASE_URL2}${resolvedPath}`;
|
|
325
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
326
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
438
327
|
const controller = new AbortController();
|
|
439
328
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
440
329
|
try {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
method,
|
|
330
|
+
const response = await fetch(proxyUrl, {
|
|
331
|
+
method: "POST",
|
|
444
332
|
headers: {
|
|
445
|
-
Authorization: `Bearer ${token}`,
|
|
446
333
|
"Content-Type": "application/json",
|
|
447
|
-
|
|
448
|
-
...lid ? { "login-customer-id": lid } : {}
|
|
334
|
+
Authorization: `Bearer ${token}`
|
|
449
335
|
},
|
|
450
|
-
body:
|
|
336
|
+
body: JSON.stringify({
|
|
337
|
+
url: `${BASE_URL2}customers:listAccessibleCustomers`,
|
|
338
|
+
method: "GET",
|
|
339
|
+
headers: {
|
|
340
|
+
"developer-token": developerToken
|
|
341
|
+
}
|
|
342
|
+
}),
|
|
451
343
|
signal: controller.signal
|
|
452
344
|
});
|
|
453
345
|
const data = await response.json();
|
|
454
346
|
if (!response.ok) {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
460
|
-
};
|
|
347
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
348
|
+
return { success: false, error: errorMessage };
|
|
461
349
|
}
|
|
462
|
-
|
|
350
|
+
const customerIds = (data.resourceNames ?? []).map(
|
|
351
|
+
(rn) => rn.replace(/^customers\//, "")
|
|
352
|
+
);
|
|
353
|
+
const customers = [];
|
|
354
|
+
for (const cid of customerIds) {
|
|
355
|
+
try {
|
|
356
|
+
const detailResponse = await fetch(proxyUrl, {
|
|
357
|
+
method: "POST",
|
|
358
|
+
headers: {
|
|
359
|
+
"Content-Type": "application/json",
|
|
360
|
+
Authorization: `Bearer ${token}`
|
|
361
|
+
},
|
|
362
|
+
body: JSON.stringify({
|
|
363
|
+
url: `${BASE_URL2}customers/${cid}/googleAds:searchStream`,
|
|
364
|
+
method: "POST",
|
|
365
|
+
headers: {
|
|
366
|
+
"Content-Type": "application/json",
|
|
367
|
+
"developer-token": developerToken,
|
|
368
|
+
"login-customer-id": cid
|
|
369
|
+
},
|
|
370
|
+
body: JSON.stringify({
|
|
371
|
+
query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
|
|
372
|
+
})
|
|
373
|
+
}),
|
|
374
|
+
signal: controller.signal
|
|
375
|
+
});
|
|
376
|
+
if (detailResponse.ok) {
|
|
377
|
+
const detailData = await detailResponse.json();
|
|
378
|
+
const customer = detailData?.[0]?.results?.[0]?.customer;
|
|
379
|
+
customers.push({
|
|
380
|
+
customerId: cid,
|
|
381
|
+
descriptiveName: customer?.descriptiveName ?? cid
|
|
382
|
+
});
|
|
383
|
+
} else {
|
|
384
|
+
customers.push({
|
|
385
|
+
customerId: cid,
|
|
386
|
+
descriptiveName: cid
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
390
|
+
customers.push({
|
|
391
|
+
customerId: cid,
|
|
392
|
+
descriptiveName: cid
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return { success: true, customers };
|
|
463
397
|
} finally {
|
|
464
398
|
clearTimeout(timeout);
|
|
465
399
|
}
|
|
@@ -470,37 +404,122 @@ Authentication is handled automatically using a service account.
|
|
|
470
404
|
}
|
|
471
405
|
});
|
|
472
406
|
|
|
473
|
-
// ../connectors/src/connectors/google-ads/
|
|
407
|
+
// ../connectors/src/connectors/google-ads/setup.ts
|
|
408
|
+
var listCustomersToolName = `google-ads_${listCustomersTool.name}`;
|
|
409
|
+
var googleAdsOnboarding = new ConnectorOnboarding({
|
|
410
|
+
connectionSetupInstructions: {
|
|
411
|
+
ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Ads (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
412
|
+
|
|
413
|
+
1. \u30E6\u30FC\u30B6\u30FC\u306B\u300CGoogle Ads API \u306E Developer Token \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08Google Ads \u7BA1\u7406\u753B\u9762 > \u30C4\u30FC\u30EB\u3068\u8A2D\u5B9A > API \u30BB\u30F3\u30BF\u30FC\u3067\u53D6\u5F97\u3067\u304D\u307E\u3059\uFF09\u300D\u3068\u4F1D\u3048\u308B
|
|
414
|
+
2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
415
|
+
- \`parameterSlug\`: \`"developer-token"\`
|
|
416
|
+
- \`value\`: \u30E6\u30FC\u30B6\u30FC\u304C\u63D0\u4F9B\u3057\u305F Developer Token
|
|
417
|
+
3. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
418
|
+
4. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
419
|
+
- \`parameterSlug\`: \`"customer-id"\`
|
|
420
|
+
- \`options\`: \u30AB\u30B9\u30BF\u30DE\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30AB\u30B9\u30BF\u30DE\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30B9\u30BF\u30DE\u30FCID
|
|
421
|
+
5. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080
|
|
422
|
+
6. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
|
|
423
|
+
- \`customer\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E\u8868\u793A\u540D
|
|
424
|
+
- \`customerId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FCID
|
|
425
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
426
|
+
|
|
427
|
+
#### \u5236\u7D04
|
|
428
|
+
- **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30EC\u30DD\u30FC\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\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\u306E\u307F
|
|
429
|
+
- \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`,
|
|
430
|
+
en: `Follow these steps to set up the Google Ads (OAuth) connection.
|
|
431
|
+
|
|
432
|
+
1. Ask the user to provide their Google Ads API Developer Token (available in Google Ads UI > Tools & Settings > API Center)
|
|
433
|
+
2. Call \`updateConnectionParameters\`:
|
|
434
|
+
- \`parameterSlug\`: \`"developer-token"\`
|
|
435
|
+
- \`value\`: The Developer Token provided by the user
|
|
436
|
+
3. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
|
|
437
|
+
4. Call \`updateConnectionParameters\`:
|
|
438
|
+
- \`parameterSlug\`: \`"customer-id"\`
|
|
439
|
+
- \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
|
|
440
|
+
5. The \`label\` of the user's selected customer will arrive as a message. Proceed to the next step
|
|
441
|
+
6. Call \`updateConnectionContext\`:
|
|
442
|
+
- \`customer\`: The selected customer's display name
|
|
443
|
+
- \`customerId\`: The selected customer ID
|
|
444
|
+
- \`note\`: Brief description of the setup
|
|
445
|
+
|
|
446
|
+
#### Constraints
|
|
447
|
+
- **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
|
|
448
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
|
|
449
|
+
},
|
|
450
|
+
dataOverviewInstructions: {
|
|
451
|
+
en: `1. Call google-ads_request with POST customers/{customerId}/googleAds:searchStream to explore available campaign data using GAQL: SELECT campaign.id, campaign.name, campaign.status FROM campaign LIMIT 10
|
|
452
|
+
2. Explore ad group and keyword data as needed to understand the data structure`,
|
|
453
|
+
ja: `1. google-ads_request \u3067 POST customers/{customerId}/googleAds:searchStream \u3092\u547C\u3073\u51FA\u3057\u3001GAQL\u3067\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30C7\u30FC\u30BF\u3092\u63A2\u7D22: SELECT campaign.id, campaign.name, campaign.status FROM campaign LIMIT 10
|
|
454
|
+
2. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u5E83\u544A\u30B0\u30EB\u30FC\u30D7\u3084\u30AD\u30FC\u30EF\u30FC\u30C9\u30C7\u30FC\u30BF\u3092\u63A2\u7D22\u3057\u3001\u30C7\u30FC\u30BF\u69CB\u9020\u3092\u628A\u63E1`
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// ../connectors/src/connectors/google-ads/tools/request.ts
|
|
474
459
|
import { z as z2 } from "zod";
|
|
475
460
|
var BASE_URL3 = "https://googleads.googleapis.com/v18/";
|
|
476
461
|
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
462
|
+
var cachedToken2 = null;
|
|
463
|
+
async function getProxyToken2(config) {
|
|
464
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
465
|
+
return cachedToken2.token;
|
|
466
|
+
}
|
|
467
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
468
|
+
const res = await fetch(url, {
|
|
469
|
+
method: "POST",
|
|
470
|
+
headers: {
|
|
471
|
+
"Content-Type": "application/json",
|
|
472
|
+
"x-api-key": config.appApiKey,
|
|
473
|
+
"project-id": config.projectId
|
|
474
|
+
},
|
|
475
|
+
body: JSON.stringify({
|
|
476
|
+
sandboxId: config.sandboxId,
|
|
477
|
+
issuedBy: "coding-agent"
|
|
478
|
+
})
|
|
479
|
+
});
|
|
480
|
+
if (!res.ok) {
|
|
481
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
482
|
+
throw new Error(
|
|
483
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
const data = await res.json();
|
|
487
|
+
cachedToken2 = {
|
|
488
|
+
token: data.token,
|
|
489
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
490
|
+
};
|
|
491
|
+
return data.token;
|
|
492
|
+
}
|
|
477
493
|
var inputSchema2 = z2.object({
|
|
478
494
|
toolUseIntent: z2.string().optional().describe(
|
|
479
495
|
"Brief description of what you intend to accomplish with this tool call"
|
|
480
496
|
),
|
|
481
|
-
connectionId: z2.string().describe("ID of the Google Ads connection to use")
|
|
497
|
+
connectionId: z2.string().describe("ID of the Google Ads OAuth connection to use"),
|
|
498
|
+
method: z2.enum(["GET", "POST"]).describe("HTTP method"),
|
|
499
|
+
path: z2.string().describe(
|
|
500
|
+
"API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
|
|
501
|
+
),
|
|
502
|
+
body: z2.record(z2.string(), z2.unknown()).optional().describe("POST request body (JSON)")
|
|
482
503
|
});
|
|
483
504
|
var outputSchema2 = z2.discriminatedUnion("success", [
|
|
484
505
|
z2.object({
|
|
485
506
|
success: z2.literal(true),
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
customerId: z2.string(),
|
|
489
|
-
descriptiveName: z2.string()
|
|
490
|
-
})
|
|
491
|
-
)
|
|
507
|
+
status: z2.number(),
|
|
508
|
+
data: z2.unknown()
|
|
492
509
|
}),
|
|
493
510
|
z2.object({
|
|
494
511
|
success: z2.literal(false),
|
|
495
512
|
error: z2.string()
|
|
496
513
|
})
|
|
497
514
|
]);
|
|
498
|
-
var
|
|
499
|
-
name: "
|
|
500
|
-
description:
|
|
515
|
+
var requestTool = new ConnectorTool({
|
|
516
|
+
name: "request",
|
|
517
|
+
description: `Send authenticated requests to the Google Ads API v18.
|
|
518
|
+
Authentication is handled automatically via OAuth proxy.
|
|
519
|
+
{customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
|
|
501
520
|
inputSchema: inputSchema2,
|
|
502
521
|
outputSchema: outputSchema2,
|
|
503
|
-
async execute({ connectionId }, connections) {
|
|
522
|
+
async execute({ connectionId, method, path: path2, body }, connections, config) {
|
|
504
523
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
505
524
|
if (!connection2) {
|
|
506
525
|
return {
|
|
@@ -509,90 +528,44 @@ var listCustomersTool = new ConnectorTool({
|
|
|
509
528
|
};
|
|
510
529
|
}
|
|
511
530
|
console.log(
|
|
512
|
-
`[connector-request] google-ads/${connection2.name}:
|
|
531
|
+
`[connector-request] google-ads/${connection2.name}: ${method} ${path2}`
|
|
513
532
|
);
|
|
514
533
|
try {
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const auth = new GoogleAuth({
|
|
522
|
-
credentials,
|
|
523
|
-
scopes: ["https://www.googleapis.com/auth/adwords"]
|
|
524
|
-
});
|
|
525
|
-
const token = await auth.getAccessToken();
|
|
526
|
-
if (!token) {
|
|
527
|
-
return {
|
|
528
|
-
success: false,
|
|
529
|
-
error: "Failed to obtain access token"
|
|
530
|
-
};
|
|
531
|
-
}
|
|
534
|
+
const rawCustomerId = parameters.customerId.tryGetValue(connection2);
|
|
535
|
+
const customerId = rawCustomerId?.replace(/-/g, "") ?? "";
|
|
536
|
+
const resolvedPath = customerId ? path2.replace(/\{customerId\}/g, customerId) : path2;
|
|
537
|
+
const url = `${BASE_URL3}${resolvedPath}`;
|
|
538
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
539
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
532
540
|
const controller = new AbortController();
|
|
533
541
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
534
542
|
try {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
543
|
+
const developerToken = parameters.developerToken.getValue(connection2);
|
|
544
|
+
const response = await fetch(proxyUrl, {
|
|
545
|
+
method: "POST",
|
|
546
|
+
headers: {
|
|
547
|
+
"Content-Type": "application/json",
|
|
548
|
+
Authorization: `Bearer ${token}`
|
|
549
|
+
},
|
|
550
|
+
body: JSON.stringify({
|
|
551
|
+
url,
|
|
552
|
+
method,
|
|
539
553
|
headers: {
|
|
540
|
-
|
|
541
|
-
"developer-token": developerToken
|
|
554
|
+
"Content-Type": "application/json",
|
|
555
|
+
"developer-token": developerToken,
|
|
556
|
+
...customerId ? { "login-customer-id": customerId } : {}
|
|
542
557
|
},
|
|
543
|
-
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const customerIds = (listData.resourceNames ?? []).map(
|
|
554
|
-
(rn) => rn.replace(/^customers\//, "")
|
|
555
|
-
);
|
|
556
|
-
const customers = [];
|
|
557
|
-
for (const cid of customerIds) {
|
|
558
|
-
try {
|
|
559
|
-
const detailResponse = await fetch(
|
|
560
|
-
`${BASE_URL3}customers/${cid}/googleAds:searchStream`,
|
|
561
|
-
{
|
|
562
|
-
method: "POST",
|
|
563
|
-
headers: {
|
|
564
|
-
Authorization: `Bearer ${token}`,
|
|
565
|
-
"Content-Type": "application/json",
|
|
566
|
-
"developer-token": developerToken,
|
|
567
|
-
"login-customer-id": cid
|
|
568
|
-
},
|
|
569
|
-
body: JSON.stringify({
|
|
570
|
-
query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
|
|
571
|
-
}),
|
|
572
|
-
signal: controller.signal
|
|
573
|
-
}
|
|
574
|
-
);
|
|
575
|
-
if (detailResponse.ok) {
|
|
576
|
-
const detailData = await detailResponse.json();
|
|
577
|
-
const customer = detailData?.[0]?.results?.[0]?.customer;
|
|
578
|
-
customers.push({
|
|
579
|
-
customerId: cid,
|
|
580
|
-
descriptiveName: customer?.descriptiveName ?? cid
|
|
581
|
-
});
|
|
582
|
-
} else {
|
|
583
|
-
customers.push({
|
|
584
|
-
customerId: cid,
|
|
585
|
-
descriptiveName: cid
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
} catch {
|
|
589
|
-
customers.push({
|
|
590
|
-
customerId: cid,
|
|
591
|
-
descriptiveName: cid
|
|
592
|
-
});
|
|
593
|
-
}
|
|
558
|
+
...method === "POST" && body ? { body: JSON.stringify(body) } : {}
|
|
559
|
+
}),
|
|
560
|
+
signal: controller.signal
|
|
561
|
+
});
|
|
562
|
+
const data = await response.json();
|
|
563
|
+
if (!response.ok) {
|
|
564
|
+
const dataObj = data;
|
|
565
|
+
const errorMessage = typeof dataObj?.error === "string" ? dataObj.error : typeof dataObj?.message === "string" ? dataObj.message : `HTTP ${response.status} ${response.statusText}`;
|
|
566
|
+
return { success: false, error: errorMessage };
|
|
594
567
|
}
|
|
595
|
-
return { success: true,
|
|
568
|
+
return { success: true, status: response.status, data };
|
|
596
569
|
} finally {
|
|
597
570
|
clearTimeout(timeout);
|
|
598
571
|
}
|
|
@@ -610,17 +583,25 @@ var tools = {
|
|
|
610
583
|
};
|
|
611
584
|
var googleAdsConnector = new ConnectorPlugin({
|
|
612
585
|
slug: "google-ads",
|
|
613
|
-
authType: AUTH_TYPES.
|
|
586
|
+
authType: AUTH_TYPES.OAUTH,
|
|
614
587
|
name: "Google Ads",
|
|
615
|
-
description: "Connect to Google Ads for advertising campaign data and reporting using
|
|
588
|
+
description: "Connect to Google Ads for advertising campaign data and reporting using OAuth.",
|
|
616
589
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1NGvmgvCxX7Tn11EST2N3N/a745fe7c63d360ed40a27ddaad3af168/google-ads.svg",
|
|
617
590
|
parameters,
|
|
618
591
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
619
592
|
onboarding: googleAdsOnboarding,
|
|
593
|
+
proxyPolicy: {
|
|
594
|
+
allowlist: [
|
|
595
|
+
{
|
|
596
|
+
host: "googleads.googleapis.com",
|
|
597
|
+
methods: ["GET", "POST"]
|
|
598
|
+
}
|
|
599
|
+
]
|
|
600
|
+
},
|
|
620
601
|
systemPrompt: {
|
|
621
602
|
en: `### Tools
|
|
622
603
|
|
|
623
|
-
- \`google-ads_request\`: Send authenticated requests to the Google Ads API. Use it for GAQL queries via searchStream. The {customerId} placeholder in paths is automatically replaced (hyphens removed). Authentication
|
|
604
|
+
- \`google-ads_request\`: Send authenticated requests to the Google Ads API. Use it for GAQL queries via searchStream. The {customerId} placeholder in paths is automatically replaced (hyphens removed). Authentication and developer token are configured automatically.
|
|
624
605
|
- \`google-ads_listCustomers\`: List accessible Google Ads customer accounts. Use this during setup to discover available accounts.
|
|
625
606
|
|
|
626
607
|
### Google Ads API Reference
|
|
@@ -673,7 +654,7 @@ const customerIds = await ads.listAccessibleCustomers();
|
|
|
673
654
|
\`\`\``,
|
|
674
655
|
ja: `### \u30C4\u30FC\u30EB
|
|
675
656
|
|
|
676
|
-
- \`google-ads_request\`: Google Ads API\u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002searchStream\u3092\u4F7F\u3063\u305FGAQL\u30AF\u30A8\u30EA\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{customerId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\uFF08\u30CF\u30A4\u30D5\u30F3\u306F\u9664\u53BB\uFF09\u3002\
|
|
657
|
+
- \`google-ads_request\`: Google Ads API\u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002searchStream\u3092\u4F7F\u3063\u305FGAQL\u30AF\u30A8\u30EA\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{customerId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\uFF08\u30CF\u30A4\u30D5\u30F3\u306F\u9664\u53BB\uFF09\u3002\u8A8D\u8A3C\u3068\u30C7\u30D9\u30ED\u30C3\u30D1\u30FC\u30C8\u30FC\u30AF\u30F3\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
|
|
677
658
|
- \`google-ads_listCustomers\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u9867\u5BA2\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u5229\u7528\u53EF\u80FD\u306A\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u78BA\u8A8D\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002
|
|
678
659
|
|
|
679
660
|
### Google Ads API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
@@ -725,7 +706,49 @@ rows.forEach(row => console.log(row));
|
|
|
725
706
|
const customerIds = await ads.listAccessibleCustomers();
|
|
726
707
|
\`\`\``
|
|
727
708
|
},
|
|
728
|
-
tools
|
|
709
|
+
tools,
|
|
710
|
+
async checkConnection(params, config) {
|
|
711
|
+
const { proxyFetch } = config;
|
|
712
|
+
const rawCustomerId = params[parameters.customerId.slug];
|
|
713
|
+
const customerId = rawCustomerId?.replace(/-/g, "");
|
|
714
|
+
if (!customerId) {
|
|
715
|
+
return { success: true };
|
|
716
|
+
}
|
|
717
|
+
const developerToken = params[parameters.developerToken.slug];
|
|
718
|
+
if (!developerToken) {
|
|
719
|
+
return {
|
|
720
|
+
success: false,
|
|
721
|
+
error: "Developer token is required"
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
const url = `https://googleads.googleapis.com/v18/customers/${customerId}/googleAds:searchStream`;
|
|
725
|
+
try {
|
|
726
|
+
const res = await proxyFetch(url, {
|
|
727
|
+
method: "POST",
|
|
728
|
+
headers: {
|
|
729
|
+
"Content-Type": "application/json",
|
|
730
|
+
"developer-token": developerToken,
|
|
731
|
+
"login-customer-id": customerId
|
|
732
|
+
},
|
|
733
|
+
body: JSON.stringify({
|
|
734
|
+
query: "SELECT customer.id FROM customer LIMIT 1"
|
|
735
|
+
})
|
|
736
|
+
});
|
|
737
|
+
if (!res.ok) {
|
|
738
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
739
|
+
return {
|
|
740
|
+
success: false,
|
|
741
|
+
error: `Google Ads API failed: HTTP ${res.status} ${errorText}`
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
return { success: true };
|
|
745
|
+
} catch (error) {
|
|
746
|
+
return {
|
|
747
|
+
success: false,
|
|
748
|
+
error: error instanceof Error ? error.message : String(error)
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
}
|
|
729
752
|
});
|
|
730
753
|
|
|
731
754
|
// src/connectors/create-connector-sdk.ts
|