@squadbase/vite-server 0.1.3-dev.0 → 0.1.3-dev.10

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.
Files changed (70) hide show
  1. package/dist/cli/index.js +82859 -9645
  2. package/dist/connectors/airtable-oauth.js +77 -3
  3. package/dist/connectors/airtable.js +85 -2
  4. package/dist/connectors/amplitude.js +85 -2
  5. package/dist/connectors/anthropic.js +85 -2
  6. package/dist/connectors/{slack.d.ts → asana.d.ts} +1 -1
  7. package/dist/connectors/asana.js +744 -0
  8. package/dist/connectors/attio.js +85 -2
  9. package/dist/connectors/{microsoft-teams-oauth.d.ts → customerio.d.ts} +1 -1
  10. package/dist/connectors/customerio.js +716 -0
  11. package/dist/connectors/dbt.js +85 -2
  12. package/dist/connectors/gemini.js +86 -3
  13. package/dist/connectors/{microsoft-teams.d.ts → gmail-oauth.d.ts} +1 -1
  14. package/dist/connectors/gmail-oauth.js +713 -0
  15. package/dist/connectors/gmail.d.ts +5 -0
  16. package/dist/connectors/gmail.js +875 -0
  17. package/dist/connectors/google-ads-oauth.js +78 -4
  18. package/dist/connectors/google-ads.d.ts +5 -0
  19. package/dist/connectors/google-ads.js +867 -0
  20. package/dist/connectors/google-analytics-oauth.js +90 -8
  21. package/dist/connectors/google-analytics.js +85 -2
  22. package/dist/connectors/google-calendar-oauth.d.ts +5 -0
  23. package/dist/connectors/google-calendar-oauth.js +817 -0
  24. package/dist/connectors/google-calendar.d.ts +5 -0
  25. package/dist/connectors/google-calendar.js +991 -0
  26. package/dist/connectors/google-sheets-oauth.js +144 -33
  27. package/dist/connectors/google-sheets.d.ts +5 -0
  28. package/dist/connectors/google-sheets.js +707 -0
  29. package/dist/connectors/grafana.d.ts +5 -0
  30. package/dist/connectors/grafana.js +638 -0
  31. package/dist/connectors/hubspot-oauth.js +77 -3
  32. package/dist/connectors/hubspot.js +89 -6
  33. package/dist/connectors/intercom-oauth.d.ts +5 -0
  34. package/dist/connectors/intercom-oauth.js +584 -0
  35. package/dist/connectors/intercom.d.ts +5 -0
  36. package/dist/connectors/intercom.js +710 -0
  37. package/dist/connectors/jira-api-key.d.ts +5 -0
  38. package/dist/connectors/jira-api-key.js +598 -0
  39. package/dist/connectors/kintone-api-token.js +77 -3
  40. package/dist/connectors/kintone.js +86 -3
  41. package/dist/connectors/linkedin-ads-oauth.d.ts +5 -0
  42. package/dist/connectors/linkedin-ads-oauth.js +848 -0
  43. package/dist/connectors/linkedin-ads.d.ts +5 -0
  44. package/dist/connectors/linkedin-ads.js +865 -0
  45. package/dist/connectors/mailchimp-oauth.d.ts +5 -0
  46. package/dist/connectors/mailchimp-oauth.js +613 -0
  47. package/dist/connectors/mailchimp.d.ts +5 -0
  48. package/dist/connectors/mailchimp.js +729 -0
  49. package/dist/connectors/notion-oauth.d.ts +5 -0
  50. package/dist/connectors/notion-oauth.js +567 -0
  51. package/dist/connectors/notion.d.ts +5 -0
  52. package/dist/connectors/notion.js +663 -0
  53. package/dist/connectors/openai.js +85 -2
  54. package/dist/connectors/shopify-oauth.js +77 -3
  55. package/dist/connectors/shopify.js +85 -2
  56. package/dist/connectors/stripe-api-key.d.ts +5 -0
  57. package/dist/connectors/stripe-api-key.js +600 -0
  58. package/dist/connectors/stripe-oauth.js +77 -3
  59. package/dist/connectors/wix-store.js +85 -2
  60. package/dist/connectors/zendesk-oauth.d.ts +5 -0
  61. package/dist/connectors/zendesk-oauth.js +579 -0
  62. package/dist/connectors/zendesk.d.ts +5 -0
  63. package/dist/connectors/zendesk.js +714 -0
  64. package/dist/index.js +83024 -7099
  65. package/dist/main.js +82988 -7063
  66. package/dist/vite-plugin.js +82862 -6974
  67. package/package.json +86 -2
  68. package/dist/connectors/microsoft-teams-oauth.js +0 -479
  69. package/dist/connectors/microsoft-teams.js +0 -381
  70. package/dist/connectors/slack.js +0 -362
