@squadbase/vite-server 0.1.5-dev.0 → 0.1.5-dev.1

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.
@@ -0,0 +1,795 @@
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/meta-ads-oauth/sdk/index.ts
46
+ var BASE_URL = "https://graph.facebook.com/v21.0/";
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, opts) {
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
+ 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
+ };
158
+ }
159
+ return result;
160
+ }
161
+ static deriveKey(slug, authType) {
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;
189
+ }
190
+ };
191
+
192
+ // ../connectors/src/auth-types.ts
193
+ var AUTH_TYPES = {
194
+ OAUTH: "oauth",
195
+ API_KEY: "api-key",
196
+ JWT: "jwt",
197
+ SERVICE_ACCOUNT: "service-account",
198
+ PAT: "pat",
199
+ USER_PASSWORD: "user-password"
200
+ };
201
+
202
+ // ../connectors/src/connectors/meta-ads-oauth/tools/list-ad-accounts.ts
203
+ import { z } from "zod";
204
+ var BASE_URL2 = "https://graph.facebook.com/v21.0/";
205
+ var REQUEST_TIMEOUT_MS = 6e4;
206
+ var cachedToken = null;
207
+ async function getProxyToken(config) {
208
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
209
+ return cachedToken.token;
210
+ }
211
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
212
+ const res = await fetch(url, {
213
+ method: "POST",
214
+ headers: {
215
+ "Content-Type": "application/json",
216
+ "x-api-key": config.appApiKey,
217
+ "project-id": config.projectId
218
+ },
219
+ body: JSON.stringify({
220
+ sandboxId: config.sandboxId,
221
+ issuedBy: "coding-agent"
222
+ })
223
+ });
224
+ if (!res.ok) {
225
+ const errorText = await res.text().catch(() => res.statusText);
226
+ throw new Error(
227
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
228
+ );
229
+ }
230
+ const data = await res.json();
231
+ cachedToken = {
232
+ token: data.token,
233
+ expiresAt: new Date(data.expiresAt).getTime()
234
+ };
235
+ return data.token;
236
+ }
237
+ var inputSchema = z.object({
238
+ toolUseIntent: z.string().optional().describe(
239
+ "Brief description of what you intend to accomplish with this tool call"
240
+ ),
241
+ connectionId: z.string().describe("ID of the Meta Ads OAuth connection to use")
242
+ });
243
+ var outputSchema = z.discriminatedUnion("success", [
244
+ z.object({
245
+ success: z.literal(true),
246
+ adAccounts: z.array(
247
+ z.object({
248
+ adAccountId: z.string(),
249
+ name: z.string()
250
+ })
251
+ )
252
+ }),
253
+ z.object({
254
+ success: z.literal(false),
255
+ error: z.string()
256
+ })
257
+ ]);
258
+ var listAdAccountsTool = new ConnectorTool({
259
+ name: "listAdAccounts",
260
+ description: "List Meta ad accounts accessible with the current OAuth credentials.",
261
+ inputSchema,
262
+ outputSchema,
263
+ async execute({ connectionId }, connections, config) {
264
+ const connection2 = connections.find((c) => c.id === connectionId);
265
+ if (!connection2) {
266
+ return {
267
+ success: false,
268
+ error: `Connection ${connectionId} not found`
269
+ };
270
+ }
271
+ console.log(
272
+ `[connector-request] meta-ads-oauth/${connection2.name}: listAdAccounts`
273
+ );
274
+ try {
275
+ const token = await getProxyToken(config.oauthProxy);
276
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
277
+ const controller = new AbortController();
278
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
279
+ try {
280
+ const response = await fetch(proxyUrl, {
281
+ method: "POST",
282
+ headers: {
283
+ "Content-Type": "application/json",
284
+ Authorization: `Bearer ${token}`
285
+ },
286
+ body: JSON.stringify({
287
+ url: `${BASE_URL2}me/adaccounts?fields=account_id,name,account_status`,
288
+ method: "GET"
289
+ }),
290
+ signal: controller.signal
291
+ });
292
+ const data = await response.json();
293
+ if (!response.ok) {
294
+ const errorMessage = data?.error?.message ?? `HTTP ${response.status} ${response.statusText}`;
295
+ return { success: false, error: errorMessage };
296
+ }
297
+ const adAccounts = (data.data ?? []).map((account) => ({
298
+ adAccountId: account.account_id ?? "",
299
+ name: account.name ?? account.account_id ?? ""
300
+ }));
301
+ return { success: true, adAccounts };
302
+ } finally {
303
+ clearTimeout(timeout);
304
+ }
305
+ } catch (err) {
306
+ const msg = err instanceof Error ? err.message : String(err);
307
+ return { success: false, error: msg };
308
+ }
309
+ }
310
+ });
311
+
312
+ // ../connectors/src/connectors/meta-ads-oauth/setup.ts
313
+ var listAdAccountsToolName = `meta-ads-oauth_${listAdAccountsTool.name}`;
314
+ var metaAdsOauthOnboarding = new ConnectorOnboarding({
315
+ connectionSetupInstructions: {
316
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Meta\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
317
+
318
+ 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
319
+ 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
320
+ 3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
321
+ - \`parameterSlug\`: \`"ad-account-id"\`
322
+ - \`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
323
+ 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
324
+
325
+ #### \u5236\u7D04
326
+ - **\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
327
+ - \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`,
328
+ en: `Follow these steps to set up the Meta Ads (OAuth) connection.
329
+
330
+ 1. Call \`${listAdAccountsToolName}\` to get the list of ad accounts accessible with the OAuth credentials
331
+ 2. If an error occurs, ask the user to verify their OAuth connection
332
+ 3. Call \`updateConnectionParameters\`:
333
+ - \`parameterSlug\`: \`"ad-account-id"\`
334
+ - \`options\`: The ad account list. Each option's \`label\` should be \`Account Name (id: accountId)\`, \`value\` should be the account ID
335
+ 4. The \`label\` of the user's selected account will arrive as a message. Proceed to the next step
336
+
337
+ #### Constraints
338
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
339
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
340
+ },
341
+ dataOverviewInstructions: {
342
+ en: `1. Call meta-ads-oauth_request with GET act_{adAccountId}/campaigns?fields=id,name,status,objective to explore campaign data
343
+ 2. Call meta-ads-oauth_request with GET act_{adAccountId}/insights?fields=impressions,clicks,spend,ctr,cpm&date_preset=last_7d to check recent performance
344
+ 3. Explore ad sets and ads as needed to understand the data structure`,
345
+ ja: `1. meta-ads-oauth_request \u3067 GET act_{adAccountId}/campaigns?fields=id,name,status,objective \u3092\u547C\u3073\u51FA\u3057\u3066\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u30C7\u30FC\u30BF\u3092\u63A2\u7D22
346
+ 2. meta-ads-oauth_request \u3067 GET act_{adAccountId}/insights?fields=impressions,clicks,spend,ctr,cpm&date_preset=last_7d \u3092\u547C\u3073\u51FA\u3057\u3066\u76F4\u8FD1\u306E\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u3092\u78BA\u8A8D
347
+ 3. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u5E83\u544A\u30BB\u30C3\u30C8\u3084\u5E83\u544A\u30C7\u30FC\u30BF\u3092\u63A2\u7D22\u3057\u3001\u30C7\u30FC\u30BF\u69CB\u9020\u3092\u628A\u63E1`
348
+ }
349
+ });
350
+
351
+ // ../connectors/src/connectors/meta-ads-oauth/parameters.ts
352
+ var parameters = {
353
+ adAccountId: new ParameterDefinition({
354
+ slug: "ad-account-id",
355
+ name: "Ad Account ID",
356
+ description: "The Meta ad account ID (numeric, without the 'act_' prefix). Found in Meta Business Suite under Ad Accounts.",
357
+ envVarBaseKey: "META_ADS_OAUTH_AD_ACCOUNT_ID",
358
+ type: "text",
359
+ secret: false,
360
+ required: false
361
+ })
362
+ };
363
+
364
+ // ../connectors/src/connectors/meta-ads-oauth/tools/request.ts
365
+ import { z as z2 } from "zod";
366
+ var BASE_URL3 = "https://graph.facebook.com/v21.0/";
367
+ var REQUEST_TIMEOUT_MS2 = 6e4;
368
+ var cachedToken2 = null;
369
+ async function getProxyToken2(config) {
370
+ if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
371
+ return cachedToken2.token;
372
+ }
373
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
374
+ const res = await fetch(url, {
375
+ method: "POST",
376
+ headers: {
377
+ "Content-Type": "application/json",
378
+ "x-api-key": config.appApiKey,
379
+ "project-id": config.projectId
380
+ },
381
+ body: JSON.stringify({
382
+ sandboxId: config.sandboxId,
383
+ issuedBy: "coding-agent"
384
+ })
385
+ });
386
+ if (!res.ok) {
387
+ const errorText = await res.text().catch(() => res.statusText);
388
+ throw new Error(
389
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
390
+ );
391
+ }
392
+ const data = await res.json();
393
+ cachedToken2 = {
394
+ token: data.token,
395
+ expiresAt: new Date(data.expiresAt).getTime()
396
+ };
397
+ return data.token;
398
+ }
399
+ var inputSchema2 = z2.object({
400
+ toolUseIntent: z2.string().optional().describe(
401
+ "Brief description of what you intend to accomplish with this tool call"
402
+ ),
403
+ connectionId: z2.string().describe("ID of the Meta Ads OAuth connection to use"),
404
+ method: z2.enum(["GET", "POST"]).describe("HTTP method"),
405
+ path: z2.string().describe(
406
+ "API path appended to https://graph.facebook.com/v21.0/ (e.g., 'act_{adAccountId}/campaigns'). {adAccountId} is automatically replaced."
407
+ ),
408
+ queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
409
+ body: z2.record(z2.string(), z2.unknown()).optional().describe("POST request body (JSON)")
410
+ });
411
+ var outputSchema2 = z2.discriminatedUnion("success", [
412
+ z2.object({
413
+ success: z2.literal(true),
414
+ status: z2.number(),
415
+ data: z2.unknown()
416
+ }),
417
+ z2.object({
418
+ success: z2.literal(false),
419
+ error: z2.string()
420
+ })
421
+ ]);
422
+ var requestTool = new ConnectorTool({
423
+ name: "request",
424
+ description: `Send authenticated requests to the Meta Marketing API v21.0.
425
+ Authentication is handled automatically via OAuth proxy.
426
+ {adAccountId} in the path is automatically replaced with the connection's ad account ID.`,
427
+ inputSchema: inputSchema2,
428
+ outputSchema: outputSchema2,
429
+ async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
430
+ const connection2 = connections.find((c) => c.id === connectionId);
431
+ if (!connection2) {
432
+ return {
433
+ success: false,
434
+ error: `Connection ${connectionId} not found`
435
+ };
436
+ }
437
+ console.log(
438
+ `[connector-request] meta-ads-oauth/${connection2.name}: ${method} ${path2}`
439
+ );
440
+ try {
441
+ const adAccountId = parameters.adAccountId.tryGetValue(connection2) ?? "";
442
+ const resolvedPath = adAccountId ? path2.replace(/\{adAccountId\}/g, adAccountId) : path2;
443
+ let url = `${BASE_URL3}${resolvedPath}`;
444
+ if (queryParams && Object.keys(queryParams).length > 0) {
445
+ const params = new URLSearchParams(queryParams);
446
+ const separator = url.includes("?") ? "&" : "?";
447
+ url = `${url}${separator}${params.toString()}`;
448
+ }
449
+ const token = await getProxyToken2(config.oauthProxy);
450
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
451
+ const controller = new AbortController();
452
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
453
+ try {
454
+ const response = await fetch(proxyUrl, {
455
+ method: "POST",
456
+ headers: {
457
+ "Content-Type": "application/json",
458
+ Authorization: `Bearer ${token}`
459
+ },
460
+ body: JSON.stringify({
461
+ url,
462
+ method,
463
+ ...method === "POST" && body ? { body: JSON.stringify(body) } : {}
464
+ }),
465
+ signal: controller.signal
466
+ });
467
+ const data = await response.json();
468
+ if (!response.ok) {
469
+ const dataObj = data;
470
+ const errorObj = dataObj?.error;
471
+ const errorMessage = typeof errorObj?.message === "string" ? errorObj.message : typeof dataObj?.error === "string" ? dataObj.error : `HTTP ${response.status} ${response.statusText}`;
472
+ return { success: false, error: errorMessage };
473
+ }
474
+ return { success: true, status: response.status, data };
475
+ } finally {
476
+ clearTimeout(timeout);
477
+ }
478
+ } catch (err) {
479
+ const msg = err instanceof Error ? err.message : String(err);
480
+ return { success: false, error: msg };
481
+ }
482
+ }
483
+ });
484
+
485
+ // ../connectors/src/connectors/meta-ads-oauth/index.ts
486
+ var tools = {
487
+ request: requestTool,
488
+ listAdAccounts: listAdAccountsTool
489
+ };
490
+ var metaAdsOauthConnector = new ConnectorPlugin({
491
+ slug: "meta-ads",
492
+ authType: AUTH_TYPES.OAUTH,
493
+ name: "Meta Ads",
494
+ description: "Connect to Meta (Facebook/Instagram) Ads for advertising campaign data and reporting using OAuth.",
495
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2vyrgcvdf3jETIFXHtbteO/de7f3288e831c9f738e44cf1f961c3bd/meta-icon.webp",
496
+ parameters,
497
+ releaseFlag: { dev1: true, dev2: false, prod: false },
498
+ onboarding: metaAdsOauthOnboarding,
499
+ proxyPolicy: {
500
+ allowlist: [
501
+ {
502
+ host: "graph.facebook.com",
503
+ methods: ["GET", "POST"]
504
+ }
505
+ ]
506
+ },
507
+ systemPrompt: {
508
+ en: `### Tools
509
+
510
+ - \`meta-ads-oauth_request\`: Send authenticated requests to the Meta Marketing API v21.0. The {adAccountId} placeholder in paths is automatically replaced. Authentication is handled automatically via OAuth proxy.
511
+ - \`meta-ads-oauth_listAdAccounts\`: List accessible Meta ad accounts. Use this during setup to discover available accounts.
512
+
513
+ ### Meta Marketing API Reference
514
+
515
+ #### List Campaigns
516
+ - GET act_{adAccountId}/campaigns?fields=id,name,status,objective,daily_budget,lifetime_budget
517
+
518
+ #### List Ad Sets
519
+ - GET act_{adAccountId}/adsets?fields=id,name,status,targeting,daily_budget,bid_amount
520
+
521
+ #### List Ads
522
+ - GET act_{adAccountId}/ads?fields=id,name,status,creative
523
+
524
+ #### Get Insights (Performance Data)
525
+ - GET act_{adAccountId}/insights?fields=impressions,clicks,spend,ctr,cpm,cpc,conversions,actions&date_preset=last_30d
526
+ - GET {campaignId}/insights?fields=impressions,clicks,spend&time_range={"since":"2025-01-01","until":"2025-01-31"}
527
+
528
+ ### Common Fields for Insights
529
+ impressions, reach, frequency, clicks, ctr, cpc, cpm, cpp, spend,
530
+ conversions, actions, action_values, cost_per_action_type,
531
+ video_p25_watched_actions, video_p50_watched_actions, video_p75_watched_actions, video_p100_watched_actions
532
+
533
+ ### Date Filters
534
+ - \`date_preset\`: today, yesterday, last_7d, last_14d, last_30d, last_90d, this_month, last_month
535
+ - \`time_range\`: {"since":"2025-01-01","until":"2025-01-31"}
536
+ - \`time_increment\`: 1 (daily), 7 (weekly), monthly, all_days
537
+
538
+ ### Breakdowns
539
+ age, gender, country, region, publisher_platform, platform_position, device_platform, impression_device
540
+
541
+ ### Pagination
542
+ Responses include a \`paging\` object with \`cursors.after\` and \`cursors.before\`. Use \`after\` parameter for next page.
543
+
544
+ ### Tips
545
+ - Ad account IDs require the \`act_\` prefix (e.g., act_123456789)
546
+ - Always specify \`fields\` parameter \u2014 without it, only IDs are returned
547
+ - Use \`level\` parameter in insights to aggregate at account/campaign/adset/ad level
548
+ - Use \`filtering\` parameter for server-side filtering: [{"field":"campaign.name","operator":"CONTAIN","value":"Brand"}]
549
+
550
+ ### Business Logic
551
+
552
+ 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.
553
+
554
+ #### Example
555
+
556
+ \`\`\`ts
557
+ import { connection } from "@squadbase/vite-server/connectors/meta-ads-oauth";
558
+
559
+ const meta = connection("<connectionId>");
560
+
561
+ // Get campaigns
562
+ const res = await meta.request("act_{adAccountId}/campaigns?fields=id,name,status");
563
+ const data = await res.json();
564
+ \`\`\``,
565
+ ja: `### \u30C4\u30FC\u30EB
566
+
567
+ - \`meta-ads-oauth_request\`: Meta Marketing API v21.0\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
568
+ - \`meta-ads-oauth_listAdAccounts\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AMeta\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
569
+
570
+ ### Meta Marketing API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
571
+
572
+ #### \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u4E00\u89A7
573
+ - GET act_{adAccountId}/campaigns?fields=id,name,status,objective,daily_budget,lifetime_budget
574
+
575
+ #### \u5E83\u544A\u30BB\u30C3\u30C8\u4E00\u89A7
576
+ - GET act_{adAccountId}/adsets?fields=id,name,status,targeting,daily_budget,bid_amount
577
+
578
+ #### \u5E83\u544A\u4E00\u89A7
579
+ - GET act_{adAccountId}/ads?fields=id,name,status,creative
580
+
581
+ #### \u30A4\u30F3\u30B5\u30A4\u30C8\uFF08\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u30C7\u30FC\u30BF\uFF09\u306E\u53D6\u5F97
582
+ - GET act_{adAccountId}/insights?fields=impressions,clicks,spend,ctr,cpm,cpc,conversions,actions&date_preset=last_30d
583
+ - GET {campaignId}/insights?fields=impressions,clicks,spend&time_range={"since":"2025-01-01","until":"2025-01-31"}
584
+
585
+ ### \u30A4\u30F3\u30B5\u30A4\u30C8\u306E\u4E3B\u8981\u30D5\u30A3\u30FC\u30EB\u30C9
586
+ impressions, reach, frequency, clicks, ctr, cpc, cpm, cpp, spend,
587
+ conversions, actions, action_values, cost_per_action_type,
588
+ video_p25_watched_actions, video_p50_watched_actions, video_p75_watched_actions, video_p100_watched_actions
589
+
590
+ ### \u65E5\u4ED8\u30D5\u30A3\u30EB\u30BF
591
+ - \`date_preset\`: today, yesterday, last_7d, last_14d, last_30d, last_90d, this_month, last_month
592
+ - \`time_range\`: {"since":"2025-01-01","until":"2025-01-31"}
593
+ - \`time_increment\`: 1\uFF08\u65E5\u6B21\uFF09, 7\uFF08\u9031\u6B21\uFF09, monthly, all_days
594
+
595
+ ### \u30D6\u30EC\u30A4\u30AF\u30C0\u30A6\u30F3
596
+ age, gender, country, region, publisher_platform, platform_position, device_platform, impression_device
597
+
598
+ ### \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3
599
+ \u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u306F \`paging\` \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u304C\u542B\u307E\u308C\u3001\`cursors.after\` \u3068 \`cursors.before\` \u304C\u3042\u308A\u307E\u3059\u3002\u6B21\u30DA\u30FC\u30B8\u306B\u306F \`after\` \u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u4F7F\u7528\u3057\u307E\u3059\u3002
600
+
601
+ ### \u30D2\u30F3\u30C8
602
+ - \u5E83\u544A\u30A2\u30AB\u30A6\u30F3\u30C8ID\u306B\u306F \`act_\` \u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u304C\u5FC5\u8981\u3067\u3059\uFF08\u4F8B\uFF1Aact_123456789\uFF09
603
+ - \`fields\` \u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u5FC5\u305A\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306FID\u306E\u307F\u304C\u8FD4\u3055\u308C\u307E\u3059
604
+ - \u30A4\u30F3\u30B5\u30A4\u30C8\u306E \`level\` \u30D1\u30E9\u30E1\u30FC\u30BF\u3067account/campaign/adset/ad\u30EC\u30D9\u30EB\u306E\u96C6\u8A08\u304C\u53EF\u80FD\u3067\u3059
605
+ - \`filtering\` \u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u30B5\u30FC\u30D0\u30FC\u30B5\u30A4\u30C9\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\u304C\u53EF\u80FD: [{"field":"campaign.name","operator":"CONTAIN","value":"Brand"}]
606
+
607
+ ### Business Logic
608
+
609
+ \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
610
+
611
+ #### Example
612
+
613
+ \`\`\`ts
614
+ import { connection } from "@squadbase/vite-server/connectors/meta-ads-oauth";
615
+
616
+ const meta = connection("<connectionId>");
617
+
618
+ // \u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u3092\u53D6\u5F97
619
+ const res = await meta.request("act_{adAccountId}/campaigns?fields=id,name,status");
620
+ const data = await res.json();
621
+ \`\`\``
622
+ },
623
+ tools,
624
+ async checkConnection(_params, config) {
625
+ const { proxyFetch } = config;
626
+ try {
627
+ const url = `https://graph.facebook.com/v21.0/me?fields=id,name`;
628
+ const res = await proxyFetch(url, { method: "GET" });
629
+ if (!res.ok) {
630
+ const data = await res.json().catch(() => ({}));
631
+ return {
632
+ success: false,
633
+ error: data?.error?.message ?? `Meta API failed: HTTP ${res.status}`
634
+ };
635
+ }
636
+ return { success: true };
637
+ } catch (error) {
638
+ return {
639
+ success: false,
640
+ error: error instanceof Error ? error.message : String(error)
641
+ };
642
+ }
643
+ }
644
+ });
645
+
646
+ // src/connectors/create-connector-sdk.ts
647
+ import { readFileSync } from "fs";
648
+ import path from "path";
649
+
650
+ // src/connector-client/env.ts
651
+ function resolveEnvVar(entry, key, connectionId) {
652
+ const envVarName = entry.envVars[key];
653
+ if (!envVarName) {
654
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
655
+ }
656
+ const value = process.env[envVarName];
657
+ if (!value) {
658
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
659
+ }
660
+ return value;
661
+ }
662
+ function resolveEnvVarOptional(entry, key) {
663
+ const envVarName = entry.envVars[key];
664
+ if (!envVarName) return void 0;
665
+ return process.env[envVarName] || void 0;
666
+ }
667
+
668
+ // src/connector-client/proxy-fetch.ts
669
+ import { getContext } from "hono/context-storage";
670
+ import { getCookie } from "hono/cookie";
671
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
672
+ function normalizeHeaders(input) {
673
+ const out = {};
674
+ if (!input) return out;
675
+ new Headers(input).forEach((value, key) => {
676
+ out[key] = value;
677
+ });
678
+ return out;
679
+ }
680
+ function createSandboxProxyFetch(connectionId) {
681
+ return async (input, init) => {
682
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
683
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
684
+ if (!token || !sandboxId) {
685
+ throw new Error(
686
+ "Connection proxy is not configured. Please check your deployment settings."
687
+ );
688
+ }
689
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
690
+ const originalMethod = init?.method ?? "GET";
691
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
692
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
693
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
694
+ return fetch(proxyUrl, {
695
+ method: "POST",
696
+ headers: {
697
+ "Content-Type": "application/json",
698
+ Authorization: `Bearer ${token}`
699
+ },
700
+ body: JSON.stringify({
701
+ url: originalUrl,
702
+ method: originalMethod,
703
+ headers: normalizeHeaders(init?.headers),
704
+ body: originalBody
705
+ })
706
+ });
707
+ };
708
+ }
709
+ function createDeployedAppProxyFetch(connectionId) {
710
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
711
+ if (!projectId) {
712
+ throw new Error(
713
+ "Connection proxy is not configured. Please check your deployment settings."
714
+ );
715
+ }
716
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
717
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
718
+ return async (input, init) => {
719
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
720
+ const originalMethod = init?.method ?? "GET";
721
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
722
+ const c = getContext();
723
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
724
+ if (!appSession) {
725
+ throw new Error(
726
+ "No authentication method available for connection proxy."
727
+ );
728
+ }
729
+ return fetch(proxyUrl, {
730
+ method: "POST",
731
+ headers: {
732
+ "Content-Type": "application/json",
733
+ Authorization: `Bearer ${appSession}`
734
+ },
735
+ body: JSON.stringify({
736
+ url: originalUrl,
737
+ method: originalMethod,
738
+ headers: normalizeHeaders(init?.headers),
739
+ body: originalBody
740
+ })
741
+ });
742
+ };
743
+ }
744
+ function createProxyFetch(connectionId) {
745
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
746
+ return createSandboxProxyFetch(connectionId);
747
+ }
748
+ return createDeployedAppProxyFetch(connectionId);
749
+ }
750
+
751
+ // src/connectors/create-connector-sdk.ts
752
+ function loadConnectionsSync() {
753
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
754
+ try {
755
+ const raw = readFileSync(filePath, "utf-8");
756
+ return JSON.parse(raw);
757
+ } catch {
758
+ return {};
759
+ }
760
+ }
761
+ function createConnectorSdk(plugin, createClient2) {
762
+ return (connectionId) => {
763
+ const connections = loadConnectionsSync();
764
+ const entry = connections[connectionId];
765
+ if (!entry) {
766
+ throw new Error(
767
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
768
+ );
769
+ }
770
+ if (entry.connector.slug !== plugin.slug) {
771
+ throw new Error(
772
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
773
+ );
774
+ }
775
+ const params = {};
776
+ for (const param of Object.values(plugin.parameters)) {
777
+ if (param.required) {
778
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
779
+ } else {
780
+ const val = resolveEnvVarOptional(entry, param.slug);
781
+ if (val !== void 0) params[param.slug] = val;
782
+ }
783
+ }
784
+ return createClient2(params, createProxyFetch(connectionId));
785
+ };
786
+ }
787
+
788
+ // src/connectors/entries/meta-ads-oauth.ts
789
+ var connection = createConnectorSdk(
790
+ metaAdsOauthConnector,
791
+ createClient
792
+ );
793
+ export {
794
+ connection
795
+ };