@squadbase/vite-server 0.1.3-dev.9 → 0.1.3
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 +14229 -29321
- 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 +43 -6
- 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 +4476 -3949
- package/dist/main.js +4474 -3948
- package/dist/vite-plugin.js +4474 -3948
- 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,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 });
|
|
97
|
-
}
|
|
98
|
-
async function getAnalytics(queryParams) {
|
|
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
|
-
}));
|
|
49
|
+
const url = `${BASE_URL}${path2}`;
|
|
50
|
+
return fetchFn(url, init);
|
|
131
51
|
}
|
|
132
|
-
return {
|
|
133
|
-
request,
|
|
134
|
-
getAnalytics,
|
|
135
|
-
listAdAccounts
|
|
136
|
-
};
|
|
52
|
+
return { request };
|
|
137
53
|
}
|
|
138
54
|
|
|
139
55
|
// ../connectors/src/connector-onboarding.ts
|
|
@@ -218,21 +134,58 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
218
134
|
* Filters connections by connectorKey internally.
|
|
219
135
|
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
220
136
|
*/
|
|
221
|
-
createTools(connections, config) {
|
|
137
|
+
createTools(connections, config, opts) {
|
|
222
138
|
const myConnections = connections.filter(
|
|
223
139
|
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
224
140
|
);
|
|
225
141
|
const result = {};
|
|
226
142
|
for (const t of Object.values(this.tools)) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
143
|
+
const tool = t.createTool(myConnections, config);
|
|
144
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
145
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
146
|
+
...tool,
|
|
147
|
+
toModelOutput: async (options) => {
|
|
148
|
+
if (!originalToModelOutput) {
|
|
149
|
+
return opts.truncateOutput(options.output);
|
|
150
|
+
}
|
|
151
|
+
const modelOutput = await originalToModelOutput(options);
|
|
152
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
153
|
+
return opts.truncateOutput(modelOutput.value);
|
|
154
|
+
}
|
|
155
|
+
return modelOutput;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
231
158
|
}
|
|
232
159
|
return result;
|
|
233
160
|
}
|
|
234
161
|
static deriveKey(slug, authType) {
|
|
235
|
-
|
|
162
|
+
if (authType) return `${slug}-${authType}`;
|
|
163
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
164
|
+
// user-password
|
|
165
|
+
"postgresql": "user-password",
|
|
166
|
+
"mysql": "user-password",
|
|
167
|
+
"clickhouse": "user-password",
|
|
168
|
+
"kintone": "user-password",
|
|
169
|
+
"squadbase-db": "user-password",
|
|
170
|
+
// service-account
|
|
171
|
+
"snowflake": "service-account",
|
|
172
|
+
"bigquery": "service-account",
|
|
173
|
+
"google-analytics": "service-account",
|
|
174
|
+
"google-calendar": "service-account",
|
|
175
|
+
"aws-athena": "service-account",
|
|
176
|
+
"redshift": "service-account",
|
|
177
|
+
// api-key
|
|
178
|
+
"databricks": "api-key",
|
|
179
|
+
"dbt": "api-key",
|
|
180
|
+
"airtable": "api-key",
|
|
181
|
+
"openai": "api-key",
|
|
182
|
+
"gemini": "api-key",
|
|
183
|
+
"anthropic": "api-key",
|
|
184
|
+
"wix-store": "api-key"
|
|
185
|
+
};
|
|
186
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
187
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
188
|
+
return slug;
|
|
236
189
|
}
|
|
237
190
|
};
|
|
238
191
|
|
|
@@ -248,13 +201,45 @@ var AUTH_TYPES = {
|
|
|
248
201
|
|
|
249
202
|
// ../connectors/src/connectors/linkedin-ads/tools/list-ad-accounts.ts
|
|
250
203
|
import { z } from "zod";
|
|
204
|
+
var BASE_URL2 = "https://api.linkedin.com/rest/";
|
|
205
|
+
var LINKEDIN_VERSION = "202603";
|
|
251
206
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
252
|
-
var
|
|
207
|
+
var cachedToken = null;
|
|
208
|
+
async function getProxyToken(config) {
|
|
209
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
210
|
+
return cachedToken.token;
|
|
211
|
+
}
|
|
212
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
213
|
+
const res = await fetch(url, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: {
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
"x-api-key": config.appApiKey,
|
|
218
|
+
"project-id": config.projectId
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify({
|
|
221
|
+
sandboxId: config.sandboxId,
|
|
222
|
+
issuedBy: "coding-agent"
|
|
223
|
+
})
|
|
224
|
+
});
|
|
225
|
+
if (!res.ok) {
|
|
226
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const data = await res.json();
|
|
232
|
+
cachedToken = {
|
|
233
|
+
token: data.token,
|
|
234
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
235
|
+
};
|
|
236
|
+
return data.token;
|
|
237
|
+
}
|
|
253
238
|
var inputSchema = z.object({
|
|
254
239
|
toolUseIntent: z.string().optional().describe(
|
|
255
240
|
"Brief description of what you intend to accomplish with this tool call"
|
|
256
241
|
),
|
|
257
|
-
connectionId: z.string().describe("ID of the LinkedIn Ads connection to use")
|
|
242
|
+
connectionId: z.string().describe("ID of the LinkedIn Ads OAuth connection to use")
|
|
258
243
|
});
|
|
259
244
|
var outputSchema = z.discriminatedUnion("success", [
|
|
260
245
|
z.object({
|
|
@@ -273,10 +258,10 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
273
258
|
]);
|
|
274
259
|
var listAdAccountsTool = new ConnectorTool({
|
|
275
260
|
name: "listAdAccounts",
|
|
276
|
-
description: "List LinkedIn ad accounts accessible with the current
|
|
261
|
+
description: "List LinkedIn ad accounts accessible with the current OAuth credentials.",
|
|
277
262
|
inputSchema,
|
|
278
263
|
outputSchema,
|
|
279
|
-
async execute({ connectionId }, connections) {
|
|
264
|
+
async execute({ connectionId }, connections, config) {
|
|
280
265
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
281
266
|
if (!connection2) {
|
|
282
267
|
return {
|
|
@@ -288,18 +273,25 @@ var listAdAccountsTool = new ConnectorTool({
|
|
|
288
273
|
`[connector-request] linkedin-ads/${connection2.name}: listAdAccounts`
|
|
289
274
|
);
|
|
290
275
|
try {
|
|
291
|
-
const
|
|
292
|
-
const
|
|
276
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
277
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
293
278
|
const controller = new AbortController();
|
|
294
279
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
295
280
|
try {
|
|
296
|
-
const response = await fetch(
|
|
297
|
-
method: "
|
|
281
|
+
const response = await fetch(proxyUrl, {
|
|
282
|
+
method: "POST",
|
|
298
283
|
headers: {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"X-Restli-Protocol-Version": "2.0.0"
|
|
284
|
+
"Content-Type": "application/json",
|
|
285
|
+
Authorization: `Bearer ${token}`
|
|
302
286
|
},
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
url: `${BASE_URL2}adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100`,
|
|
289
|
+
method: "GET",
|
|
290
|
+
headers: {
|
|
291
|
+
"LinkedIn-Version": LINKEDIN_VERSION,
|
|
292
|
+
"X-Restli-Protocol-Version": "2.0.0"
|
|
293
|
+
}
|
|
294
|
+
}),
|
|
303
295
|
signal: controller.signal
|
|
304
296
|
});
|
|
305
297
|
const data = await response.json();
|
|
@@ -326,10 +318,10 @@ var listAdAccountsTool = new ConnectorTool({
|
|
|
326
318
|
var listAdAccountsToolName = `linkedin-ads_${listAdAccountsTool.name}`;
|
|
327
319
|
var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
328
320
|
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
|
|
321
|
+
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
322
|
|
|
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\
|
|
323
|
+
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
|
|
324
|
+
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
325
|
3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
334
326
|
- \`parameterSlug\`: \`"ad-account-id"\`
|
|
335
327
|
- \`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 +334,10 @@ var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
|
342
334
|
#### \u5236\u7D04
|
|
343
335
|
- **\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
336
|
- \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.
|
|
337
|
+
en: `Follow these steps to set up the LinkedIn Ads (OAuth) connection.
|
|
346
338
|
|
|
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
|
|
339
|
+
1. Call \`${listAdAccountsToolName}\` to get the list of ad accounts accessible with the OAuth credentials
|
|
340
|
+
2. If an error occurs, ask the user to verify their OAuth connection
|
|
349
341
|
3. Call \`updateConnectionParameters\`:
|
|
350
342
|
- \`parameterSlug\`: \`"ad-account-id"\`
|
|
351
343
|
- \`options\`: The ad account list. Each option's \`label\` should be \`Account Name (id: accountId)\`, \`value\` should be the account ID
|
|
@@ -369,19 +361,63 @@ var linkedinAdsOnboarding = new ConnectorOnboarding({
|
|
|
369
361
|
}
|
|
370
362
|
});
|
|
371
363
|
|
|
364
|
+
// ../connectors/src/connectors/linkedin-ads/parameters.ts
|
|
365
|
+
var parameters = {
|
|
366
|
+
adAccountId: new ParameterDefinition({
|
|
367
|
+
slug: "ad-account-id",
|
|
368
|
+
name: "Ad Account ID",
|
|
369
|
+
description: "The LinkedIn ad account ID (numeric). Found in Campaign Manager under Account Assets. The URN format urn:li:sponsoredAccount:{id} is constructed automatically.",
|
|
370
|
+
envVarBaseKey: "LINKEDIN_ADS_AD_ACCOUNT_ID",
|
|
371
|
+
type: "text",
|
|
372
|
+
secret: false,
|
|
373
|
+
required: false
|
|
374
|
+
})
|
|
375
|
+
};
|
|
376
|
+
|
|
372
377
|
// ../connectors/src/connectors/linkedin-ads/tools/request.ts
|
|
373
378
|
import { z as z2 } from "zod";
|
|
374
|
-
var
|
|
375
|
-
var
|
|
379
|
+
var BASE_URL3 = "https://api.linkedin.com/rest/";
|
|
380
|
+
var LINKEDIN_VERSION2 = "202603";
|
|
376
381
|
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
382
|
+
var cachedToken2 = null;
|
|
383
|
+
async function getProxyToken2(config) {
|
|
384
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
385
|
+
return cachedToken2.token;
|
|
386
|
+
}
|
|
387
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
388
|
+
const res = await fetch(url, {
|
|
389
|
+
method: "POST",
|
|
390
|
+
headers: {
|
|
391
|
+
"Content-Type": "application/json",
|
|
392
|
+
"x-api-key": config.appApiKey,
|
|
393
|
+
"project-id": config.projectId
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify({
|
|
396
|
+
sandboxId: config.sandboxId,
|
|
397
|
+
issuedBy: "coding-agent"
|
|
398
|
+
})
|
|
399
|
+
});
|
|
400
|
+
if (!res.ok) {
|
|
401
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const data = await res.json();
|
|
407
|
+
cachedToken2 = {
|
|
408
|
+
token: data.token,
|
|
409
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
410
|
+
};
|
|
411
|
+
return data.token;
|
|
412
|
+
}
|
|
377
413
|
var inputSchema2 = z2.object({
|
|
378
414
|
toolUseIntent: z2.string().optional().describe(
|
|
379
415
|
"Brief description of what you intend to accomplish with this tool call"
|
|
380
416
|
),
|
|
381
|
-
connectionId: z2.string().describe("ID of the LinkedIn Ads connection to use"),
|
|
417
|
+
connectionId: z2.string().describe("ID of the LinkedIn Ads OAuth connection to use"),
|
|
382
418
|
method: z2.enum(["GET", "POST", "DELETE"]).describe("HTTP method"),
|
|
383
419
|
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
|
|
420
|
+
"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
421
|
),
|
|
386
422
|
queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
|
|
387
423
|
body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON). For partial updates, use the patch format: { patch: { $set: { ... } } }")
|
|
@@ -400,12 +436,12 @@ var outputSchema2 = z2.discriminatedUnion("success", [
|
|
|
400
436
|
var requestTool = new ConnectorTool({
|
|
401
437
|
name: "request",
|
|
402
438
|
description: `Send authenticated requests to the LinkedIn Marketing API (REST).
|
|
403
|
-
Authentication is handled via
|
|
439
|
+
Authentication is handled automatically via OAuth proxy.
|
|
404
440
|
{adAccountId} in the path is automatically replaced with the connection's ad account ID.
|
|
405
|
-
Required headers (
|
|
441
|
+
Required headers (LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.`,
|
|
406
442
|
inputSchema: inputSchema2,
|
|
407
443
|
outputSchema: outputSchema2,
|
|
408
|
-
async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
|
|
444
|
+
async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
|
|
409
445
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
410
446
|
if (!connection2) {
|
|
411
447
|
return {
|
|
@@ -417,31 +453,38 @@ Required headers (Authorization, LinkedIn-Version, X-Restli-Protocol-Version) ar
|
|
|
417
453
|
`[connector-request] linkedin-ads/${connection2.name}: ${method} ${path2}`
|
|
418
454
|
);
|
|
419
455
|
try {
|
|
420
|
-
const accessToken = parameters.accessToken.getValue(connection2);
|
|
421
456
|
const adAccountId = parameters.adAccountId.tryGetValue(connection2) ?? "";
|
|
422
457
|
const resolvedPath = adAccountId ? path2.replace(/\{adAccountId\}/g, adAccountId) : path2;
|
|
423
|
-
let url = `${
|
|
458
|
+
let url = `${BASE_URL3}${resolvedPath}`;
|
|
424
459
|
if (queryParams && Object.keys(queryParams).length > 0) {
|
|
425
460
|
const params = new URLSearchParams(queryParams);
|
|
426
461
|
const separator = url.includes("?") ? "&" : "?";
|
|
427
462
|
url = `${url}${separator}${params.toString()}`;
|
|
428
463
|
}
|
|
464
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
465
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
429
466
|
const controller = new AbortController();
|
|
430
467
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
431
468
|
try {
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
"
|
|
435
|
-
"X-Restli-Protocol-Version": "2.0.0",
|
|
436
|
-
"Content-Type": "application/json"
|
|
469
|
+
const additionalHeaders = {
|
|
470
|
+
"LinkedIn-Version": LINKEDIN_VERSION2,
|
|
471
|
+
"X-Restli-Protocol-Version": "2.0.0"
|
|
437
472
|
};
|
|
438
473
|
if (body && "patch" in body) {
|
|
439
|
-
|
|
474
|
+
additionalHeaders["X-RestLi-Method"] = "PARTIAL_UPDATE";
|
|
440
475
|
}
|
|
441
|
-
const response = await fetch(
|
|
442
|
-
method,
|
|
443
|
-
headers
|
|
444
|
-
|
|
476
|
+
const response = await fetch(proxyUrl, {
|
|
477
|
+
method: "POST",
|
|
478
|
+
headers: {
|
|
479
|
+
"Content-Type": "application/json",
|
|
480
|
+
Authorization: `Bearer ${token}`
|
|
481
|
+
},
|
|
482
|
+
body: JSON.stringify({
|
|
483
|
+
url,
|
|
484
|
+
method,
|
|
485
|
+
headers: additionalHeaders,
|
|
486
|
+
...(method === "POST" || method === "DELETE") && body ? { body: JSON.stringify(body) } : {}
|
|
487
|
+
}),
|
|
445
488
|
signal: controller.signal
|
|
446
489
|
});
|
|
447
490
|
if (response.status === 204) {
|
|
@@ -471,17 +514,25 @@ var tools = {
|
|
|
471
514
|
};
|
|
472
515
|
var linkedinAdsConnector = new ConnectorPlugin({
|
|
473
516
|
slug: "linkedin-ads",
|
|
474
|
-
authType: AUTH_TYPES.
|
|
517
|
+
authType: AUTH_TYPES.OAUTH,
|
|
475
518
|
name: "LinkedIn Ads",
|
|
476
|
-
description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using
|
|
519
|
+
description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using OAuth.",
|
|
477
520
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3x7xd9pVJkRFY7ADIg4ycq/b94720e34cb081e9ae45dfde799a59cd/LinkedIn_icon.svg.png",
|
|
478
521
|
parameters,
|
|
479
522
|
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
480
523
|
onboarding: linkedinAdsOnboarding,
|
|
524
|
+
proxyPolicy: {
|
|
525
|
+
allowlist: [
|
|
526
|
+
{
|
|
527
|
+
host: "api.linkedin.com",
|
|
528
|
+
methods: ["GET", "POST", "DELETE"]
|
|
529
|
+
}
|
|
530
|
+
]
|
|
531
|
+
},
|
|
481
532
|
systemPrompt: {
|
|
482
533
|
en: `### Tools
|
|
483
534
|
|
|
484
|
-
- \`linkedin-ads_request\`: Send authenticated requests to the LinkedIn Marketing API (REST). The {adAccountId} placeholder in paths is automatically replaced. Authentication is
|
|
535
|
+
- \`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
536
|
- \`linkedin-ads_listAdAccounts\`: List accessible LinkedIn ad accounts. Use this during setup to discover available accounts.
|
|
486
537
|
|
|
487
538
|
### LinkedIn Marketing API Reference
|
|
@@ -568,25 +619,13 @@ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
|
|
|
568
619
|
|
|
569
620
|
const linkedin = connection("<connectionId>");
|
|
570
621
|
|
|
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
622
|
// Get campaigns
|
|
584
623
|
const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
|
|
585
624
|
const data = await res.json();
|
|
586
625
|
\`\`\``,
|
|
587
626
|
ja: `### \u30C4\u30FC\u30EB
|
|
588
627
|
|
|
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\
|
|
628
|
+
- \`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
629
|
- \`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
630
|
|
|
592
631
|
### LinkedIn Marketing API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
@@ -673,38 +712,19 @@ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
|
|
|
673
712
|
|
|
674
713
|
const linkedin = connection("<connectionId>");
|
|
675
714
|
|
|
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
715
|
// \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u3092\u53D6\u5F97
|
|
689
716
|
const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
|
|
690
717
|
const data = await res.json();
|
|
691
718
|
\`\`\``
|
|
692
719
|
},
|
|
693
720
|
tools,
|
|
694
|
-
async checkConnection(
|
|
695
|
-
const
|
|
696
|
-
if (!accessToken) {
|
|
697
|
-
return {
|
|
698
|
-
success: false,
|
|
699
|
-
error: "Access token is required"
|
|
700
|
-
};
|
|
701
|
-
}
|
|
721
|
+
async checkConnection(_params, config) {
|
|
722
|
+
const { proxyFetch } = config;
|
|
702
723
|
try {
|
|
703
724
|
const url = "https://api.linkedin.com/rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=1";
|
|
704
|
-
const res = await
|
|
725
|
+
const res = await proxyFetch(url, {
|
|
705
726
|
method: "GET",
|
|
706
727
|
headers: {
|
|
707
|
-
Authorization: `Bearer ${accessToken}`,
|
|
708
728
|
"LinkedIn-Version": "202603",
|
|
709
729
|
"X-Restli-Protocol-Version": "2.0.0"
|
|
710
730
|
}
|
|
@@ -140,21 +140,58 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
140
140
|
* Filters connections by connectorKey internally.
|
|
141
141
|
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
142
142
|
*/
|
|
143
|
-
createTools(connections, config) {
|
|
143
|
+
createTools(connections, config, opts) {
|
|
144
144
|
const myConnections = connections.filter(
|
|
145
145
|
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
146
146
|
);
|
|
147
147
|
const result = {};
|
|
148
148
|
for (const t of Object.values(this.tools)) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
const tool = t.createTool(myConnections, config);
|
|
150
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
151
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
152
|
+
...tool,
|
|
153
|
+
toModelOutput: async (options) => {
|
|
154
|
+
if (!originalToModelOutput) {
|
|
155
|
+
return opts.truncateOutput(options.output);
|
|
156
|
+
}
|
|
157
|
+
const modelOutput = await originalToModelOutput(options);
|
|
158
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
159
|
+
return opts.truncateOutput(modelOutput.value);
|
|
160
|
+
}
|
|
161
|
+
return modelOutput;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
153
164
|
}
|
|
154
165
|
return result;
|
|
155
166
|
}
|
|
156
167
|
static deriveKey(slug, authType) {
|
|
157
|
-
|
|
168
|
+
if (authType) return `${slug}-${authType}`;
|
|
169
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
170
|
+
// user-password
|
|
171
|
+
"postgresql": "user-password",
|
|
172
|
+
"mysql": "user-password",
|
|
173
|
+
"clickhouse": "user-password",
|
|
174
|
+
"kintone": "user-password",
|
|
175
|
+
"squadbase-db": "user-password",
|
|
176
|
+
// service-account
|
|
177
|
+
"snowflake": "service-account",
|
|
178
|
+
"bigquery": "service-account",
|
|
179
|
+
"google-analytics": "service-account",
|
|
180
|
+
"google-calendar": "service-account",
|
|
181
|
+
"aws-athena": "service-account",
|
|
182
|
+
"redshift": "service-account",
|
|
183
|
+
// api-key
|
|
184
|
+
"databricks": "api-key",
|
|
185
|
+
"dbt": "api-key",
|
|
186
|
+
"airtable": "api-key",
|
|
187
|
+
"openai": "api-key",
|
|
188
|
+
"gemini": "api-key",
|
|
189
|
+
"anthropic": "api-key",
|
|
190
|
+
"wix-store": "api-key"
|
|
191
|
+
};
|
|
192
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
193
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
194
|
+
return slug;
|
|
158
195
|
}
|
|
159
196
|
};
|
|
160
197
|
|
|
@@ -244,21 +244,58 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
244
244
|
* Filters connections by connectorKey internally.
|
|
245
245
|
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
246
246
|
*/
|
|
247
|
-
createTools(connections, config) {
|
|
247
|
+
createTools(connections, config, opts) {
|
|
248
248
|
const myConnections = connections.filter(
|
|
249
249
|
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
250
250
|
);
|
|
251
251
|
const result = {};
|
|
252
252
|
for (const t of Object.values(this.tools)) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
const tool = t.createTool(myConnections, config);
|
|
254
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
255
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
256
|
+
...tool,
|
|
257
|
+
toModelOutput: async (options) => {
|
|
258
|
+
if (!originalToModelOutput) {
|
|
259
|
+
return opts.truncateOutput(options.output);
|
|
260
|
+
}
|
|
261
|
+
const modelOutput = await originalToModelOutput(options);
|
|
262
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
263
|
+
return opts.truncateOutput(modelOutput.value);
|
|
264
|
+
}
|
|
265
|
+
return modelOutput;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
257
268
|
}
|
|
258
269
|
return result;
|
|
259
270
|
}
|
|
260
271
|
static deriveKey(slug, authType) {
|
|
261
|
-
|
|
272
|
+
if (authType) return `${slug}-${authType}`;
|
|
273
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
274
|
+
// user-password
|
|
275
|
+
"postgresql": "user-password",
|
|
276
|
+
"mysql": "user-password",
|
|
277
|
+
"clickhouse": "user-password",
|
|
278
|
+
"kintone": "user-password",
|
|
279
|
+
"squadbase-db": "user-password",
|
|
280
|
+
// service-account
|
|
281
|
+
"snowflake": "service-account",
|
|
282
|
+
"bigquery": "service-account",
|
|
283
|
+
"google-analytics": "service-account",
|
|
284
|
+
"google-calendar": "service-account",
|
|
285
|
+
"aws-athena": "service-account",
|
|
286
|
+
"redshift": "service-account",
|
|
287
|
+
// api-key
|
|
288
|
+
"databricks": "api-key",
|
|
289
|
+
"dbt": "api-key",
|
|
290
|
+
"airtable": "api-key",
|
|
291
|
+
"openai": "api-key",
|
|
292
|
+
"gemini": "api-key",
|
|
293
|
+
"anthropic": "api-key",
|
|
294
|
+
"wix-store": "api-key"
|
|
295
|
+
};
|
|
296
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
297
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
298
|
+
return slug;
|
|
262
299
|
}
|
|
263
300
|
};
|
|
264
301
|
|