@squadbase/vite-server 0.1.3-dev.13 → 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 +1890 -3173
- package/dist/connectors/google-ads.d.ts +1 -1
- package/dist/connectors/google-ads.js +343 -320
- package/dist/connectors/google-drive-oauth.js +1 -1
- package/dist/connectors/google-drive.js +1 -1
- package/dist/connectors/google-slides-oauth.js +1 -1
- package/dist/connectors/google-slides.js +47 -3
- 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.js +1 -1
- package/dist/index.js +1888 -3171
- package/dist/main.js +1888 -3171
- package/dist/vite-plugin.js +1888 -3171
- package/package.json +5 -9
- package/dist/connectors/google-ads-oauth.js +0 -890
- package/dist/connectors/linkedin-ads-oauth.d.ts +0 -5
- package/dist/connectors/linkedin-ads-oauth.js +0 -848
|
@@ -462,7 +462,7 @@ var googleDriveOauthConnector = new ConnectorPlugin({
|
|
|
462
462
|
authType: AUTH_TYPES.OAUTH,
|
|
463
463
|
name: "Google Drive",
|
|
464
464
|
description: "Connect to Google Drive for file management, sharing, and collaboration using OAuth.",
|
|
465
|
-
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/
|
|
465
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7rOUJhO4dDKRYirBqCYzxI/48e9a5b85f0b4c6f9a3d7e2c1b8a5f4d/google-drive.svg",
|
|
466
466
|
parameters,
|
|
467
467
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
468
468
|
onboarding: googleDriveOnboarding,
|
|
@@ -510,7 +510,7 @@ var googleDriveConnector = new ConnectorPlugin({
|
|
|
510
510
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
|
511
511
|
name: "Google Drive",
|
|
512
512
|
description: "Connect to Google Drive for file management, sharing, and collaboration using a service account.",
|
|
513
|
-
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/
|
|
513
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7rOUJhO4dDKRYirBqCYzxI/48e9a5b85f0b4c6f9a3d7e2c1b8a5f4d/google-drive.svg",
|
|
514
514
|
parameters,
|
|
515
515
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
516
516
|
onboarding: googleDriveOnboarding,
|
|
@@ -429,7 +429,7 @@ var googleSlidesOauthConnector = new ConnectorPlugin({
|
|
|
429
429
|
authType: AUTH_TYPES.OAUTH,
|
|
430
430
|
name: "Google Slides",
|
|
431
431
|
description: "Connect to Google Slides for presentation data access and creation using OAuth.",
|
|
432
|
-
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/
|
|
432
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5g0LJMwN28s2OhJbYU9CxC/71f1abfb40c89e5a6b5e3a3ad83b5a73/google-slides.svg",
|
|
433
433
|
parameters,
|
|
434
434
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
435
435
|
onboarding: googleSlidesOnboarding,
|
|
@@ -64,6 +64,15 @@ var parameters = {
|
|
|
64
64
|
type: "text",
|
|
65
65
|
secret: false,
|
|
66
66
|
required: false
|
|
67
|
+
}),
|
|
68
|
+
sharedDriveId: new ParameterDefinition({
|
|
69
|
+
slug: "shared-drive-id",
|
|
70
|
+
name: "Shared Drive ID",
|
|
71
|
+
description: "The ID of the Google Shared Drive where new presentations will be created. Required for service accounts that do not have their own Drive storage. You can find the ID in the Shared Drive URL: https://drive.google.com/drive/folders/{sharedDriveId}",
|
|
72
|
+
envVarBaseKey: "GOOGLE_SLIDES_SHARED_DRIVE_ID",
|
|
73
|
+
type: "text",
|
|
74
|
+
secret: false,
|
|
75
|
+
required: false
|
|
67
76
|
})
|
|
68
77
|
};
|
|
69
78
|
|
|
@@ -81,7 +90,8 @@ function extractPresentationId(urlOrId) {
|
|
|
81
90
|
// ../connectors/src/connectors/google-slides/sdk/index.ts
|
|
82
91
|
var TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
83
92
|
var SLIDES_BASE_URL = "https://slides.googleapis.com/v1/presentations";
|
|
84
|
-
var
|
|
93
|
+
var DRIVE_FILES_URL = "https://www.googleapis.com/drive/v3/files";
|
|
94
|
+
var SCOPE = "https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/drive";
|
|
85
95
|
function base64url(input) {
|
|
86
96
|
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
87
97
|
return buf.toString("base64url");
|
|
@@ -108,6 +118,7 @@ function createClient(params) {
|
|
|
108
118
|
const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
109
119
|
const presentationUrl = params[parameters.presentationUrl.slug];
|
|
110
120
|
const defaultPresentationId = presentationUrl ? extractPresentationId(presentationUrl) : void 0;
|
|
121
|
+
const sharedDriveId = params[parameters.sharedDriveId.slug] || void 0;
|
|
111
122
|
if (!serviceAccountKeyJsonBase64) {
|
|
112
123
|
throw new Error(
|
|
113
124
|
`google-slides: missing required parameter: ${parameters.serviceAccountKeyJsonBase64.slug}`
|
|
@@ -216,6 +227,36 @@ function createClient(params) {
|
|
|
216
227
|
return await response.json();
|
|
217
228
|
},
|
|
218
229
|
async createPresentation(title) {
|
|
230
|
+
if (sharedDriveId) {
|
|
231
|
+
const driveResponse = await authenticatedFetch(
|
|
232
|
+
`${DRIVE_FILES_URL}?supportsAllDrives=true`,
|
|
233
|
+
{
|
|
234
|
+
method: "POST",
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
name: title,
|
|
237
|
+
mimeType: "application/vnd.google-apps.presentation",
|
|
238
|
+
parents: [sharedDriveId]
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
if (!driveResponse.ok) {
|
|
243
|
+
const body = await driveResponse.text();
|
|
244
|
+
throw new Error(
|
|
245
|
+
`google-slides: createPresentation (Drive API) failed (${driveResponse.status}): ${body}`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
const driveFile = await driveResponse.json();
|
|
249
|
+
const slidesResponse = await authenticatedFetch(
|
|
250
|
+
`${SLIDES_BASE_URL}/${driveFile.id}`
|
|
251
|
+
);
|
|
252
|
+
if (!slidesResponse.ok) {
|
|
253
|
+
const body = await slidesResponse.text();
|
|
254
|
+
throw new Error(
|
|
255
|
+
`google-slides: getPresentation after create failed (${slidesResponse.status}): ${body}`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return await slidesResponse.json();
|
|
259
|
+
}
|
|
219
260
|
const response = await authenticatedFetch(SLIDES_BASE_URL, {
|
|
220
261
|
method: "POST",
|
|
221
262
|
body: JSON.stringify({ title })
|
|
@@ -405,7 +446,10 @@ Authentication is handled automatically using a service account.
|
|
|
405
446
|
);
|
|
406
447
|
const auth = new GoogleAuth({
|
|
407
448
|
credentials,
|
|
408
|
-
scopes: [
|
|
449
|
+
scopes: [
|
|
450
|
+
"https://www.googleapis.com/auth/presentations",
|
|
451
|
+
"https://www.googleapis.com/auth/drive"
|
|
452
|
+
]
|
|
409
453
|
});
|
|
410
454
|
const token = await auth.getAccessToken();
|
|
411
455
|
if (!token) {
|
|
@@ -458,7 +502,7 @@ var googleSlidesConnector = new ConnectorPlugin({
|
|
|
458
502
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
|
459
503
|
name: "Google Slides",
|
|
460
504
|
description: "Connect to Google Slides for presentation data access and creation using a service account.",
|
|
461
|
-
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/
|
|
505
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5g0LJMwN28s2OhJbYU9CxC/71f1abfb40c89e5a6b5e3a3ad83b5a73/google-slides.svg",
|
|
462
506
|
parameters,
|
|
463
507
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
464
508
|
onboarding: googleSlidesOnboarding,
|
|
@@ -42,98 +42,14 @@ var ParameterDefinition = class {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// ../connectors/src/connectors/linkedin-ads/parameters.ts
|
|
46
|
-
var parameters = {
|
|
47
|
-
accessToken: new ParameterDefinition({
|
|
48
|
-
slug: "access-token",
|
|
49
|
-
name: "Access Token",
|
|
50
|
-
description: "A LinkedIn Marketing API access token. Obtainable from the LinkedIn Developer Portal token generator or via OAuth 2.0 authorization code flow.",
|
|
51
|
-
envVarBaseKey: "LINKEDIN_ADS_ACCESS_TOKEN",
|
|
52
|
-
type: "text",
|
|
53
|
-
secret: true,
|
|
54
|
-
required: true
|
|
55
|
-
}),
|
|
56
|
-
adAccountId: new ParameterDefinition({
|
|
57
|
-
slug: "ad-account-id",
|
|
58
|
-
name: "Ad Account ID",
|
|
59
|
-
description: "The LinkedIn ad account ID (numeric). Found in Campaign Manager under Account Assets. The URN format urn:li:sponsoredAccount:{id} is constructed automatically.",
|
|
60
|
-
envVarBaseKey: "LINKEDIN_ADS_AD_ACCOUNT_ID",
|
|
61
|
-
type: "text",
|
|
62
|
-
secret: false,
|
|
63
|
-
required: false
|
|
64
|
-
})
|
|
65
|
-
};
|
|
66
|
-
|
|
67
45
|
// ../connectors/src/connectors/linkedin-ads/sdk/index.ts
|
|
68
46
|
var BASE_URL = "https://api.linkedin.com/rest/";
|
|
69
|
-
|
|
70
|
-
function createClient(params, fetchFn = fetch) {
|
|
71
|
-
const accessToken = params[parameters.accessToken.slug];
|
|
72
|
-
const defaultAdAccountId = params[parameters.adAccountId.slug] ?? "";
|
|
73
|
-
if (!accessToken) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`linkedin-ads: missing required parameter: ${parameters.accessToken.slug}`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
function getHeaders(extra) {
|
|
79
|
-
return {
|
|
80
|
-
Authorization: `Bearer ${accessToken}`,
|
|
81
|
-
"LinkedIn-Version": LINKEDIN_VERSION,
|
|
82
|
-
"X-Restli-Protocol-Version": "2.0.0",
|
|
83
|
-
"Content-Type": "application/json",
|
|
84
|
-
...extra
|
|
85
|
-
};
|
|
86
|
-
}
|
|
47
|
+
function createClient(_params, fetchFn = fetch) {
|
|
87
48
|
function request(path2, init) {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
const headers = new Headers(init?.headers);
|
|
91
|
-
for (const [k, v] of Object.entries(getHeaders())) {
|
|
92
|
-
if (!headers.has(k)) {
|
|
93
|
-
headers.set(k, v);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return fetchFn(url, { ...init, headers });
|
|
49
|
+
const url = `${BASE_URL}${path2}`;
|
|
50
|
+
return fetchFn(url, init);
|
|
97
51
|
}
|
|
98
|
-
|
|
99
|
-
const searchParams = new URLSearchParams({ q: "analytics", ...queryParams });
|
|
100
|
-
const url = `${BASE_URL}adAnalytics?${searchParams.toString()}`;
|
|
101
|
-
const response = await fetchFn(url, {
|
|
102
|
-
method: "GET",
|
|
103
|
-
headers: getHeaders()
|
|
104
|
-
});
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
const body = await response.text();
|
|
107
|
-
throw new Error(
|
|
108
|
-
`linkedin-ads: getAnalytics failed (${response.status}): ${body}`
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
const data = await response.json();
|
|
112
|
-
return data.elements ?? [];
|
|
113
|
-
}
|
|
114
|
-
async function listAdAccounts() {
|
|
115
|
-
const url = `${BASE_URL}adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100`;
|
|
116
|
-
const response = await fetchFn(url, {
|
|
117
|
-
method: "GET",
|
|
118
|
-
headers: getHeaders()
|
|
119
|
-
});
|
|
120
|
-
if (!response.ok) {
|
|
121
|
-
const body = await response.text();
|
|
122
|
-
throw new Error(
|
|
123
|
-
`linkedin-ads: listAdAccounts failed (${response.status}): ${body}`
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
const data = await response.json();
|
|
127
|
-
return (data.elements ?? []).map((a) => ({
|
|
128
|
-
adAccountId: String(a.id ?? ""),
|
|
129
|
-
name: a.name ?? String(a.id ?? "")
|
|
130
|
-
}));
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
request,
|
|
134
|
-
getAnalytics,
|
|
135
|
-
listAdAccounts
|
|
136
|
-
};
|
|
52
|
+
return { request };
|
|
137
53
|
}
|
|
138
54
|
|
|
139
55
|
// ../connectors/src/connector-onboarding.ts
|
|
@@ -248,13 +164,45 @@ var AUTH_TYPES = {
|
|
|
248
164
|
|
|
249
165
|
// ../connectors/src/connectors/linkedin-ads/tools/list-ad-accounts.ts
|
|
250
166
|
import { z } from "zod";
|
|
167
|
+
var BASE_URL2 = "https://api.linkedin.com/rest/";
|
|
168
|
+
var LINKEDIN_VERSION = "202603";
|
|
251
169
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
252
|
-
var
|
|
170
|
+
var cachedToken = null;
|
|
171
|
+
async function getProxyToken(config) {
|
|
172
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
173
|
+
return cachedToken.token;
|
|
174
|
+
}
|
|
175
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
176
|
+
const res = await fetch(url, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: {
|
|
179
|
+
"Content-Type": "application/json",
|
|
180
|
+
"x-api-key": config.appApiKey,
|
|
181
|
+
"project-id": config.projectId
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
sandboxId: config.sandboxId,
|
|
185
|
+
issuedBy: "coding-agent"
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const data = await res.json();
|
|
195
|
+
cachedToken = {
|
|
196
|
+
token: data.token,
|
|
197
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
198
|
+
};
|
|
199
|
+
return data.token;
|
|
200
|
+
}
|
|
253
201
|
var inputSchema = z.object({
|
|
254
202
|
toolUseIntent: z.string().optional().describe(
|
|
255
203
|
"Brief description of what you intend to accomplish with this tool call"
|
|
256
204
|
),
|
|
257
|
-
connectionId: z.string().describe("ID of the LinkedIn Ads connection to use")
|
|
205
|
+
connectionId: z.string().describe("ID of the LinkedIn Ads OAuth connection to use")
|
|
258
206
|
});
|
|
259
207
|
var outputSchema = z.discriminatedUnion("success", [
|
|
260
208
|
z.object({
|
|
@@ -273,10 +221,10 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
273
221
|
]);
|
|
274
222
|
var listAdAccountsTool = new ConnectorTool({
|
|
275
223
|
name: "listAdAccounts",
|
|
276
|
-
description: "List LinkedIn ad accounts accessible with the current
|
|
224
|
+
description: "List LinkedIn ad accounts accessible with the current OAuth credentials.",
|
|
277
225
|
inputSchema,
|
|
278
226
|
outputSchema,
|
|
279
|
-
async execute({ connectionId }, connections) {
|
|
227
|
+
async execute({ connectionId }, connections, config) {
|
|
280
228
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
281
229
|
if (!connection2) {
|
|
282
230
|
return {
|
|
@@ -288,18 +236,25 @@ var listAdAccountsTool = new ConnectorTool({
|
|
|
288
236
|
`[connector-request] linkedin-ads/${connection2.name}: listAdAccounts`
|
|
289
237
|
);
|
|
290
238
|
try {
|
|
291
|
-
const
|
|
292
|
-
const
|
|
239
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
240
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
293
241
|
const controller = new AbortController();
|
|
294
242
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
295
243
|
try {
|
|
296
|
-
const response = await fetch(
|
|
297
|
-
method: "
|
|
244
|
+
const response = await fetch(proxyUrl, {
|
|
245
|
+
method: "POST",
|
|
298
246
|
headers: {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"X-Restli-Protocol-Version": "2.0.0"
|
|
247
|
+
"Content-Type": "application/json",
|
|
248
|
+
Authorization: `Bearer ${token}`
|
|
302
249
|
},
|
|
250
|
+
body: JSON.stringify({
|
|
251
|
+
url: `${BASE_URL2}adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100`,
|
|
252
|
+
method: "GET",
|
|
253
|
+
headers: {
|
|
254
|
+
"LinkedIn-Version": LINKEDIN_VERSION,
|
|
255
|
+
"X-Restli-Protocol-Version": "2.0.0"
|
|
256
|
+
}
|
|
257
|
+
}),
|
|
303
258
|
signal: controller.signal
|
|
304
259
|
});
|
|
305
260
|
const data = await response.json();
|
|
@@ -326,10 +281,10 @@ var listAdAccountsTool = new ConnectorTool({
|
|
|
326
281
|
var listAdAccountsToolName = `linkedin-ads_${listAdAccountsTool.name}`;
|
|
327
282
|
var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
328
283
|
connectionSetupInstructions: {
|
|
329
|
-
ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067LinkedIn\u5E83\u544A\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
284
|
+
ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067LinkedIn\u5E83\u544A (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
330
285
|
|
|
331
|
-
1. \`${listAdAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\
|
|
332
|
-
2. \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F\u30E6\u30FC\u30B6\u30FC\
|
|
286
|
+
1. \`${listAdAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
287
|
+
2. \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F\u30E6\u30FC\u30B6\u30FC\u306BOAuth\u63A5\u7D9A\u306E\u78BA\u8A8D\u3092\u4F9D\u983C\u3059\u308B
|
|
333
288
|
3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
334
289
|
- \`parameterSlug\`: \`"ad-account-id"\`
|
|
335
290
|
- \`options\`: \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30A2\u30AB\u30A6\u30F3\u30C8ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30A2\u30AB\u30A6\u30F3\u30C8ID
|
|
@@ -342,10 +297,10 @@ var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
|
342
297
|
#### \u5236\u7D04
|
|
343
298
|
- **\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
|
|
344
299
|
- \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`,
|
|
345
|
-
en: `Follow these steps to set up the LinkedIn Ads connection.
|
|
300
|
+
en: `Follow these steps to set up the LinkedIn Ads (OAuth) connection.
|
|
346
301
|
|
|
347
|
-
1. Call \`${listAdAccountsToolName}\` to get the list of ad accounts accessible with the
|
|
348
|
-
2. If an error occurs, ask the user to verify their
|
|
302
|
+
1. Call \`${listAdAccountsToolName}\` to get the list of ad accounts accessible with the OAuth credentials
|
|
303
|
+
2. If an error occurs, ask the user to verify their OAuth connection
|
|
349
304
|
3. Call \`updateConnectionParameters\`:
|
|
350
305
|
- \`parameterSlug\`: \`"ad-account-id"\`
|
|
351
306
|
- \`options\`: The ad account list. Each option's \`label\` should be \`Account Name (id: accountId)\`, \`value\` should be the account ID
|
|
@@ -369,19 +324,63 @@ var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
|
369
324
|
}
|
|
370
325
|
});
|
|
371
326
|
|
|
327
|
+
// ../connectors/src/connectors/linkedin-ads/parameters.ts
|
|
328
|
+
var parameters = {
|
|
329
|
+
adAccountId: new ParameterDefinition({
|
|
330
|
+
slug: "ad-account-id",
|
|
331
|
+
name: "Ad Account ID",
|
|
332
|
+
description: "The LinkedIn ad account ID (numeric). Found in Campaign Manager under Account Assets. The URN format urn:li:sponsoredAccount:{id} is constructed automatically.",
|
|
333
|
+
envVarBaseKey: "LINKEDIN_ADS_AD_ACCOUNT_ID",
|
|
334
|
+
type: "text",
|
|
335
|
+
secret: false,
|
|
336
|
+
required: false
|
|
337
|
+
})
|
|
338
|
+
};
|
|
339
|
+
|
|
372
340
|
// ../connectors/src/connectors/linkedin-ads/tools/request.ts
|
|
373
341
|
import { z as z2 } from "zod";
|
|
374
|
-
var
|
|
375
|
-
var
|
|
342
|
+
var BASE_URL3 = "https://api.linkedin.com/rest/";
|
|
343
|
+
var LINKEDIN_VERSION2 = "202603";
|
|
376
344
|
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
345
|
+
var cachedToken2 = null;
|
|
346
|
+
async function getProxyToken2(config) {
|
|
347
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
348
|
+
return cachedToken2.token;
|
|
349
|
+
}
|
|
350
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
351
|
+
const res = await fetch(url, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
headers: {
|
|
354
|
+
"Content-Type": "application/json",
|
|
355
|
+
"x-api-key": config.appApiKey,
|
|
356
|
+
"project-id": config.projectId
|
|
357
|
+
},
|
|
358
|
+
body: JSON.stringify({
|
|
359
|
+
sandboxId: config.sandboxId,
|
|
360
|
+
issuedBy: "coding-agent"
|
|
361
|
+
})
|
|
362
|
+
});
|
|
363
|
+
if (!res.ok) {
|
|
364
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
const data = await res.json();
|
|
370
|
+
cachedToken2 = {
|
|
371
|
+
token: data.token,
|
|
372
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
373
|
+
};
|
|
374
|
+
return data.token;
|
|
375
|
+
}
|
|
377
376
|
var inputSchema2 = z2.object({
|
|
378
377
|
toolUseIntent: z2.string().optional().describe(
|
|
379
378
|
"Brief description of what you intend to accomplish with this tool call"
|
|
380
379
|
),
|
|
381
|
-
connectionId: z2.string().describe("ID of the LinkedIn Ads connection to use"),
|
|
380
|
+
connectionId: z2.string().describe("ID of the LinkedIn Ads OAuth connection to use"),
|
|
382
381
|
method: z2.enum(["GET", "POST", "DELETE"]).describe("HTTP method"),
|
|
383
382
|
path: z2.string().describe(
|
|
384
|
-
"API path appended to https://api.linkedin.com/rest/ (e.g., 'adAccounts/{adAccountId}/adCampaigns'). {adAccountId} is automatically replaced with the
|
|
383
|
+
"API path appended to https://api.linkedin.com/rest/ (e.g., 'adAccounts/{adAccountId}/adCampaigns'). {adAccountId} is automatically replaced with the connection's ad account ID."
|
|
385
384
|
),
|
|
386
385
|
queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
|
|
387
386
|
body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON). For partial updates, use the patch format: { patch: { $set: { ... } } }")
|
|
@@ -400,12 +399,12 @@ var outputSchema2 = z2.discriminatedUnion("success", [
|
|
|
400
399
|
var requestTool = new ConnectorTool({
|
|
401
400
|
name: "request",
|
|
402
401
|
description: `Send authenticated requests to the LinkedIn Marketing API (REST).
|
|
403
|
-
Authentication is handled via
|
|
402
|
+
Authentication is handled automatically via OAuth proxy.
|
|
404
403
|
{adAccountId} in the path is automatically replaced with the connection's ad account ID.
|
|
405
|
-
Required headers (
|
|
404
|
+
Required headers (LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.`,
|
|
406
405
|
inputSchema: inputSchema2,
|
|
407
406
|
outputSchema: outputSchema2,
|
|
408
|
-
async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
|
|
407
|
+
async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
|
|
409
408
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
410
409
|
if (!connection2) {
|
|
411
410
|
return {
|
|
@@ -417,31 +416,38 @@ Required headers (Authorization, LinkedIn-Version, X-Restli-Protocol-Version) ar
|
|
|
417
416
|
`[connector-request] linkedin-ads/${connection2.name}: ${method} ${path2}`
|
|
418
417
|
);
|
|
419
418
|
try {
|
|
420
|
-
const accessToken = parameters.accessToken.getValue(connection2);
|
|
421
419
|
const adAccountId = parameters.adAccountId.tryGetValue(connection2) ?? "";
|
|
422
420
|
const resolvedPath = adAccountId ? path2.replace(/\{adAccountId\}/g, adAccountId) : path2;
|
|
423
|
-
let url = `${
|
|
421
|
+
let url = `${BASE_URL3}${resolvedPath}`;
|
|
424
422
|
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
425
423
|
const params = new URLSearchParams(queryParams);
|
|
426
424
|
const separator = url.includes("?") ? "&" : "?";
|
|
427
425
|
url = `${url}${separator}${params.toString()}`;
|
|
428
426
|
}
|
|
427
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
428
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
429
429
|
const controller = new AbortController();
|
|
430
430
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
431
431
|
try {
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
"
|
|
435
|
-
"X-Restli-Protocol-Version": "2.0.0",
|
|
436
|
-
"Content-Type": "application/json"
|
|
432
|
+
const additionalHeaders = {
|
|
433
|
+
"LinkedIn-Version": LINKEDIN_VERSION2,
|
|
434
|
+
"X-Restli-Protocol-Version": "2.0.0"
|
|
437
435
|
};
|
|
438
436
|
if (body && "patch" in body) {
|
|
439
|
-
|
|
437
|
+
additionalHeaders["X-RestLi-Method"] = "PARTIAL_UPDATE";
|
|
440
438
|
}
|
|
441
|
-
const response = await fetch(
|
|
442
|
-
method,
|
|
443
|
-
headers
|
|
444
|
-
|
|
439
|
+
const response = await fetch(proxyUrl, {
|
|
440
|
+
method: "POST",
|
|
441
|
+
headers: {
|
|
442
|
+
"Content-Type": "application/json",
|
|
443
|
+
Authorization: `Bearer ${token}`
|
|
444
|
+
},
|
|
445
|
+
body: JSON.stringify({
|
|
446
|
+
url,
|
|
447
|
+
method,
|
|
448
|
+
headers: additionalHeaders,
|
|
449
|
+
...(method === "POST" || method === "DELETE") && body ? { body: JSON.stringify(body) } : {}
|
|
450
|
+
}),
|
|
445
451
|
signal: controller.signal
|
|
446
452
|
});
|
|
447
453
|
if (response.status === 204) {
|
|
@@ -471,17 +477,25 @@ var tools = {
|
|
|
471
477
|
};
|
|
472
478
|
var linkedinAdsConnector = new ConnectorPlugin({
|
|
473
479
|
slug: "linkedin-ads",
|
|
474
|
-
authType: AUTH_TYPES.
|
|
480
|
+
authType: AUTH_TYPES.OAUTH,
|
|
475
481
|
name: "LinkedIn Ads",
|
|
476
|
-
description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using
|
|
482
|
+
description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using OAuth.",
|
|
477
483
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3x7xd9pVJkRFY7ADIg4ycq/b94720e34cb081e9ae45dfde799a59cd/LinkedIn_icon.svg.png",
|
|
478
484
|
parameters,
|
|
479
485
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
480
486
|
onboarding: linkedinAdsOnboarding,
|
|
487
|
+
proxyPolicy: {
|
|
488
|
+
allowlist: [
|
|
489
|
+
{
|
|
490
|
+
host: "api.linkedin.com",
|
|
491
|
+
methods: ["GET", "POST", "DELETE"]
|
|
492
|
+
}
|
|
493
|
+
]
|
|
494
|
+
},
|
|
481
495
|
systemPrompt: {
|
|
482
496
|
en: `### Tools
|
|
483
497
|
|
|
484
|
-
- \`linkedin-ads_request\`: Send authenticated requests to the LinkedIn Marketing API (REST). The {adAccountId} placeholder in paths is automatically replaced. Authentication is
|
|
498
|
+
- \`linkedin-ads_request\`: Send authenticated requests to the LinkedIn Marketing API (REST). The {adAccountId} placeholder in paths is automatically replaced. Authentication is handled automatically via OAuth proxy. Required headers (LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.
|
|
485
499
|
- \`linkedin-ads_listAdAccounts\`: List accessible LinkedIn ad accounts. Use this during setup to discover available accounts.
|
|
486
500
|
|
|
487
501
|
### LinkedIn Marketing API Reference
|
|
@@ -568,25 +582,13 @@ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
|
|
|
568
582
|
|
|
569
583
|
const linkedin = connection("<connectionId>");
|
|
570
584
|
|
|
571
|
-
// Get analytics for an ad account
|
|
572
|
-
const analytics = await linkedin.getAnalytics({
|
|
573
|
-
pivot: "CAMPAIGN",
|
|
574
|
-
timeGranularity: "MONTHLY",
|
|
575
|
-
"dateRange": "(start:(year:2025,month:1,day:1))",
|
|
576
|
-
accounts: "List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})",
|
|
577
|
-
fields: "impressions,clicks,costInLocalCurrency,dateRange,pivotValues",
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
// List ad accounts
|
|
581
|
-
const accounts = await linkedin.listAdAccounts();
|
|
582
|
-
|
|
583
585
|
// Get campaigns
|
|
584
586
|
const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
|
|
585
587
|
const data = await res.json();
|
|
586
588
|
\`\`\``,
|
|
587
589
|
ja: `### \u30C4\u30FC\u30EB
|
|
588
590
|
|
|
589
|
-
- \`linkedin-ads_request\`: LinkedIn Marketing API\uFF08REST\uFF09\u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{adAccountId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u8A8D\u8A3C\
|
|
591
|
+
- \`linkedin-ads_request\`: LinkedIn Marketing API\uFF08REST\uFF09\u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{adAccountId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u8A8D\u8A3C\u306FOAuth\u30D7\u30ED\u30AD\u30B7\u7D4C\u7531\u3067\u81EA\u52D5\u7684\u306B\u51E6\u7406\u3055\u308C\u307E\u3059\u3002\u5FC5\u9808\u30D8\u30C3\u30C0\u30FC\uFF08LinkedIn-Version\u3001X-Restli-Protocol-Version\uFF09\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
|
|
590
592
|
- \`linkedin-ads_listAdAccounts\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306ALinkedIn\u5E83\u544A\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
|
|
591
593
|
|
|
592
594
|
### LinkedIn Marketing API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
@@ -673,38 +675,19 @@ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
|
|
|
673
675
|
|
|
674
676
|
const linkedin = connection("<connectionId>");
|
|
675
677
|
|
|
676
|
-
// \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u3092\u53D6\u5F97
|
|
677
|
-
const analytics = await linkedin.getAnalytics({
|
|
678
|
-
pivot: "CAMPAIGN",
|
|
679
|
-
timeGranularity: "MONTHLY",
|
|
680
|
-
"dateRange": "(start:(year:2025,month:1,day:1))",
|
|
681
|
-
accounts: "List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})",
|
|
682
|
-
fields: "impressions,clicks,costInLocalCurrency,dateRange,pivotValues",
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
// \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
|
|
686
|
-
const accounts = await linkedin.listAdAccounts();
|
|
687
|
-
|
|
688
678
|
// \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u3092\u53D6\u5F97
|
|
689
679
|
const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
|
|
690
680
|
const data = await res.json();
|
|
691
681
|
\`\`\``
|
|
692
682
|
},
|
|
693
683
|
tools,
|
|
694
|
-
async checkConnection(
|
|
695
|
-
const
|
|
696
|
-
if (!accessToken) {
|
|
697
|
-
return {
|
|
698
|
-
success: false,
|
|
699
|
-
error: "Access token is required"
|
|
700
|
-
};
|
|
701
|
-
}
|
|
684
|
+
async checkConnection(_params, config) {
|
|
685
|
+
const { proxyFetch } = config;
|
|
702
686
|
try {
|
|
703
687
|
const url = "https://api.linkedin.com/rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=1";
|
|
704
|
-
const res = await
|
|
688
|
+
const res = await proxyFetch(url, {
|
|
705
689
|
method: "GET",
|
|
706
690
|
headers: {
|
|
707
|
-
Authorization: `Bearer ${accessToken}`,
|
|
708
691
|
"LinkedIn-Version": "202603",
|
|
709
692
|
"X-Restli-Protocol-Version": "2.0.0"
|
|
710
693
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _squadbase_connectors_sdk from '@squadbase/connectors/sdk';
|
|
2
2
|
|
|
3
|
-
declare const connection: (connectionId: string) => _squadbase_connectors_sdk.
|
|
3
|
+
declare const connection: (connectionId: string) => _squadbase_connectors_sdk.MixpanelConnectorSdk;
|
|
4
4
|
|
|
5
5
|
export { connection };
|