@squadbase/vite-server 0.1.3-dev.9 → 0.1.4-dev.0
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 +14539 -29447
- package/dist/connectors/airtable-oauth.js +43 -6
- package/dist/connectors/airtable.js +43 -6
- package/dist/connectors/amplitude.js +43 -6
- package/dist/connectors/anthropic.js +43 -6
- package/dist/connectors/asana.js +43 -6
- package/dist/connectors/attio.js +522 -118
- package/dist/connectors/{google-ads-oauth.d.ts → backlog-api-key.d.ts} +1 -1
- package/dist/connectors/backlog-api-key.js +629 -0
- package/dist/connectors/customerio.js +43 -6
- package/dist/connectors/dbt.js +43 -6
- package/dist/connectors/{google-sheets-oauth.d.ts → gamma.d.ts} +1 -1
- package/dist/connectors/gamma.js +866 -0
- package/dist/connectors/gemini.js +43 -6
- package/dist/connectors/gmail-oauth.js +65 -8
- package/dist/connectors/gmail.js +104 -44
- package/dist/connectors/google-ads.d.ts +1 -1
- package/dist/connectors/google-ads.js +410 -332
- package/dist/connectors/google-analytics-oauth.js +61 -8
- package/dist/connectors/google-analytics.js +107 -292
- package/dist/connectors/google-calendar-oauth.js +61 -8
- package/dist/connectors/google-calendar.js +111 -58
- package/dist/connectors/{linkedin-ads-oauth.d.ts → google-docs.d.ts} +1 -1
- package/dist/connectors/google-docs.js +631 -0
- package/dist/connectors/google-drive.d.ts +5 -0
- package/dist/connectors/google-drive.js +875 -0
- package/dist/connectors/google-sheets.d.ts +1 -1
- package/dist/connectors/google-sheets.js +267 -285
- package/dist/connectors/google-slides.d.ts +5 -0
- package/dist/connectors/google-slides.js +663 -0
- package/dist/connectors/grafana.js +43 -6
- package/dist/connectors/hubspot-oauth.js +43 -6
- package/dist/connectors/hubspot.js +43 -6
- package/dist/connectors/intercom-oauth.js +43 -6
- package/dist/connectors/intercom.js +43 -6
- package/dist/connectors/jira-api-key.js +43 -6
- package/dist/connectors/kintone-api-token.js +256 -82
- package/dist/connectors/kintone.js +43 -6
- package/dist/connectors/linkedin-ads.js +188 -168
- package/dist/connectors/mailchimp-oauth.js +43 -6
- package/dist/connectors/mailchimp.js +43 -6
- package/dist/connectors/mixpanel.d.ts +5 -0
- package/dist/connectors/mixpanel.js +779 -0
- package/dist/connectors/notion-oauth.js +43 -6
- package/dist/connectors/notion.js +43 -6
- package/dist/connectors/openai.js +43 -6
- package/dist/connectors/sentry.d.ts +5 -0
- package/dist/connectors/sentry.js +761 -0
- package/dist/connectors/shopify-oauth.js +43 -6
- package/dist/connectors/shopify.js +43 -6
- package/dist/connectors/stripe-api-key.js +46 -7
- package/dist/connectors/stripe-oauth.js +43 -6
- package/dist/connectors/wix-store.js +43 -6
- package/dist/connectors/zendesk-oauth.js +43 -6
- package/dist/connectors/zendesk.js +43 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4574 -3863
- package/dist/main.js +4572 -3862
- package/dist/vite-plugin.js +4572 -3862
- package/package.json +30 -12
- package/dist/connectors/google-ads-oauth.js +0 -890
- package/dist/connectors/google-sheets-oauth.js +0 -718
- 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
|
|
|
@@ -329,21 +220,58 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
329
220
|
* Filters connections by connectorKey internally.
|
|
330
221
|
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
331
222
|
*/
|
|
332
|
-
createTools(connections, config) {
|
|
223
|
+
createTools(connections, config, opts) {
|
|
333
224
|
const myConnections = connections.filter(
|
|
334
225
|
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
335
226
|
);
|
|
336
227
|
const result = {};
|
|
337
228
|
for (const t of Object.values(this.tools)) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
229
|
+
const tool = t.createTool(myConnections, config);
|
|
230
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
231
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
232
|
+
...tool,
|
|
233
|
+
toModelOutput: async (options) => {
|
|
234
|
+
if (!originalToModelOutput) {
|
|
235
|
+
return opts.truncateOutput(options.output);
|
|
236
|
+
}
|
|
237
|
+
const modelOutput = await originalToModelOutput(options);
|
|
238
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
239
|
+
return opts.truncateOutput(modelOutput.value);
|
|
240
|
+
}
|
|
241
|
+
return modelOutput;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
342
244
|
}
|
|
343
245
|
return result;
|
|
344
246
|
}
|
|
345
247
|
static deriveKey(slug, authType) {
|
|
346
|
-
|
|
248
|
+
if (authType) return `${slug}-${authType}`;
|
|
249
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
250
|
+
// user-password
|
|
251
|
+
"postgresql": "user-password",
|
|
252
|
+
"mysql": "user-password",
|
|
253
|
+
"clickhouse": "user-password",
|
|
254
|
+
"kintone": "user-password",
|
|
255
|
+
"squadbase-db": "user-password",
|
|
256
|
+
// service-account
|
|
257
|
+
"snowflake": "service-account",
|
|
258
|
+
"bigquery": "service-account",
|
|
259
|
+
"google-analytics": "service-account",
|
|
260
|
+
"google-calendar": "service-account",
|
|
261
|
+
"aws-athena": "service-account",
|
|
262
|
+
"redshift": "service-account",
|
|
263
|
+
// api-key
|
|
264
|
+
"databricks": "api-key",
|
|
265
|
+
"dbt": "api-key",
|
|
266
|
+
"airtable": "api-key",
|
|
267
|
+
"openai": "api-key",
|
|
268
|
+
"gemini": "api-key",
|
|
269
|
+
"anthropic": "api-key",
|
|
270
|
+
"wix-store": "api-key"
|
|
271
|
+
};
|
|
272
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
273
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
274
|
+
return slug;
|
|
347
275
|
}
|
|
348
276
|
};
|
|
349
277
|
|
|
@@ -357,50 +285,68 @@ var AUTH_TYPES = {
|
|
|
357
285
|
USER_PASSWORD: "user-password"
|
|
358
286
|
};
|
|
359
287
|
|
|
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
|
|
288
|
+
// ../connectors/src/connectors/google-ads/tools/list-customers.ts
|
|
371
289
|
import { z } from "zod";
|
|
372
290
|
var BASE_URL2 = "https://googleads.googleapis.com/v18/";
|
|
373
291
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
292
|
+
var cachedToken = null;
|
|
293
|
+
async function getProxyToken(config) {
|
|
294
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
295
|
+
return cachedToken.token;
|
|
296
|
+
}
|
|
297
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
298
|
+
const res = await fetch(url, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
headers: {
|
|
301
|
+
"Content-Type": "application/json",
|
|
302
|
+
"x-api-key": config.appApiKey,
|
|
303
|
+
"project-id": config.projectId
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify({
|
|
306
|
+
sandboxId: config.sandboxId,
|
|
307
|
+
issuedBy: "coding-agent"
|
|
308
|
+
})
|
|
309
|
+
});
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
312
|
+
throw new Error(
|
|
313
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const data = await res.json();
|
|
317
|
+
cachedToken = {
|
|
318
|
+
token: data.token,
|
|
319
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
320
|
+
};
|
|
321
|
+
return data.token;
|
|
322
|
+
}
|
|
374
323
|
var inputSchema = z.object({
|
|
375
324
|
toolUseIntent: z.string().optional().describe(
|
|
376
325
|
"Brief description of what you intend to accomplish with this tool call"
|
|
377
326
|
),
|
|
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)")
|
|
327
|
+
connectionId: z.string().describe("ID of the Google Ads OAuth connection to use")
|
|
384
328
|
});
|
|
385
329
|
var outputSchema = z.discriminatedUnion("success", [
|
|
386
330
|
z.object({
|
|
387
331
|
success: z.literal(true),
|
|
388
|
-
|
|
389
|
-
|
|
332
|
+
customers: z.array(
|
|
333
|
+
z.object({
|
|
334
|
+
customerId: z.string(),
|
|
335
|
+
descriptiveName: z.string()
|
|
336
|
+
})
|
|
337
|
+
)
|
|
390
338
|
}),
|
|
391
339
|
z.object({
|
|
392
340
|
success: z.literal(false),
|
|
393
341
|
error: z.string()
|
|
394
342
|
})
|
|
395
343
|
]);
|
|
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).`,
|
|
344
|
+
var listCustomersTool = new ConnectorTool({
|
|
345
|
+
name: "listCustomers",
|
|
346
|
+
description: "List Google Ads customer accounts accessible with the current OAuth credentials.",
|
|
401
347
|
inputSchema,
|
|
402
348
|
outputSchema,
|
|
403
|
-
async execute({ connectionId
|
|
349
|
+
async execute({ connectionId }, connections, config) {
|
|
404
350
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
405
351
|
if (!connection2) {
|
|
406
352
|
return {
|
|
@@ -409,57 +355,82 @@ Authentication is handled automatically using a service account.
|
|
|
409
355
|
};
|
|
410
356
|
}
|
|
411
357
|
console.log(
|
|
412
|
-
`[connector-request] google-ads/${connection2.name}:
|
|
358
|
+
`[connector-request] google-ads/${connection2.name}: listCustomers`
|
|
413
359
|
);
|
|
414
360
|
try {
|
|
415
|
-
const { GoogleAuth } = await import("google-auth-library");
|
|
416
|
-
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
417
361
|
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}`;
|
|
362
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
363
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
438
364
|
const controller = new AbortController();
|
|
439
365
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
440
366
|
try {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
method,
|
|
367
|
+
const response = await fetch(proxyUrl, {
|
|
368
|
+
method: "POST",
|
|
444
369
|
headers: {
|
|
445
|
-
Authorization: `Bearer ${token}`,
|
|
446
370
|
"Content-Type": "application/json",
|
|
447
|
-
|
|
448
|
-
...lid ? { "login-customer-id": lid } : {}
|
|
371
|
+
Authorization: `Bearer ${token}`
|
|
449
372
|
},
|
|
450
|
-
body:
|
|
373
|
+
body: JSON.stringify({
|
|
374
|
+
url: `${BASE_URL2}customers:listAccessibleCustomers`,
|
|
375
|
+
method: "GET",
|
|
376
|
+
headers: {
|
|
377
|
+
"developer-token": developerToken
|
|
378
|
+
}
|
|
379
|
+
}),
|
|
451
380
|
signal: controller.signal
|
|
452
381
|
});
|
|
453
382
|
const data = await response.json();
|
|
454
383
|
if (!response.ok) {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
460
|
-
};
|
|
384
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
385
|
+
return { success: false, error: errorMessage };
|
|
461
386
|
}
|
|
462
|
-
|
|
387
|
+
const customerIds = (data.resourceNames ?? []).map(
|
|
388
|
+
(rn) => rn.replace(/^customers\//, "")
|
|
389
|
+
);
|
|
390
|
+
const customers = [];
|
|
391
|
+
for (const cid of customerIds) {
|
|
392
|
+
try {
|
|
393
|
+
const detailResponse = await fetch(proxyUrl, {
|
|
394
|
+
method: "POST",
|
|
395
|
+
headers: {
|
|
396
|
+
"Content-Type": "application/json",
|
|
397
|
+
Authorization: `Bearer ${token}`
|
|
398
|
+
},
|
|
399
|
+
body: JSON.stringify({
|
|
400
|
+
url: `${BASE_URL2}customers/${cid}/googleAds:searchStream`,
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: {
|
|
403
|
+
"Content-Type": "application/json",
|
|
404
|
+
"developer-token": developerToken,
|
|
405
|
+
"login-customer-id": cid
|
|
406
|
+
},
|
|
407
|
+
body: JSON.stringify({
|
|
408
|
+
query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
|
|
409
|
+
})
|
|
410
|
+
}),
|
|
411
|
+
signal: controller.signal
|
|
412
|
+
});
|
|
413
|
+
if (detailResponse.ok) {
|
|
414
|
+
const detailData = await detailResponse.json();
|
|
415
|
+
const customer = detailData?.[0]?.results?.[0]?.customer;
|
|
416
|
+
customers.push({
|
|
417
|
+
customerId: cid,
|
|
418
|
+
descriptiveName: customer?.descriptiveName ?? cid
|
|
419
|
+
});
|
|
420
|
+
} else {
|
|
421
|
+
customers.push({
|
|
422
|
+
customerId: cid,
|
|
423
|
+
descriptiveName: cid
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
} catch {
|
|
427
|
+
customers.push({
|
|
428
|
+
customerId: cid,
|
|
429
|
+
descriptiveName: cid
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return { success: true, customers };
|
|
463
434
|
} finally {
|
|
464
435
|
clearTimeout(timeout);
|
|
465
436
|
}
|
|
@@ -470,37 +441,122 @@ Authentication is handled automatically using a service account.
|
|
|
470
441
|
}
|
|
471
442
|
});
|
|
472
443
|
|
|
473
|
-
// ../connectors/src/connectors/google-ads/
|
|
444
|
+
// ../connectors/src/connectors/google-ads/setup.ts
|
|
445
|
+
var listCustomersToolName = `google-ads-oauth_${listCustomersTool.name}`;
|
|
446
|
+
var googleAdsOnboarding = new ConnectorOnboarding({
|
|
447
|
+
connectionSetupInstructions: {
|
|
448
|
+
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
|
|
449
|
+
|
|
450
|
+
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
|
|
451
|
+
2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
452
|
+
- \`parameterSlug\`: \`"developer-token"\`
|
|
453
|
+
- \`value\`: \u30E6\u30FC\u30B6\u30FC\u304C\u63D0\u4F9B\u3057\u305F Developer Token
|
|
454
|
+
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
|
|
455
|
+
4. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
456
|
+
- \`parameterSlug\`: \`"customer-id"\`
|
|
457
|
+
- \`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
|
|
458
|
+
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
|
|
459
|
+
6. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
|
|
460
|
+
- \`customer\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E\u8868\u793A\u540D
|
|
461
|
+
- \`customerId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FCID
|
|
462
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
463
|
+
|
|
464
|
+
#### \u5236\u7D04
|
|
465
|
+
- **\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
|
|
466
|
+
- \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`,
|
|
467
|
+
en: `Follow these steps to set up the Google Ads (OAuth) connection.
|
|
468
|
+
|
|
469
|
+
1. Ask the user to provide their Google Ads API Developer Token (available in Google Ads UI > Tools & Settings > API Center)
|
|
470
|
+
2. Call \`updateConnectionParameters\`:
|
|
471
|
+
- \`parameterSlug\`: \`"developer-token"\`
|
|
472
|
+
- \`value\`: The Developer Token provided by the user
|
|
473
|
+
3. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
|
|
474
|
+
4. Call \`updateConnectionParameters\`:
|
|
475
|
+
- \`parameterSlug\`: \`"customer-id"\`
|
|
476
|
+
- \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
|
|
477
|
+
5. The \`label\` of the user's selected customer will arrive as a message. Proceed to the next step
|
|
478
|
+
6. Call \`updateConnectionContext\`:
|
|
479
|
+
- \`customer\`: The selected customer's display name
|
|
480
|
+
- \`customerId\`: The selected customer ID
|
|
481
|
+
- \`note\`: Brief description of the setup
|
|
482
|
+
|
|
483
|
+
#### Constraints
|
|
484
|
+
- **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
|
|
485
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
|
|
486
|
+
},
|
|
487
|
+
dataOverviewInstructions: {
|
|
488
|
+
en: `1. Call google-ads-oauth_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
|
|
489
|
+
2. Explore ad group and keyword data as needed to understand the data structure`,
|
|
490
|
+
ja: `1. google-ads-oauth_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
|
|
491
|
+
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`
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// ../connectors/src/connectors/google-ads/tools/request.ts
|
|
474
496
|
import { z as z2 } from "zod";
|
|
475
497
|
var BASE_URL3 = "https://googleads.googleapis.com/v18/";
|
|
476
498
|
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
499
|
+
var cachedToken2 = null;
|
|
500
|
+
async function getProxyToken2(config) {
|
|
501
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
502
|
+
return cachedToken2.token;
|
|
503
|
+
}
|
|
504
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
505
|
+
const res = await fetch(url, {
|
|
506
|
+
method: "POST",
|
|
507
|
+
headers: {
|
|
508
|
+
"Content-Type": "application/json",
|
|
509
|
+
"x-api-key": config.appApiKey,
|
|
510
|
+
"project-id": config.projectId
|
|
511
|
+
},
|
|
512
|
+
body: JSON.stringify({
|
|
513
|
+
sandboxId: config.sandboxId,
|
|
514
|
+
issuedBy: "coding-agent"
|
|
515
|
+
})
|
|
516
|
+
});
|
|
517
|
+
if (!res.ok) {
|
|
518
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
const data = await res.json();
|
|
524
|
+
cachedToken2 = {
|
|
525
|
+
token: data.token,
|
|
526
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
527
|
+
};
|
|
528
|
+
return data.token;
|
|
529
|
+
}
|
|
477
530
|
var inputSchema2 = z2.object({
|
|
478
531
|
toolUseIntent: z2.string().optional().describe(
|
|
479
532
|
"Brief description of what you intend to accomplish with this tool call"
|
|
480
533
|
),
|
|
481
|
-
connectionId: z2.string().describe("ID of the Google Ads connection to use")
|
|
534
|
+
connectionId: z2.string().describe("ID of the Google Ads OAuth connection to use"),
|
|
535
|
+
method: z2.enum(["GET", "POST"]).describe("HTTP method"),
|
|
536
|
+
path: z2.string().describe(
|
|
537
|
+
"API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
|
|
538
|
+
),
|
|
539
|
+
body: z2.record(z2.string(), z2.unknown()).optional().describe("POST request body (JSON)")
|
|
482
540
|
});
|
|
483
541
|
var outputSchema2 = z2.discriminatedUnion("success", [
|
|
484
542
|
z2.object({
|
|
485
543
|
success: z2.literal(true),
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
customerId: z2.string(),
|
|
489
|
-
descriptiveName: z2.string()
|
|
490
|
-
})
|
|
491
|
-
)
|
|
544
|
+
status: z2.number(),
|
|
545
|
+
data: z2.unknown()
|
|
492
546
|
}),
|
|
493
547
|
z2.object({
|
|
494
548
|
success: z2.literal(false),
|
|
495
549
|
error: z2.string()
|
|
496
550
|
})
|
|
497
551
|
]);
|
|
498
|
-
var
|
|
499
|
-
name: "
|
|
500
|
-
description:
|
|
552
|
+
var requestTool = new ConnectorTool({
|
|
553
|
+
name: "request",
|
|
554
|
+
description: `Send authenticated requests to the Google Ads API v18.
|
|
555
|
+
Authentication is handled automatically via OAuth proxy.
|
|
556
|
+
{customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
|
|
501
557
|
inputSchema: inputSchema2,
|
|
502
558
|
outputSchema: outputSchema2,
|
|
503
|
-
async execute({ connectionId }, connections) {
|
|
559
|
+
async execute({ connectionId, method, path: path2, body }, connections, config) {
|
|
504
560
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
505
561
|
if (!connection2) {
|
|
506
562
|
return {
|
|
@@ -509,90 +565,44 @@ var listCustomersTool = new ConnectorTool({
|
|
|
509
565
|
};
|
|
510
566
|
}
|
|
511
567
|
console.log(
|
|
512
|
-
`[connector-request] google-ads/${connection2.name}:
|
|
568
|
+
`[connector-request] google-ads/${connection2.name}: ${method} ${path2}`
|
|
513
569
|
);
|
|
514
570
|
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
|
-
}
|
|
571
|
+
const rawCustomerId = parameters.customerId.tryGetValue(connection2);
|
|
572
|
+
const customerId = rawCustomerId?.replace(/-/g, "") ?? "";
|
|
573
|
+
const resolvedPath = customerId ? path2.replace(/\{customerId\}/g, customerId) : path2;
|
|
574
|
+
const url = `${BASE_URL3}${resolvedPath}`;
|
|
575
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
576
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
532
577
|
const controller = new AbortController();
|
|
533
578
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
534
579
|
try {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
580
|
+
const developerToken = parameters.developerToken.getValue(connection2);
|
|
581
|
+
const response = await fetch(proxyUrl, {
|
|
582
|
+
method: "POST",
|
|
583
|
+
headers: {
|
|
584
|
+
"Content-Type": "application/json",
|
|
585
|
+
Authorization: `Bearer ${token}`
|
|
586
|
+
},
|
|
587
|
+
body: JSON.stringify({
|
|
588
|
+
url,
|
|
589
|
+
method,
|
|
539
590
|
headers: {
|
|
540
|
-
|
|
541
|
-
"developer-token": developerToken
|
|
591
|
+
"Content-Type": "application/json",
|
|
592
|
+
"developer-token": developerToken,
|
|
593
|
+
...customerId ? { "login-customer-id": customerId } : {}
|
|
542
594
|
},
|
|
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
|
-
}
|
|
595
|
+
...method === "POST" && body ? { body: JSON.stringify(body) } : {}
|
|
596
|
+
}),
|
|
597
|
+
signal: controller.signal
|
|
598
|
+
});
|
|
599
|
+
const data = await response.json();
|
|
600
|
+
if (!response.ok) {
|
|
601
|
+
const dataObj = data;
|
|
602
|
+
const errorMessage = typeof dataObj?.error === "string" ? dataObj.error : typeof dataObj?.message === "string" ? dataObj.message : `HTTP ${response.status} ${response.statusText}`;
|
|
603
|
+
return { success: false, error: errorMessage };
|
|
594
604
|
}
|
|
595
|
-
return { success: true,
|
|
605
|
+
return { success: true, status: response.status, data };
|
|
596
606
|
} finally {
|
|
597
607
|
clearTimeout(timeout);
|
|
598
608
|
}
|
|
@@ -610,18 +620,28 @@ var tools = {
|
|
|
610
620
|
};
|
|
611
621
|
var googleAdsConnector = new ConnectorPlugin({
|
|
612
622
|
slug: "google-ads",
|
|
613
|
-
authType: AUTH_TYPES.
|
|
623
|
+
authType: AUTH_TYPES.OAUTH,
|
|
614
624
|
name: "Google Ads",
|
|
615
|
-
description: "Connect to Google Ads for advertising campaign data and reporting using
|
|
625
|
+
description: "Connect to Google Ads for advertising campaign data and reporting using OAuth.",
|
|
616
626
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1NGvmgvCxX7Tn11EST2N3N/a745fe7c63d360ed40a27ddaad3af168/google-ads.svg",
|
|
617
627
|
parameters,
|
|
618
628
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
619
629
|
onboarding: googleAdsOnboarding,
|
|
630
|
+
proxyPolicy: {
|
|
631
|
+
allowlist: [
|
|
632
|
+
{
|
|
633
|
+
host: "googleads.googleapis.com",
|
|
634
|
+
methods: ["GET", "POST"]
|
|
635
|
+
}
|
|
636
|
+
]
|
|
637
|
+
},
|
|
620
638
|
systemPrompt: {
|
|
621
|
-
en: `### Tools
|
|
639
|
+
en: `### Tools (setup-time only)
|
|
640
|
+
|
|
641
|
+
- \`google-ads-oauth_request\`: Send authenticated requests to the Google Ads API during setup / data overview. Use it for GAQL queries via searchStream. The {customerId} placeholder in paths is automatically replaced (hyphens removed). Authentication and developer token are configured automatically.
|
|
642
|
+
- \`google-ads-oauth_listCustomers\`: List accessible Google Ads customer accounts. Use this during setup to discover available accounts.
|
|
622
643
|
|
|
623
|
-
|
|
624
|
-
- \`google-ads_listCustomers\`: List accessible Google Ads customer accounts. Use this during setup to discover available accounts.
|
|
644
|
+
> **Important**: These tools are only available at setup time. Inside server-logic handlers, use the SDK (\`connection(id).search\`, etc.) \u2014 the SDK's fetch is already wired through the OAuth proxy. **Do NOT** hand-roll HTTP calls to \`_sqcore/connections/*/request\` from a handler.
|
|
625
645
|
|
|
626
646
|
### Google Ads API Reference
|
|
627
647
|
|
|
@@ -653,7 +673,14 @@ segments.date, segments.device, segments.ad_network_type
|
|
|
653
673
|
|
|
654
674
|
### Business Logic
|
|
655
675
|
|
|
656
|
-
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.
|
|
676
|
+
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 and do NOT read \`INTERNAL_SQUADBASE_*\` env vars \u2014 the SDK takes care of OAuth.
|
|
677
|
+
|
|
678
|
+
SDK surface (client created via \`connection(connectionId)\`):
|
|
679
|
+
- \`client.request(path, init?)\` \u2014 low-level authenticated fetch. \`{customerId}\` placeholders in the path are auto-replaced with the configured customer id.
|
|
680
|
+
- \`client.search(query, customerId?)\` \u2014 execute a GAQL query via searchStream and get rows back.
|
|
681
|
+
- \`client.listAccessibleCustomers()\` \u2014 return the list of customer IDs the OAuth user can access.
|
|
682
|
+
|
|
683
|
+
If a handler test fails with \`Connection proxy is not configured\`, retry \u2014 the sandbox is still initializing. Do NOT abandon the SDK and construct OAuth proxy URLs manually.
|
|
657
684
|
|
|
658
685
|
#### Example
|
|
659
686
|
|
|
@@ -671,10 +698,12 @@ rows.forEach(row => console.log(row));
|
|
|
671
698
|
// List accessible customer accounts
|
|
672
699
|
const customerIds = await ads.listAccessibleCustomers();
|
|
673
700
|
\`\`\``,
|
|
674
|
-
ja: `### \u30C4\u30FC\u30EB
|
|
701
|
+
ja: `### \u30C4\u30FC\u30EB\uFF08\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306E\u307F\uFF09
|
|
675
702
|
|
|
676
|
-
- \`google-
|
|
677
|
-
- \`google-
|
|
703
|
+
- \`google-ads-oauth_request\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3084\u30C7\u30FC\u30BF\u6982\u8981\u628A\u63E1\u6642\u306B Google Ads API \u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002searchStream \u3092\u4F7F\u3063\u305F GAQL \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
|
|
704
|
+
- \`google-ads-oauth_listCustomers\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A Google 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
|
|
705
|
+
|
|
706
|
+
> **\u91CD\u8981**: \u3053\u308C\u3089\u306E\u30C4\u30FC\u30EB\u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306E\u307F\u5229\u7528\u53EF\u80FD\u3067\u3059\u3002\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u306E\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u5FC5\u305A SDK\uFF08\`connection(id).search\` \u306A\u3069\uFF09\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002SDK \u306E fetch \u306F OAuth \u30D7\u30ED\u30AD\u30B7\u7D4C\u7531\u3067\u65E2\u306B\u914D\u7DDA\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u304B\u3089 \`_sqcore/connections/*/request\` \u3092\u624B\u66F8\u304D\u3067\u547C\u3073\u51FA\u3055\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
|
|
678
707
|
|
|
679
708
|
### Google Ads API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
680
709
|
|
|
@@ -706,7 +735,14 @@ segments.date, segments.device, segments.ad_network_type
|
|
|
706
735
|
|
|
707
736
|
### Business Logic
|
|
708
737
|
|
|
709
|
-
\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\
|
|
738
|
+
\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\u30BF SDK \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\`INTERNAL_SQUADBASE_*\` \u306E\u74B0\u5883\u5909\u6570\u3092\u4F7F\u3063\u3066\u624B\u52D5\u3067 OAuth \u30D7\u30ED\u30AD\u30B7\u3092\u53E9\u304F\u3053\u3068\u3082\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044 \u2014 SDK \u304C OAuth \u3092\u51E6\u7406\u3057\u307E\u3059\u3002
|
|
739
|
+
|
|
740
|
+
SDK\uFF08\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\uFF09:
|
|
741
|
+
- \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304D fetch\u3002\u30D1\u30B9\u5185\u306E \`{customerId}\` \u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u6E08\u307F\u30AB\u30B9\u30BF\u30DE\u30FC ID \u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
|
|
742
|
+
- \`client.search(query, customerId?)\` \u2014 GAQL \u30AF\u30A8\u30EA\u3092 searchStream \u3067\u5B9F\u884C\u3057\u3001\u884C\u3092\u53D6\u5F97\u3002
|
|
743
|
+
- \`client.listAccessibleCustomers()\` \u2014 OAuth \u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30B9\u30BF\u30DE\u30FC ID \u4E00\u89A7\u3092\u53D6\u5F97\u3002
|
|
744
|
+
|
|
745
|
+
\u30CF\u30F3\u30C9\u30E9\u306E\u30C6\u30B9\u30C8\u304C \`Connection proxy is not configured\` \u3067\u5931\u6557\u3059\u308B\u5834\u5408\u306F\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u901A\u5E38\u306F\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306E\u521D\u671F\u5316\u4E2D\u306B\u8D77\u304D\u307E\u3059\u3002SDK \u3092\u8AE6\u3081\u3066 OAuth \u30D7\u30ED\u30AD\u30B7\u306E URL \u3092\u81EA\u5206\u3067\u7D44\u307F\u7ACB\u3066\u308B\u3053\u3068\u306F **\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044**\u3002
|
|
710
746
|
|
|
711
747
|
#### Example
|
|
712
748
|
|
|
@@ -725,7 +761,49 @@ rows.forEach(row => console.log(row));
|
|
|
725
761
|
const customerIds = await ads.listAccessibleCustomers();
|
|
726
762
|
\`\`\``
|
|
727
763
|
},
|
|
728
|
-
tools
|
|
764
|
+
tools,
|
|
765
|
+
async checkConnection(params, config) {
|
|
766
|
+
const { proxyFetch } = config;
|
|
767
|
+
const rawCustomerId = params[parameters.customerId.slug];
|
|
768
|
+
const customerId = rawCustomerId?.replace(/-/g, "");
|
|
769
|
+
if (!customerId) {
|
|
770
|
+
return { success: true };
|
|
771
|
+
}
|
|
772
|
+
const developerToken = params[parameters.developerToken.slug];
|
|
773
|
+
if (!developerToken) {
|
|
774
|
+
return {
|
|
775
|
+
success: false,
|
|
776
|
+
error: "Developer token is required"
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const url = `https://googleads.googleapis.com/v18/customers/${customerId}/googleAds:searchStream`;
|
|
780
|
+
try {
|
|
781
|
+
const res = await proxyFetch(url, {
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: {
|
|
784
|
+
"Content-Type": "application/json",
|
|
785
|
+
"developer-token": developerToken,
|
|
786
|
+
"login-customer-id": customerId
|
|
787
|
+
},
|
|
788
|
+
body: JSON.stringify({
|
|
789
|
+
query: "SELECT customer.id FROM customer LIMIT 1"
|
|
790
|
+
})
|
|
791
|
+
});
|
|
792
|
+
if (!res.ok) {
|
|
793
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
794
|
+
return {
|
|
795
|
+
success: false,
|
|
796
|
+
error: `Google Ads API failed: HTTP ${res.status} ${errorText}`
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
return { success: true };
|
|
800
|
+
} catch (error) {
|
|
801
|
+
return {
|
|
802
|
+
success: false,
|
|
803
|
+
error: error instanceof Error ? error.message : String(error)
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
}
|
|
729
807
|
});
|
|
730
808
|
|
|
731
809
|
// src/connectors/create-connector-sdk.ts
|