@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,848 @@
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-oauth/sdk/index.ts
46
+ var BASE_URL = "https://api.linkedin.com/rest/";
47
+ function createClient(_params, fetchFn = fetch) {
48
+ function request(path2, init) {
49
+ const url = `${BASE_URL}${path2}`;
50
+ return fetchFn(url, init);
51
+ }
52
+ return { request };
53
+ }
54
+
55
+ // ../connectors/src/connector-onboarding.ts
56
+ var ConnectorOnboarding = class {
57
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
58
+ connectionSetupInstructions;
59
+ /** Phase 2: Data overview instructions */
60
+ dataOverviewInstructions;
61
+ constructor(config) {
62
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
63
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
64
+ }
65
+ getConnectionSetupPrompt(language) {
66
+ return this.connectionSetupInstructions?.[language] ?? null;
67
+ }
68
+ getDataOverviewInstructions(language) {
69
+ return this.dataOverviewInstructions[language];
70
+ }
71
+ };
72
+
73
+ // ../connectors/src/connector-tool.ts
74
+ var ConnectorTool = class {
75
+ name;
76
+ description;
77
+ inputSchema;
78
+ outputSchema;
79
+ _execute;
80
+ constructor(config) {
81
+ this.name = config.name;
82
+ this.description = config.description;
83
+ this.inputSchema = config.inputSchema;
84
+ this.outputSchema = config.outputSchema;
85
+ this._execute = config.execute;
86
+ }
87
+ createTool(connections, config) {
88
+ return {
89
+ description: this.description,
90
+ inputSchema: this.inputSchema,
91
+ outputSchema: this.outputSchema,
92
+ execute: (input) => this._execute(input, connections, config)
93
+ };
94
+ }
95
+ };
96
+
97
+ // ../connectors/src/connector-plugin.ts
98
+ var ConnectorPlugin = class _ConnectorPlugin {
99
+ slug;
100
+ authType;
101
+ name;
102
+ description;
103
+ iconUrl;
104
+ parameters;
105
+ releaseFlag;
106
+ proxyPolicy;
107
+ experimentalAttributes;
108
+ onboarding;
109
+ systemPrompt;
110
+ tools;
111
+ query;
112
+ checkConnection;
113
+ constructor(config) {
114
+ this.slug = config.slug;
115
+ this.authType = config.authType;
116
+ this.name = config.name;
117
+ this.description = config.description;
118
+ this.iconUrl = config.iconUrl;
119
+ this.parameters = config.parameters;
120
+ this.releaseFlag = config.releaseFlag;
121
+ this.proxyPolicy = config.proxyPolicy;
122
+ this.experimentalAttributes = config.experimentalAttributes;
123
+ this.onboarding = config.onboarding;
124
+ this.systemPrompt = config.systemPrompt;
125
+ this.tools = config.tools;
126
+ this.query = config.query;
127
+ this.checkConnection = config.checkConnection;
128
+ }
129
+ get connectorKey() {
130
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
131
+ }
132
+ /**
133
+ * Create tools for connections that belong to this connector.
134
+ * Filters connections by connectorKey internally.
135
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
136
+ */
137
+ createTools(connections, config) {
138
+ const myConnections = connections.filter(
139
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
140
+ );
141
+ const result = {};
142
+ for (const t of Object.values(this.tools)) {
143
+ result[`${this.connectorKey}_${t.name}`] = t.createTool(
144
+ myConnections,
145
+ config
146
+ );
147
+ }
148
+ return result;
149
+ }
150
+ static deriveKey(slug, authType) {
151
+ return authType ? `${slug}-${authType}` : slug;
152
+ }
153
+ };
154
+
155
+ // ../connectors/src/auth-types.ts
156
+ var AUTH_TYPES = {
157
+ OAUTH: "oauth",
158
+ API_KEY: "api-key",
159
+ JWT: "jwt",
160
+ SERVICE_ACCOUNT: "service-account",
161
+ PAT: "pat",
162
+ USER_PASSWORD: "user-password"
163
+ };
164
+
165
+ // ../connectors/src/connectors/linkedin-ads-oauth/tools/list-ad-accounts.ts
166
+ import { z } from "zod";
167
+ var BASE_URL2 = "https://api.linkedin.com/rest/";
168
+ var LINKEDIN_VERSION = "202603";
169
+ var REQUEST_TIMEOUT_MS = 6e4;
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
+ }
201
+ var inputSchema = z.object({
202
+ toolUseIntent: z.string().optional().describe(
203
+ "Brief description of what you intend to accomplish with this tool call"
204
+ ),
205
+ connectionId: z.string().describe("ID of the LinkedIn Ads OAuth connection to use")
206
+ });
207
+ var outputSchema = z.discriminatedUnion("success", [
208
+ z.object({
209
+ success: z.literal(true),
210
+ adAccounts: z.array(
211
+ z.object({
212
+ adAccountId: z.string(),
213
+ name: z.string()
214
+ })
215
+ )
216
+ }),
217
+ z.object({
218
+ success: z.literal(false),
219
+ error: z.string()
220
+ })
221
+ ]);
222
+ var listAdAccountsTool = new ConnectorTool({
223
+ name: "listAdAccounts",
224
+ description: "List LinkedIn ad accounts accessible with the current OAuth credentials.",
225
+ inputSchema,
226
+ outputSchema,
227
+ async execute({ connectionId }, connections, config) {
228
+ const connection2 = connections.find((c) => c.id === connectionId);
229
+ if (!connection2) {
230
+ return {
231
+ success: false,
232
+ error: `Connection ${connectionId} not found`
233
+ };
234
+ }
235
+ console.log(
236
+ `[connector-request] linkedin-ads-oauth/${connection2.name}: listAdAccounts`
237
+ );
238
+ try {
239
+ const token = await getProxyToken(config.oauthProxy);
240
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
241
+ const controller = new AbortController();
242
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
243
+ try {
244
+ const response = await fetch(proxyUrl, {
245
+ method: "POST",
246
+ headers: {
247
+ "Content-Type": "application/json",
248
+ Authorization: `Bearer ${token}`
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
+ }),
258
+ signal: controller.signal
259
+ });
260
+ const data = await response.json();
261
+ if (!response.ok) {
262
+ const errorMessage = data?.message ?? `HTTP ${response.status} ${response.statusText}`;
263
+ return { success: false, error: errorMessage };
264
+ }
265
+ const adAccounts = (data.elements ?? []).map((account) => ({
266
+ adAccountId: String(account.id ?? ""),
267
+ name: account.name ?? String(account.id ?? "")
268
+ }));
269
+ return { success: true, adAccounts };
270
+ } finally {
271
+ clearTimeout(timeout);
272
+ }
273
+ } catch (err) {
274
+ const msg = err instanceof Error ? err.message : String(err);
275
+ return { success: false, error: msg };
276
+ }
277
+ }
278
+ });
279
+
280
+ // ../connectors/src/connectors/linkedin-ads-oauth/setup.ts
281
+ var listAdAccountsToolName = `linkedin-ads-oauth_${listAdAccountsTool.name}`;
282
+ var linkedinAdsOauthOnboarding = new ConnectorOnboarding({
283
+ connectionSetupInstructions: {
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
285
+
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
288
+ 3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
289
+ - \`parameterSlug\`: \`"ad-account-id"\`
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
291
+ 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
292
+ 5. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
293
+ - \`adAccount\`: \u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u8868\u793A\u540D
294
+ - \`adAccountId\`: \u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8ID
295
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
296
+
297
+ #### \u5236\u7D04
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
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`,
300
+ en: `Follow these steps to set up the LinkedIn Ads (OAuth) connection.
301
+
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
304
+ 3. Call \`updateConnectionParameters\`:
305
+ - \`parameterSlug\`: \`"ad-account-id"\`
306
+ - \`options\`: The ad account list. Each option's \`label\` should be \`Account Name (id: accountId)\`, \`value\` should be the account ID
307
+ 4. The \`label\` of the user's selected account will arrive as a message. Proceed to the next step
308
+ 5. Call \`updateConnectionContext\`:
309
+ - \`adAccount\`: The selected account's display name
310
+ - \`adAccountId\`: The selected account ID
311
+ - \`note\`: Brief description of the setup
312
+
313
+ #### Constraints
314
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
315
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
316
+ },
317
+ dataOverviewInstructions: {
318
+ en: `1. Call linkedin-ads-oauth_request with GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=10 to explore campaigns
319
+ 2. Call linkedin-ads-oauth_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
320
+ 3. Explore campaign groups and creatives as needed to understand the data structure`,
321
+ ja: `1. linkedin-ads-oauth_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
322
+ 2. linkedin-ads-oauth_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
323
+ 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`
324
+ }
325
+ });
326
+
327
+ // ../connectors/src/connectors/linkedin-ads-oauth/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_OAUTH_AD_ACCOUNT_ID",
334
+ type: "text",
335
+ secret: false,
336
+ required: false
337
+ })
338
+ };
339
+
340
+ // ../connectors/src/connectors/linkedin-ads-oauth/tools/request.ts
341
+ import { z as z2 } from "zod";
342
+ var BASE_URL3 = "https://api.linkedin.com/rest/";
343
+ var LINKEDIN_VERSION2 = "202603";
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
+ }
376
+ var inputSchema2 = z2.object({
377
+ toolUseIntent: z2.string().optional().describe(
378
+ "Brief description of what you intend to accomplish with this tool call"
379
+ ),
380
+ connectionId: z2.string().describe("ID of the LinkedIn Ads OAuth connection to use"),
381
+ method: z2.enum(["GET", "POST", "DELETE"]).describe("HTTP method"),
382
+ path: z2.string().describe(
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."
384
+ ),
385
+ queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
386
+ body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON). For partial updates, use the patch format: { patch: { $set: { ... } } }")
387
+ });
388
+ var outputSchema2 = z2.discriminatedUnion("success", [
389
+ z2.object({
390
+ success: z2.literal(true),
391
+ status: z2.number(),
392
+ data: z2.unknown()
393
+ }),
394
+ z2.object({
395
+ success: z2.literal(false),
396
+ error: z2.string()
397
+ })
398
+ ]);
399
+ var requestTool = new ConnectorTool({
400
+ name: "request",
401
+ description: `Send authenticated requests to the LinkedIn Marketing API (REST).
402
+ Authentication is handled automatically via OAuth proxy.
403
+ {adAccountId} in the path is automatically replaced with the connection's ad account ID.
404
+ Required headers (LinkedIn-Version, X-Restli-Protocol-Version) are set automatically.`,
405
+ inputSchema: inputSchema2,
406
+ outputSchema: outputSchema2,
407
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
408
+ const connection2 = connections.find((c) => c.id === connectionId);
409
+ if (!connection2) {
410
+ return {
411
+ success: false,
412
+ error: `Connection ${connectionId} not found`
413
+ };
414
+ }
415
+ console.log(
416
+ `[connector-request] linkedin-ads-oauth/${connection2.name}: ${method} ${path2}`
417
+ );
418
+ try {
419
+ const adAccountId = parameters.adAccountId.tryGetValue(connection2) ?? "";
420
+ const resolvedPath = adAccountId ? path2.replace(/\{adAccountId\}/g, adAccountId) : path2;
421
+ let url = `${BASE_URL3}${resolvedPath}`;
422
+ if (queryParams && Object.keys(queryParams).length > 0) {
423
+ const params = new URLSearchParams(queryParams);
424
+ const separator = url.includes("?") ? "&" : "?";
425
+ url = `${url}${separator}${params.toString()}`;
426
+ }
427
+ const token = await getProxyToken2(config.oauthProxy);
428
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
429
+ const controller = new AbortController();
430
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
431
+ try {
432
+ const additionalHeaders = {
433
+ "LinkedIn-Version": LINKEDIN_VERSION2,
434
+ "X-Restli-Protocol-Version": "2.0.0"
435
+ };
436
+ if (body && "patch" in body) {
437
+ additionalHeaders["X-RestLi-Method"] = "PARTIAL_UPDATE";
438
+ }
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
+ }),
451
+ signal: controller.signal
452
+ });
453
+ if (response.status === 204) {
454
+ return { success: true, status: 204, data: {} };
455
+ }
456
+ const data = await response.json();
457
+ if (!response.ok) {
458
+ const dataObj = data;
459
+ const errorMessage = typeof dataObj?.message === "string" ? dataObj.message : typeof dataObj?.error === "string" ? dataObj.error : `HTTP ${response.status} ${response.statusText}`;
460
+ return { success: false, error: errorMessage };
461
+ }
462
+ return { success: true, status: response.status, data };
463
+ } finally {
464
+ clearTimeout(timeout);
465
+ }
466
+ } catch (err) {
467
+ const msg = err instanceof Error ? err.message : String(err);
468
+ return { success: false, error: msg };
469
+ }
470
+ }
471
+ });
472
+
473
+ // ../connectors/src/connectors/linkedin-ads-oauth/index.ts
474
+ var tools = {
475
+ request: requestTool,
476
+ listAdAccounts: listAdAccountsTool
477
+ };
478
+ var linkedinAdsOauthConnector = new ConnectorPlugin({
479
+ slug: "linkedin-ads",
480
+ authType: AUTH_TYPES.OAUTH,
481
+ name: "LinkedIn Ads",
482
+ description: "Connect to LinkedIn Ads (Marketing API) for advertising campaign data and reporting using OAuth.",
483
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/3x7xd9pVJkRFY7ADIg4ycq/b94720e34cb081e9ae45dfde799a59cd/LinkedIn_icon.svg.png",
484
+ parameters,
485
+ releaseFlag: { dev1: true, dev2: false, prod: false },
486
+ onboarding: linkedinAdsOauthOnboarding,
487
+ proxyPolicy: {
488
+ allowlist: [
489
+ {
490
+ host: "api.linkedin.com",
491
+ methods: ["GET", "POST", "DELETE"]
492
+ }
493
+ ]
494
+ },
495
+ systemPrompt: {
496
+ en: `### Tools
497
+
498
+ - \`linkedin-ads-oauth_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.
499
+ - \`linkedin-ads-oauth_listAdAccounts\`: List accessible LinkedIn ad accounts. Use this during setup to discover available accounts.
500
+
501
+ ### LinkedIn Marketing API Reference
502
+
503
+ Base URL: https://api.linkedin.com/rest/
504
+ API Version: 202603 (set automatically via LinkedIn-Version header)
505
+
506
+ #### Account Hierarchy
507
+ Ad Account (sponsoredAccount) > Campaign Group (sponsoredCampaignGroup) > Campaign (adCampaign) > Creative
508
+
509
+ #### Search Ad Accounts
510
+ - GET adAccounts?q=search&search=(status:(values:List(ACTIVE)))
511
+
512
+ #### Search Campaigns
513
+ - GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100
514
+ - Search params: search.status.values, search.type.values, search.name.values, search.campaignGroup.values
515
+
516
+ #### Search Campaign Groups
517
+ - GET adAccounts/{adAccountId}/adCampaignGroups?q=search&search=(status:(values:List(ACTIVE)))
518
+
519
+ #### Search Creatives
520
+ - GET adAccounts/{adAccountId}/adCreatives?q=search&search=(status:(values:List(ACTIVE)))
521
+
522
+ #### Get Ad Analytics (Analytics Finder - single pivot)
523
+ - 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
524
+
525
+ #### Get Ad Analytics (Statistics Finder - up to 3 pivots)
526
+ - 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
527
+
528
+ ### Analytics Pivots
529
+ ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, COMPANY, CONVERSION,
530
+ MEMBER_COMPANY, MEMBER_COMPANY_SIZE, MEMBER_COUNTRY_V2, MEMBER_REGION_V2,
531
+ MEMBER_JOB_FUNCTION, MEMBER_JOB_TITLE, MEMBER_INDUSTRY, MEMBER_SENIORITY
532
+
533
+ ### Analytics Time Granularity
534
+ DAILY, MONTHLY, YEARLY, ALL
535
+
536
+ ### Key Analytics Metrics (specify via fields parameter)
537
+ impressions, clicks, costInLocalCurrency, costInUsd, landingPageClicks,
538
+ totalEngagements, likes, comments, shares, follows, reactions,
539
+ externalWebsiteConversions, conversionValueInLocalCurrency,
540
+ oneClickLeads, oneClickLeadFormOpens, videoViews, videoCompletions,
541
+ videoStarts, sends, opens, approximateMemberReach, dateRange, pivotValues
542
+
543
+ ### Analytics Facets (filters)
544
+ - \`accounts\`: List of account URNs \u2014 accounts=List(urn%3Ali%3AsponsoredAccount%3A123)
545
+ - \`campaigns\`: List of campaign URNs \u2014 campaigns=List(urn%3Ali%3AsponsoredCampaign%3A456)
546
+ - \`campaignGroups\`: List of campaign group URNs
547
+ - \`creatives\`: List of creative URNs
548
+
549
+ ### Date Range Format
550
+ dateRange=(start:(year:2025,month:1,day:1),end:(year:2025,month:12,day:31))
551
+ End date is optional (defaults to today).
552
+
553
+ ### URN Formats
554
+ - Ad Account: urn:li:sponsoredAccount:{id}
555
+ - Campaign Group: urn:li:sponsoredCampaignGroup:{id}
556
+ - Campaign: urn:li:sponsoredCampaign:{id}
557
+ - Creative: urn:li:sponsoredCreative:{id}
558
+
559
+ ### Pagination
560
+ Search APIs use cursor-based pagination with pageSize (max 1000) and pageToken.
561
+ Response includes metadata.nextPageToken for fetching next page.
562
+
563
+ ### Partial Updates
564
+ Use POST with X-RestLi-Method: PARTIAL_UPDATE header (set automatically when body contains "patch").
565
+ Body format: { "patch": { "$set": { "field": "value" } } }
566
+
567
+ ### Tips
568
+ - Always URL-encode URNs in query parameters (: \u2192 %3A)
569
+ - Use fields parameter in adAnalytics to request specific metrics (default: only impressions and clicks)
570
+ - Request up to 20 metrics per analytics call
571
+ - adAnalytics does not support pagination \u2014 response is limited to 15,000 elements
572
+ - Professional demographic pivots (MEMBER_*) may have 12-24h delay
573
+
574
+ ### Business Logic
575
+
576
+ 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.
577
+
578
+ #### Example
579
+
580
+ \`\`\`ts
581
+ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads-oauth";
582
+
583
+ const linkedin = connection("<connectionId>");
584
+
585
+ // Get campaigns
586
+ const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
587
+ const data = await res.json();
588
+ \`\`\``,
589
+ ja: `### \u30C4\u30FC\u30EB
590
+
591
+ - \`linkedin-ads-oauth_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
592
+ - \`linkedin-ads-oauth_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
593
+
594
+ ### LinkedIn Marketing API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
595
+
596
+ \u30D9\u30FC\u30B9URL: https://api.linkedin.com/rest/
597
+ API\u30D0\u30FC\u30B8\u30E7\u30F3: 202603\uFF08LinkedIn-Version\u30D8\u30C3\u30C0\u30FC\u3067\u81EA\u52D5\u8A2D\u5B9A\uFF09
598
+
599
+ #### \u30A2\u30AB\u30A6\u30F3\u30C8\u968E\u5C64
600
+ \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
601
+
602
+ #### \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8\u691C\u7D22
603
+ - GET adAccounts?q=search&search=(status:(values:List(ACTIVE)))
604
+
605
+ #### \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u691C\u7D22
606
+ - GET adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))&pageSize=100
607
+ - \u691C\u7D22\u30D1\u30E9\u30E1\u30FC\u30BF: search.status.values, search.type.values, search.name.values, search.campaignGroup.values
608
+
609
+ #### \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7\u691C\u7D22
610
+ - GET adAccounts/{adAccountId}/adCampaignGroups?q=search&search=(status:(values:List(ACTIVE)))
611
+
612
+ #### \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6\u691C\u7D22
613
+ - GET adAccounts/{adAccountId}/adCreatives?q=search&search=(status:(values:List(ACTIVE)))
614
+
615
+ #### \u5E83\u544A\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u53D6\u5F97\uFF08Analytics Finder - \u5358\u4E00\u30D4\u30DC\u30C3\u30C8\uFF09
616
+ - 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
617
+
618
+ #### \u5E83\u544A\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u53D6\u5F97\uFF08Statistics Finder - \u6700\u59273\u30D4\u30DC\u30C3\u30C8\uFF09
619
+ - 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
620
+
621
+ ### \u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u306E\u30D4\u30DC\u30C3\u30C8
622
+ ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, COMPANY, CONVERSION,
623
+ MEMBER_COMPANY, MEMBER_COMPANY_SIZE, MEMBER_COUNTRY_V2, MEMBER_REGION_V2,
624
+ MEMBER_JOB_FUNCTION, MEMBER_JOB_TITLE, MEMBER_INDUSTRY, MEMBER_SENIORITY
625
+
626
+ ### \u6642\u9593\u7C92\u5EA6
627
+ DAILY, MONTHLY, YEARLY, ALL
628
+
629
+ ### \u4E3B\u8981\u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u30E1\u30C8\u30EA\u30AF\u30B9\uFF08fields\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u6307\u5B9A\uFF09
630
+ impressions, clicks, costInLocalCurrency, costInUsd, landingPageClicks,
631
+ totalEngagements, likes, comments, shares, follows, reactions,
632
+ externalWebsiteConversions, conversionValueInLocalCurrency,
633
+ oneClickLeads, oneClickLeadFormOpens, videoViews, videoCompletions,
634
+ videoStarts, sends, opens, approximateMemberReach, dateRange, pivotValues
635
+
636
+ ### \u30A2\u30CA\u30EA\u30C6\u30A3\u30AF\u30B9\u306E\u30D5\u30A1\u30BB\u30C3\u30C8\uFF08\u30D5\u30A3\u30EB\u30BF\uFF09
637
+ - \`accounts\`: \u30A2\u30AB\u30A6\u30F3\u30C8URN\u306E\u30EA\u30B9\u30C8 \u2014 accounts=List(urn%3Ali%3AsponsoredAccount%3A123)
638
+ - \`campaigns\`: \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3URN\u306E\u30EA\u30B9\u30C8 \u2014 campaigns=List(urn%3Ali%3AsponsoredCampaign%3A456)
639
+ - \`campaignGroups\`: \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7URN\u306E\u30EA\u30B9\u30C8
640
+ - \`creatives\`: \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6URN\u306E\u30EA\u30B9\u30C8
641
+
642
+ ### \u65E5\u4ED8\u7BC4\u56F2\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8
643
+ dateRange=(start:(year:2025,month:1,day:1),end:(year:2025,month:12,day:31))
644
+ \u7D42\u4E86\u65E5\u306F\u30AA\u30D7\u30B7\u30E7\u30F3\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u4ECA\u65E5\uFF09\u3002
645
+
646
+ ### URN\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8
647
+ - \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8: urn:li:sponsoredAccount:{id}
648
+ - \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30B0\u30EB\u30FC\u30D7: urn:li:sponsoredCampaignGroup:{id}
649
+ - \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3: urn:li:sponsoredCampaign:{id}
650
+ - \u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30D6: urn:li:sponsoredCreative:{id}
651
+
652
+ ### \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3
653
+ \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
654
+ \u30EC\u30B9\u30DD\u30F3\u30B9\u306Emetadata.nextPageToken\u3067\u6B21\u30DA\u30FC\u30B8\u3092\u53D6\u5F97\u3002
655
+
656
+ ### \u90E8\u5206\u66F4\u65B0
657
+ 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
658
+ \u30DC\u30C7\u30A3\u5F62\u5F0F: { "patch": { "$set": { "field": "value" } } }
659
+
660
+ ### \u30D2\u30F3\u30C8
661
+ - \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u5185\u306EURN\u306F\u5FC5\u305AURL\u30A8\u30F3\u30B3\u30FC\u30C9\uFF08: \u2192 %3A\uFF09
662
+ - 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
663
+ - \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
664
+ - 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
665
+ - \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
666
+
667
+ ### Business Logic
668
+
669
+ \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
670
+
671
+ #### Example
672
+
673
+ \`\`\`ts
674
+ import { connection } from "@squadbase/vite-server/connectors/linkedin-ads-oauth";
675
+
676
+ const linkedin = connection("<connectionId>");
677
+
678
+ // \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u3092\u53D6\u5F97
679
+ const res = await linkedin.request("adAccounts/{adAccountId}/adCampaigns?q=search&search=(status:(values:List(ACTIVE)))");
680
+ const data = await res.json();
681
+ \`\`\``
682
+ },
683
+ tools,
684
+ async checkConnection(_params, config) {
685
+ const { proxyFetch } = config;
686
+ try {
687
+ const url = "https://api.linkedin.com/rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))&pageSize=1";
688
+ const res = await proxyFetch(url, {
689
+ method: "GET",
690
+ headers: {
691
+ "LinkedIn-Version": "202603",
692
+ "X-Restli-Protocol-Version": "2.0.0"
693
+ }
694
+ });
695
+ if (!res.ok) {
696
+ const data = await res.json().catch(() => ({}));
697
+ return {
698
+ success: false,
699
+ error: data?.message ?? `LinkedIn API failed: HTTP ${res.status}`
700
+ };
701
+ }
702
+ return { success: true };
703
+ } catch (error) {
704
+ return {
705
+ success: false,
706
+ error: error instanceof Error ? error.message : String(error)
707
+ };
708
+ }
709
+ }
710
+ });
711
+
712
+ // src/connectors/create-connector-sdk.ts
713
+ import { readFileSync } from "fs";
714
+ import path from "path";
715
+
716
+ // src/connector-client/env.ts
717
+ function resolveEnvVar(entry, key, connectionId) {
718
+ const envVarName = entry.envVars[key];
719
+ if (!envVarName) {
720
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
721
+ }
722
+ const value = process.env[envVarName];
723
+ if (!value) {
724
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
725
+ }
726
+ return value;
727
+ }
728
+ function resolveEnvVarOptional(entry, key) {
729
+ const envVarName = entry.envVars[key];
730
+ if (!envVarName) return void 0;
731
+ return process.env[envVarName] || void 0;
732
+ }
733
+
734
+ // src/connector-client/proxy-fetch.ts
735
+ import { getContext } from "hono/context-storage";
736
+ import { getCookie } from "hono/cookie";
737
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
738
+ function createSandboxProxyFetch(connectionId) {
739
+ return async (input, init) => {
740
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
741
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
742
+ if (!token || !sandboxId) {
743
+ throw new Error(
744
+ "Connection proxy is not configured. Please check your deployment settings."
745
+ );
746
+ }
747
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
748
+ const originalMethod = init?.method ?? "GET";
749
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
750
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
751
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
752
+ return fetch(proxyUrl, {
753
+ method: "POST",
754
+ headers: {
755
+ "Content-Type": "application/json",
756
+ Authorization: `Bearer ${token}`
757
+ },
758
+ body: JSON.stringify({
759
+ url: originalUrl,
760
+ method: originalMethod,
761
+ body: originalBody
762
+ })
763
+ });
764
+ };
765
+ }
766
+ function createDeployedAppProxyFetch(connectionId) {
767
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
768
+ if (!projectId) {
769
+ throw new Error(
770
+ "Connection proxy is not configured. Please check your deployment settings."
771
+ );
772
+ }
773
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
774
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
775
+ return async (input, init) => {
776
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
777
+ const originalMethod = init?.method ?? "GET";
778
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
779
+ const c = getContext();
780
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
781
+ if (!appSession) {
782
+ throw new Error(
783
+ "No authentication method available for connection proxy."
784
+ );
785
+ }
786
+ return fetch(proxyUrl, {
787
+ method: "POST",
788
+ headers: {
789
+ "Content-Type": "application/json",
790
+ Authorization: `Bearer ${appSession}`
791
+ },
792
+ body: JSON.stringify({
793
+ url: originalUrl,
794
+ method: originalMethod,
795
+ body: originalBody
796
+ })
797
+ });
798
+ };
799
+ }
800
+ function createProxyFetch(connectionId) {
801
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
802
+ return createSandboxProxyFetch(connectionId);
803
+ }
804
+ return createDeployedAppProxyFetch(connectionId);
805
+ }
806
+
807
+ // src/connectors/create-connector-sdk.ts
808
+ function loadConnectionsSync() {
809
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
810
+ try {
811
+ const raw = readFileSync(filePath, "utf-8");
812
+ return JSON.parse(raw);
813
+ } catch {
814
+ return {};
815
+ }
816
+ }
817
+ function createConnectorSdk(plugin, createClient2) {
818
+ return (connectionId) => {
819
+ const connections = loadConnectionsSync();
820
+ const entry = connections[connectionId];
821
+ if (!entry) {
822
+ throw new Error(
823
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
824
+ );
825
+ }
826
+ if (entry.connector.slug !== plugin.slug) {
827
+ throw new Error(
828
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
829
+ );
830
+ }
831
+ const params = {};
832
+ for (const param of Object.values(plugin.parameters)) {
833
+ if (param.required) {
834
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
835
+ } else {
836
+ const val = resolveEnvVarOptional(entry, param.slug);
837
+ if (val !== void 0) params[param.slug] = val;
838
+ }
839
+ }
840
+ return createClient2(params, createProxyFetch(connectionId));
841
+ };
842
+ }
843
+
844
+ // src/connectors/entries/linkedin-ads-oauth.ts
845
+ var connection = createConnectorSdk(linkedinAdsOauthConnector, createClient);
846
+ export {
847
+ connection
848
+ };