@@ -0,0 +1,865 @@
1
+ // ../connectors/src/parameter-definition.ts
2
+ var ParameterDefinition = class {
3
+ slug;
4
+ name;
5
+ description;
6
+ envVarBaseKey;
7
+ type;
8
+ secret;
9
+ required;
10
+ constructor(config) {
11
+ this.slug = config.slug;
12
+ this.name = config.name;
13
+ this.description = config.description;
14
+ this.envVarBaseKey = config.envVarBaseKey;
15
+ this.type = config.type;
16
+ this.secret = config.secret;
17
+ this.required = config.required;
18
+ }
19
+ /**
20
+ * Get the parameter value from a ConnectorConnectionObject.
21
+ */
22
+ getValue(connection2) {
23
+ const param = connection2.parameters.find(
24
+ (p) => p.parameterSlug === this.slug
25
+ );
26
+ if (!param || param.value == null) {
27
+ throw new Error(
28
+ `Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
29
+ );
30
+ }
31
+ return param.value;
32
+ }
33
+ /**
34
+ * Try to get the parameter value. Returns undefined if not found (for optional params).
35
+ */
36
+ tryGetValue(connection2) {
37
+ const param = connection2.parameters.find(
38
+ (p) => p.parameterSlug === this.slug
39
+ );
40
+ if (!param || param.value == null) return void 0;
41
+ return param.value;
42
+ }
43
+ };
44
+
45
+ // ../connectors/src/connectors/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
+ // ../connectors/src/connectors/linkedin-ads/sdk/index.ts
68
+ var BASE_URL = "https://api.linkedin.com/rest/";
69
+ var LINKEDIN_VERSION = "202603";
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
+ }
87
+ function request(path2, init) {
88
+ const resolvedPath = defaultAdAccountId ? path2.replace(/\{adAccountId\}/g, defaultAdAccountId) : path2;
89
+ const url = `${BASE_URL}${resolvedPath}`;
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
+ }));
131
+ }
132
+ return {
133
+ request,
134
+ getAnalytics,
135
+ listAdAccounts
136
+ };
137
+ }
138
+
139
+ // ../connectors/src/connector-onboarding.ts
140
+ var ConnectorOnboarding = class {
141
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
142
+ connectionSetupInstructions;
143
+ /** Phase 2: Data overview instructions */
144
+ dataOverviewInstructions;
145
+ constructor(config) {
146
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
147
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
148
+ }
149
+ getConnectionSetupPrompt(language) {
150
+ return this.connectionSetupInstructions?.[language] ?? null;
151
+ }
152
+ getDataOverviewInstructions(language) {
153
+ return this.dataOverviewInstructions[language];
154
+ }
155
+ };
156
+
157
+ // ../connectors/src/connector-tool.ts
158
+ var ConnectorTool = class {
159
+ name;
160
+ description;
161
+ inputSchema;
162
+ outputSchema;
163
+ _execute;
164
+ constructor(config) {
165
+ this.name = config.name;
166
+ this.description = config.description;
167
+ this.inputSchema = config.inputSchema;
168
+ this.outputSchema = config.outputSchema;
169
+ this._execute = config.execute;
170
+ }
171
+ createTool(connections, config) {
172
+ return {
173
+ description: this.description,
174
+ inputSchema: this.inputSchema,
175
+ outputSchema: this.outputSchema,
176
+ execute: (input) => this._execute(input, connections, config)
177
+ };
178
+ }
179
+ };
180
+
181
+ // ../connectors/src/connector-plugin.ts
182
+ var ConnectorPlugin = class _ConnectorPlugin {
183
+ slug;
184
+ authType;
185
+ name;
186
+ description;
187
+ iconUrl;
188
+ parameters;
189
+ releaseFlag;
190
+ proxyPolicy;
191
+ experimentalAttributes;
192
+ onboarding;
193
+ systemPrompt;
194
+ tools;
195
+ query;
196
+ checkConnection;
197
+ constructor(config) {
198
+ this.slug = config.slug;
199
+ this.authType = config.authType;
200
+ this.name = config.name;
201
+ this.description = config.description;
202
+ this.iconUrl = config.iconUrl;
203
+ this.parameters = config.parameters;
204
+ this.releaseFlag = config.releaseFlag;
205
+ this.proxyPolicy = config.proxyPolicy;
206
+ this.experimentalAttributes = config.experimentalAttributes;
207
+ this.onboarding = config.onboarding;
208
+ this.systemPrompt = config.systemPrompt;
209
+ this.tools = config.tools;
210
+ this.query = config.query;
211
+ this.checkConnection = config.checkConnection;
212
+ }
213
+ get connectorKey() {
214
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
215
+ }
216
+ /**
217
+ * Create tools for connections that belong to this connector.
218
+ * Filters connections by connectorKey internally.
219
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
220
+ */
221
+ createTools(connections, config) {
222
+ const myConnections = connections.filter(
223
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
224
+ );
225
+ const result = {};
226
+ for (const t of Object.values(this.tools)) {
227
+ result[`${this.connectorKey}_${t.name}`] = t.createTool(
228
+ myConnections,
229
+ config
230
+ );
231
+ }
232
+ return result;
233
+ }
234
+ static deriveKey(slug, authType) {
235
+ return authType ? `${slug}-${authType}` : slug;
236
+ }
237
+ };
238
+
239
+ // ../connectors/src/auth-types.ts
240
+ var AUTH_TYPES = {
241
+ OAUTH: "oauth",
242
+ API_KEY: "api-key",
243
+ JWT: "jwt",
244
+ SERVICE_ACCOUNT: "service-account",
245
+ PAT: "pat",
246
+ USER_PASSWORD: "user-password"
247
+ };
248
+
249
+ // ../connectors/src/connectors/linkedin-ads/tools/list-ad-accounts.ts
250
+ import { z } from "zod";
251
+ var REQUEST_TIMEOUT_MS = 6e4;
252
+ var LINKEDIN_VERSION2 = "202603";
253
+ var inputSchema = z.object({
254
+ toolUseIntent: z.string().optional().describe(
255
+ "Brief description of what you intend to accomplish with this tool call"
256
+ ),
257
+ connectionId: z.string().describe("ID of the LinkedIn Ads connection to use")
258
+ });
259
+ var outputSchema = z.discriminatedUnion("success", [
260
+ z.object({
261
+ success: z.literal(true),
262
+ adAccounts: z.array(
263
+ z.object({
264
+ adAccountId: z.string(),
265
+ name: z.string()
266
+ })
267
+ )
268
+ }),
269
+ z.object({
270
+ success: z.literal(false),
271
+ error: z.string()
272
+ })
273
+ ]);
274
+ var listAdAccountsTool = new ConnectorTool({
275
+ name: "listAdAccounts",
276
+ description: "List LinkedIn ad accounts accessible with the current access token.",
277
+ inputSchema,
278
+ outputSchema,
279
+ async execute({ connectionId }, connections) {
280
+ const connection2 = connections.find((c) => c.id === connectionId);
281
+ if (!connection2) {
282
+ return {
283
+ success: false,
284
+ error: `Connection ${connectionId} not found`
285
+ };
286
+ }
287
+ console.log(
288
+ `[connector-request] linkedin-ads/${connection2.name}: listAdAccounts`
289
+ );
290
+ try {
291
+ const accessToken = parameters.accessToken.getValue(connection2);
292
+ const url = "https://api.linkedin.com/rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100";
293
+ const controller = new AbortController();
294
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
295
+ try {
296
+ const response = await fetch(url, {
297
+ method: "GET",
298
+ headers: {
299
+ Authorization: `Bearer ${accessToken}`,
300
+ "LinkedIn-Version": LINKEDIN_VERSION2,
301
+ "X-Restli-Protocol-Version": "2.0.0"
302
+ },
303
+ signal: controller.signal
304
+ });
305
+ const data = await response.json();
306
+ if (!response.ok) {
307
+ const errorMessage = data?.message ?? `HTTP ${response.status} ${response.statusText}`;
308
+ return { success: false, error: errorMessage };
309
+ }
310
+ const adAccounts = (data.elements ?? []).map((account) => ({
311
+ adAccountId: String(account.id ?? ""),
312
+ name: account.name ?? String(account.id ?? "")
313
+ }));
314
+ return { success: true, adAccounts };
315
+ } finally {
316
+ clearTimeout(timeout);
317
+ }
318
+ } catch (err) {
319
+ const msg = err instanceof Error ? err.message : String(err);
320
+ return { success: false, error: msg };
321
+ }
322
+ }
323
+ });
324
+
325
+ // ../connectors/src/connectors/linkedin-ads/setup.ts
326
+ var listAdAccountsToolName = `linkedin-ads_${listAdAccountsTool.name}`;
327
+ var linkedinAdsOnboarding = new ConnectorOnboarding({
328
+ 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
330
+
331
+ 1. \`${listAdAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
332
+ 2. \u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F\u5834\u5408\u306F\u30E6\u30FC\u30B6\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u78BA\u8A8D\u3092\u4F9D\u983C\u3059\u308B
333
+ 3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
334
+ - \`parameterSlug\`: \`"ad-account-id"\`
335
+ - \`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
336
+ 4. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\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
337
+ 5. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
338
+ - \`adAccount\`: \u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u8868\u793A\u540D
339
+ - \`adAccountId\`: \u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8ID
340
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
341
+
342
+ #### \u5236\u7D04
343
+ - **\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
+ - \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.
346
+
347
+ 1. Call \`${listAdAccountsToolName}\` to get the list of ad accounts accessible with the access token
348
+ 2. If an error occurs, ask the user to verify their access token
349
+ 3. Call \`updateConnectionParameters\`:
350
+ - \`parameterSlug\`: \`"ad-account-id"\`
351
+ - \`options\`: The ad account list. Each option's \`label\` should be \`Account Name (id: accountId)\`, \`value\` should be the account ID
352
+ 4. The \`label\` of the user's selected account will arrive as a message. Proceed to the next step
353
+ 5. Call \`updateConnectionContext\`:
354
+ - \`adAccount\`: The selected account's display name
355
+ - \`adAccountId\`: The selected account ID
356
+ - \`note\`: Brief description of the setup
357
+
358
+ #### Constraints
359
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
360
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
361
+ },
362
+ dataOverviewInstructions: {
363
+ en: `1. Call linkedin-ads_request with GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=10 to explore campaigns
364
+ 2. Call linkedin-ads_request with GET adAnalytics?q=analytics&pivot=CAMPAIGN&timeGranularity=MONTHLY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues to check recent performance
365
+ 3. Explore campaign groups and creatives as needed to understand the data structure`,
366
+ ja: `1. linkedin-ads_request \u3067 GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=10 \u3092\u547C\u3073\u51FA\u3057\u3066\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30C7\u30FC\u30BF\u3092\u63A2\u7D22
367
+ 2. linkedin-ads_request \u3067 GET adAnalytics?q=analytics&pivot=CAMPAIGN&timeGranularity=MONTHLY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues \u3092\u547C\u3073\u51FA\u3057\u3066\u76F4\u8FD1\u306E\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u3092\u78BA\u8A8D
368
+ 3. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7\u3084\u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6\u3092\u63A2\u7D22\u3057\u3001\u30C7\u30FC\u30BF\u69CB\u9020\u3092\u628A\u63E1`
369
+ }
370
+ });
371
+
372
+ // ../connectors/src/connectors/linkedin-ads/tools/request.ts
373
+ import { z as z2 } from "zod";
374
+ var BASE_URL2 = "https://api.linkedin.com/rest/";
375
+ var LINKEDIN_VERSION3 = "202603";
376
+ var REQUEST_TIMEOUT_MS2 = 6e4;
377
+ var inputSchema2 = z2.object({
378
+ toolUseIntent: z2.string().optional().describe(
379
+ "Brief description of what you intend to accomplish with this tool call"
380
+ ),
381
+ connectionId: z2.string().describe("ID of the LinkedIn Ads connection to use"),
382
+ method: z2.enum(["GET", "POST", "DELETE"]).describe("HTTP method"),
383
+ 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 configured ad account ID."
385
+ ),
386
+ queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
387
+ body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON). For partial updates, use the patch format: { patch: { $set: { ... } } }")
388
+ });
389
+ var outputSchema2 = z2.discriminatedUnion("success", [
390
+ z2.object({
391
+ success: z2.literal(true),
392
+ status: z2.number(),
393
+ data: z2.unknown()
394
+ }),
395
+ z2.object({
396
+ success: z2.literal(false),
397
+ error: z2.string()
398
+ })
399
+ ]);
400
+ var requestTool = new ConnectorTool({
401
+ name: "request",
402
+ description: `Send authenticated requests to the LinkedIn Marketing API (REST).
403
+ Authentication is handled via the configured access token.
404
+ {adAccountId} in the path is automatically replaced with the connection's ad account ID.
405
+ Required headers (Authorization, LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.`,
406
+ inputSchema: inputSchema2,
407
+ outputSchema: outputSchema2,
408
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
409
+ const connection2 = connections.find((c) => c.id === connectionId);
410
+ if (!connection2) {
411
+ return {
412
+ success: false,
413
+ error: `Connection ${connectionId} not found`
414
+ };
415
+ }
416
+ console.log(
417
+ `[connector-request] linkedin-ads/${connection2.name}: ${method} ${path2}`
418
+ );
419
+ try {
420
+ const accessToken = parameters.accessToken.getValue(connection2);
421
+ const adAccountId = parameters.adAccountId.tryGetValue(connection2) ?? "";
422
+ const resolvedPath = adAccountId ? path2.replace(/\{adAccountId\}/g, adAccountId) : path2;
423
+ let url = `${BASE_URL2}${resolvedPath}`;
424
+ if (queryParams && Object.keys(queryParams).length > 0) {
425
+ const params = new URLSearchParams(queryParams);
426
+ const separator = url.includes("?") ? "&" : "?";
427
+ url = `${url}${separator}${params.toString()}`;
428
+ }
429
+ const controller = new AbortController();
430
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
431
+ try {
432
+ const headers = {
433
+ Authorization: `Bearer ${accessToken}`,
434
+ "LinkedIn-Version": LINKEDIN_VERSION3,
435
+ "X-Restli-Protocol-Version": "2.0.0",
436
+ "Content-Type": "application/json"
437
+ };
438
+ if (body && "patch" in body) {
439
+ headers["X-RestLi-Method"] = "PARTIAL_UPDATE";
440
+ }
441
+ const response = await fetch(url, {
442
+ method,
443
+ headers,
444
+ ...(method === "POST" || method === "DELETE") && body ? { body: JSON.stringify(body) } : {},
445
+ signal: controller.signal
446
+ });
447
+ if (response.status === 204) {
448
+ return { success: true, status: 204, data: {} };
449
+ }
450
+ const data = await response.json();
451
+ if (!response.ok) {
452
+ const dataObj = data;
453
+ const errorMessage = typeof dataObj?.message === "string" ? dataObj.message : typeof dataObj?.error === "string" ? dataObj.error : `HTTP ${response.status} ${response.statusText}`;
454
+ return { success: false, error: errorMessage };
455
+ }
456
+ return { success: true, status: response.status, data };
457
+ } finally {
458
+ clearTimeout(timeout);
459
+ }
460
+ } catch (err) {
461
+ const msg = err instanceof Error ? err.message : String(err);
462
+ return { success: false, error: msg };
463
+ }
464
+ }
465
+ });
466
+
467
+ // ../connectors/src/connectors/linkedin-ads/index.ts
468
+ var tools = {
469
+ request: requestTool,
470
+ listAdAccounts: listAdAccountsTool
471
+ };
472
+ var linkedinAdsConnector = new ConnectorPlugin({
473
+ slug: "linkedin-ads",
474
+ authType: AUTH_TYPES.API_KEY,
475
+ name: "LinkedIn Ads",
476
+ description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using an access token.",
477
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3x7xd9pVJkRFY7ADIg4ycq/b94720e34cb081e9ae45dfde799a59cd/LinkedIn_icon.svg.png",
478
+ parameters,
479
+ releaseFlag: { dev1: true, dev2: false, prod: false },
480
+ onboarding: linkedinAdsOnboarding,
481
+ systemPrompt: {
482
+ en: `### Tools
483
+
484
+ - \`linkedin-ads_request\`: Send authenticated requests to the LinkedIn Marketing API (REST). The {adAccountId} placeholder in paths is automatically replaced. Authentication is configured via the access token parameter. Required headers (Authorization, LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.
485
+ - \`linkedin-ads_listAdAccounts\`: List accessible LinkedIn ad accounts. Use this during setup to discover available accounts.
486
+
487
+ ### LinkedIn Marketing API Reference
488
+
489
+ Base URL: https://api.linkedin.com/rest/
490
+ API Version: 202603 (set automatically via LinkedIn-Version header)
491
+
492
+ #### Account Hierarchy
493
+ Ad Account (sponsoredAccount) > Campaign Group (sponsoredCampaignGroup) > Campaign (adCampaign) > Creative
494
+
495
+ #### Search Ad Accounts
496
+ - GET adAccounts?q=search&search=(status:(values:List(ACTIVE)))
497
+
498
+ #### Search Campaigns
499
+ - GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100
500
+ - Search params: search.status.values, search.type.values, search.name.values, search.campaignGroup.values
501
+
502
+ #### Search Campaign Groups
503
+ - GET adAccounts/{adAccountId}/adCampaignGroups?q=search&search=(status:(values:List(ACTIVE)))
504
+
505
+ #### Search Creatives
506
+ - GET adAccounts/{adAccountId}/adCreatives?q=search&search=(status:(values:List(ACTIVE)))
507
+
508
+ #### Get Ad Analytics (Analytics Finder - single pivot)
509
+ - GET adAnalytics?q=analytics&pivot=CAMPAIGN&timeGranularity=DAILY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues
510
+
511
+ #### Get Ad Analytics (Statistics Finder - up to 3 pivots)
512
+ - GET adAnalytics?q=statistics&pivots=List(CAMPAIGN,CREATIVE)&timeGranularity=DAILY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues
513
+
514
+ ### Analytics Pivots
515
+ ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, COMPANY, CONVERSION,
516
+ MEMBER_COMPANY, MEMBER_COMPANY_SIZE, MEMBER_COUNTRY_V2, MEMBER_REGION_V2,
517
+ MEMBER_JOB_FUNCTION, MEMBER_JOB_TITLE, MEMBER_INDUSTRY, MEMBER_SENIORITY
518
+
519
+ ### Analytics Time Granularity
520
+ DAILY, MONTHLY, YEARLY, ALL
521
+
522
+ ### Key Analytics Metrics (specify via fields parameter)
523
+ impressions, clicks, costInLocalCurrency, costInUsd, landingPageClicks,
524
+ totalEngagements, likes, comments, shares, follows, reactions,
525
+ externalWebsiteConversions, conversionValueInLocalCurrency,
526
+ oneClickLeads, oneClickLeadFormOpens, videoViews, videoCompletions,
527
+ videoStarts, sends, opens, approximateMemberReach, dateRange, pivotValues
528
+
529
+ ### Analytics Facets (filters)
530
+ - \`accounts\`: List of account URNs \u2014 accounts=List(urn%3Ali%3AsponsoredAccount%3A123)
531
+ - \`campaigns\`: List of campaign URNs \u2014 campaigns=List(urn%3Ali%3AsponsoredCampaign%3A456)
532
+ - \`campaignGroups\`: List of campaign group URNs
533
+ - \`creatives\`: List of creative URNs
534
+
535
+ ### Date Range Format
536
+ dateRange=(start:(year:2025,month:1,day:1),end:(year:2025,month:12,day:31))
537
+ End date is optional (defaults to today).
538
+
539
+ ### URN Formats
540
+ - Ad Account: urn:li:sponsoredAccount:{id}
541
+ - Campaign Group: urn:li:sponsoredCampaignGroup:{id}
542
+ - Campaign: urn:li:sponsoredCampaign:{id}
543
+ - Creative: urn:li:sponsoredCreative:{id}
544
+
545
+ ### Pagination
546
+ Search APIs use cursor-based pagination with pageSize (max 1000) and pageToken.
547
+ Response includes metadata.nextPageToken for fetching next page.
548
+
549
+ ### Partial Updates
550
+ Use POST with X-RestLi-Method: PARTIAL_UPDATE header (set automatically when body contains "patch").
551
+ Body format: { "patch": { "$set": { "field": "value" } } }
552
+
553
+ ### Tips
554
+ - Always URL-encode URNs in query parameters (: \u2192 %3A)
555
+ - Use fields parameter in adAnalytics to request specific metrics (default: only impressions and clicks)
556
+ - Request up to 20 metrics per analytics call
557
+ - adAnalytics does not support pagination \u2014 response is limited to 15,000 elements
558
+ - Professional demographic pivots (MEMBER_*) may have 12-24h delay
559
+
560
+ ### Business Logic
561
+
562
+ 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.
563
+
564
+ #### Example
565
+
566
+ \`\`\`ts
567
+ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
568
+
569
+ const linkedin = connection("<connectionId>");
570
+
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
+ // Get campaigns
584
+ const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
585
+ const data = await res.json();
586
+ \`\`\``,
587
+ ja: `### \u30C4\u30FC\u30EB
588
+
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\u306F\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u5FC5\u9808\u30D8\u30C3\u30C0\u30FC\uFF08Authorization\u3001LinkedIn-Version\u3001X-Restli-Protocol-Version\uFF09\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
590
+ - \`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
+
592
+ ### LinkedIn Marketing API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
593
+
594
+ \u30D9\u30FC\u30B9URL: https://api.linkedin.com/rest/
595
+ API\u30D0\u30FC\u30B8\u30E7\u30F3: 202603\uFF08LinkedIn-Version\u30D8\u30C3\u30C0\u30FC\u3067\u81EA\u52D5\u8A2D\u5B9A\uFF09
596
+
597
+ #### \u30A2\u30AB\u30A6\u30F3\u30C8\u968E\u5C64
598
+ \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8 (sponsoredAccount) > \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7 (sponsoredCampaignGroup) > \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3 (adCampaign) > \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6
599
+
600
+ #### \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u691C\u7D22
601
+ - GET adAccounts?q=search&search=(status:(values:List(ACTIVE)))
602
+
603
+ #### \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u691C\u7D22
604
+ - GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100
605
+ - \u691C\u7D22\u30D1\u30E9\u30E1\u30FC\u30BF: search.status.values, search.type.values, search.name.values, search.campaignGroup.values
606
+
607
+ #### \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7\u691C\u7D22
608
+ - GET adAccounts/{adAccountId}/adCampaignGroups?q=search&search=(status:(values:List(ACTIVE)))
609
+
610
+ #### \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6\u691C\u7D22
611
+ - GET adAccounts/{adAccountId}/adCreatives?q=search&search=(status:(values:List(ACTIVE)))
612
+
613
+ #### \u5E83\u544A\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u53D6\u5F97\uFF08Analytics Finder - \u5358\u4E00\u30D4\u30DC\u30C3\u30C8\uFF09
614
+ - GET adAnalytics?q=analytics&pivot=CAMPAIGN&timeGranularity=DAILY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues
615
+
616
+ #### \u5E83\u544A\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u53D6\u5F97\uFF08Statistics Finder - \u6700\u59273\u30D4\u30DC\u30C3\u30C8\uFF09
617
+ - GET adAnalytics?q=statistics&pivots=List(CAMPAIGN,CREATIVE)&timeGranularity=DAILY&dateRange=(start:(year:2025,month:1,day:1))&accounts=List(urn%3Ali%3AsponsoredAccount%3A{adAccountId})&fields=impressions,clicks,costInLocalCurrency,dateRange,pivotValues
618
+
619
+ ### \u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u306E\u30D4\u30DC\u30C3\u30C8
620
+ ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, COMPANY, CONVERSION,
621
+ MEMBER_COMPANY, MEMBER_COMPANY_SIZE, MEMBER_COUNTRY_V2, MEMBER_REGION_V2,
622
+ MEMBER_JOB_FUNCTION, MEMBER_JOB_TITLE, MEMBER_INDUSTRY, MEMBER_SENIORITY
623
+
624
+ ### \u6642\u9593\u7C92\u5EA6
625
+ DAILY, MONTHLY, YEARLY, ALL
626
+
627
+ ### \u4E3B\u8981\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u30E1\u30C8\u30EA\u30AF\u30B9\uFF08fields\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u6307\u5B9A\uFF09
628
+ impressions, clicks, costInLocalCurrency, costInUsd, landingPageClicks,
629
+ totalEngagements, likes, comments, shares, follows, reactions,
630
+ externalWebsiteConversions, conversionValueInLocalCurrency,
631
+ oneClickLeads, oneClickLeadFormOpens, videoViews, videoCompletions,
632
+ videoStarts, sends, opens, approximateMemberReach, dateRange, pivotValues
633
+
634
+ ### \u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u306E\u30D5\u30A1\u30BB\u30C3\u30C8\uFF08\u30D5\u30A3\u30EB\u30BF\uFF09
635
+ - \`accounts\`: \u30A2\u30AB\u30A6\u30F3\u30C8URN\u306E\u30EA\u30B9\u30C8 \u2014 accounts=List(urn%3Ali%3AsponsoredAccount%3A123)
636
+ - \`campaigns\`: \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3URN\u306E\u30EA\u30B9\u30C8 \u2014 campaigns=List(urn%3Ali%3AsponsoredCampaign%3A456)
637
+ - \`campaignGroups\`: \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7URN\u306E\u30EA\u30B9\u30C8
638
+ - \`creatives\`: \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6URN\u306E\u30EA\u30B9\u30C8
639
+
640
+ ### \u65E5\u4ED8\u7BC4\u56F2\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8
641
+ dateRange=(start:(year:2025,month:1,day:1),end:(year:2025,month:12,day:31))
642
+ \u7D42\u4E86\u65E5\u306F\u30AA\u30D7\u30B7\u30E7\u30F3\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u4ECA\u65E5\uFF09\u3002
643
+
644
+ ### URN\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8
645
+ - \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8: urn:li:sponsoredAccount:{id}
646
+ - \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7: urn:li:sponsoredCampaignGroup:{id}
647
+ - \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3: urn:li:sponsoredCampaign:{id}
648
+ - \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6: urn:li:sponsoredCreative:{id}
649
+
650
+ ### \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3
651
+ \u691C\u7D22API\u306F\u30AB\u30FC\u30BD\u30EB\u30D9\u30FC\u30B9\u306E\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\uFF08pageSize\u6700\u59271000\u3001pageToken\uFF09\u3002
652
+ \u30EC\u30B9\u30DD\u30F3\u30B9\u306Emetadata.nextPageToken\u3067\u6B21\u30DA\u30FC\u30B8\u3092\u53D6\u5F97\u3002
653
+
654
+ ### \u90E8\u5206\u66F4\u65B0
655
+ POST\u3067X-RestLi-Method: PARTIAL_UPDATE\u30D8\u30C3\u30C0\u30FC\u3092\u4F7F\u7528\uFF08body\u306B"patch"\u304C\u542B\u307E\u308C\u308B\u5834\u5408\u81EA\u52D5\u8A2D\u5B9A\uFF09\u3002
656
+ \u30DC\u30C7\u30A3\u5F62\u5F0F: { "patch": { "$set": { "field": "value" } } }
657
+
658
+ ### \u30D2\u30F3\u30C8
659
+ - \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u5185\u306EURN\u306F\u5FC5\u305AURL\u30A8\u30F3\u30B3\u30FC\u30C9\uFF08: \u2192 %3A\uFF09
660
+ - adAnalytics\u3067\u306Ffields\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u53D6\u5F97\u3059\u308B\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u6307\u5B9A\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8: impressions\u3068clicks\u306E\u307F\uFF09
661
+ - \u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u30B3\u30FC\u30EB\u3054\u3068\u306B\u6700\u592720\u30E1\u30C8\u30EA\u30AF\u30B9\u3092\u30EA\u30AF\u30A8\u30B9\u30C8\u53EF\u80FD
662
+ - adAnalytics\u306F\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u975E\u5BFE\u5FDC \u2014 \u30EC\u30B9\u30DD\u30F3\u30B9\u306F15,000\u8981\u7D20\u307E\u3067
663
+ - \u30D7\u30ED\u30D5\u30A7\u30C3\u30B7\u30E7\u30CA\u30EB\u30C7\u30E2\u30B0\u30E9\u30D5\u30A3\u30C3\u30AF\u30D4\u30DC\u30C3\u30C8\uFF08MEMBER_*\uFF09\u306F12-24\u6642\u9593\u306E\u9045\u5EF6\u3042\u308A
664
+
665
+ ### Business Logic
666
+
667
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
668
+
669
+ #### Example
670
+
671
+ \`\`\`ts
672
+ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads";
673
+
674
+ const linkedin = connection("<connectionId>");
675
+
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
+ // \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u3092\u53D6\u5F97
689
+ const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
690
+ const data = await res.json();
691
+ \`\`\``
692
+ },
693
+ tools,
694
+ async checkConnection(params) {
695
+ const accessToken = params[parameters.accessToken.slug];
696
+ if (!accessToken) {
697
+ return {
698
+ success: false,
699
+ error: "Access token is required"
700
+ };
701
+ }
702
+ try {
703
+ const url = "https://api.linkedin.com/rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=1";
704
+ const res = await fetch(url, {
705
+ method: "GET",
706
+ headers: {
707
+ Authorization: `Bearer ${accessToken}`,
708
+ "LinkedIn-Version": "202603",
709
+ "X-Restli-Protocol-Version": "2.0.0"
710
+ }
711
+ });
712
+ if (!res.ok) {
713
+ const data = await res.json().catch(() => ({}));
714
+ return {
715
+ success: false,
716
+ error: data?.message ?? `LinkedIn API failed: HTTP ${res.status}`
717
+ };
718
+ }
719
+ return { success: true };
720
+ } catch (error) {
721
+ return {
722
+ success: false,
723
+ error: error instanceof Error ? error.message : String(error)
724
+ };
725
+ }
726
+ }
727
+ });
728
+
729
+ // src/connectors/create-connector-sdk.ts
730
+ import { readFileSync } from "fs";
731
+ import path from "path";
732
+
733
+ // src/connector-client/env.ts
734
+ function resolveEnvVar(entry, key, connectionId) {
735
+ const envVarName = entry.envVars[key];
736
+ if (!envVarName) {
737
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
738
+ }
739
+ const value = process.env[envVarName];
740
+ if (!value) {
741
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
742
+ }
743
+ return value;
744
+ }
745
+ function resolveEnvVarOptional(entry, key) {
746
+ const envVarName = entry.envVars[key];
747
+ if (!envVarName) return void 0;
748
+ return process.env[envVarName] || void 0;
749
+ }
750
+
751
+ // src/connector-client/proxy-fetch.ts
752
+ import { getContext } from "hono/context-storage";
753
+ import { getCookie } from "hono/cookie";
754
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
755
+ function createSandboxProxyFetch(connectionId) {
756
+ return async (input, init) => {
757
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
758
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
759
+ if (!token || !sandboxId) {
760
+ throw new Error(
761
+ "Connection proxy is not configured. Please check your deployment settings."
762
+ );
763
+ }
764
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
765
+ const originalMethod = init?.method ?? "GET";
766
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
767
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
768
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
769
+ return fetch(proxyUrl, {
770
+ method: "POST",
771
+ headers: {
772
+ "Content-Type": "application/json",
773
+ Authorization: `Bearer ${token}`
774
+ },
775
+ body: JSON.stringify({
776
+ url: originalUrl,
777
+ method: originalMethod,
778
+ body: originalBody
779
+ })
780
+ });
781
+ };
782
+ }
783
+ function createDeployedAppProxyFetch(connectionId) {
784
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
785
+ if (!projectId) {
786
+ throw new Error(
787
+ "Connection proxy is not configured. Please check your deployment settings."
788
+ );
789
+ }
790
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
791
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
792
+ return async (input, init) => {
793
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
794
+ const originalMethod = init?.method ?? "GET";
795
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
796
+ const c = getContext();
797
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
798
+ if (!appSession) {
799
+ throw new Error(
800
+ "No authentication method available for connection proxy."
801
+ );
802
+ }
803
+ return fetch(proxyUrl, {
804
+ method: "POST",
805
+ headers: {
806
+ "Content-Type": "application/json",
807
+ Authorization: `Bearer ${appSession}`
808
+ },
809
+ body: JSON.stringify({
810
+ url: originalUrl,
811
+ method: originalMethod,
812
+ body: originalBody
813
+ })
814
+ });
815
+ };
816
+ }
817
+ function createProxyFetch(connectionId) {
818
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
819
+ return createSandboxProxyFetch(connectionId);
820
+ }
821
+ return createDeployedAppProxyFetch(connectionId);
822
+ }
823
+
824
+ // src/connectors/create-connector-sdk.ts
825
+ function loadConnectionsSync() {
826
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
827
+ try {
828
+ const raw = readFileSync(filePath, "utf-8");
829
+ return JSON.parse(raw);
830
+ } catch {
831
+ return {};
832
+ }
833
+ }
834
+ function createConnectorSdk(plugin, createClient2) {
835
+ return (connectionId) => {
836
+ const connections = loadConnectionsSync();
837
+ const entry = connections[connectionId];
838
+ if (!entry) {
839
+ throw new Error(
840
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
841
+ );
842
+ }
843
+ if (entry.connector.slug !== plugin.slug) {
844
+ throw new Error(
845
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
846
+ );
847
+ }
848
+ const params = {};
849
+ for (const param of Object.values(plugin.parameters)) {
850
+ if (param.required) {
851
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
852
+ } else {
853
+ const val = resolveEnvVarOptional(entry, param.slug);
854
+ if (val !== void 0) params[param.slug] = val;
855
+ }
856
+ }
857
+ return createClient2(params, createProxyFetch(connectionId));
858
+ };
859
+ }
860
+
861
+ // src/connectors/entries/linkedin-ads.ts
862
+ var connection = createConnectorSdk(linkedinAdsConnector, createClient);
863
+ export {
864
+ connection
865
+ };