@squadbase/vite-server 0.1.3-dev.9 → 0.1.4-dev.0

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 (63) hide show
  1. package/dist/cli/index.js +14539 -29447
  2. package/dist/connectors/airtable-oauth.js +43 -6
  3. package/dist/connectors/airtable.js +43 -6
  4. package/dist/connectors/amplitude.js +43 -6
  5. package/dist/connectors/anthropic.js +43 -6
  6. package/dist/connectors/asana.js +43 -6
  7. package/dist/connectors/attio.js +522 -118
  8. package/dist/connectors/{google-ads-oauth.d.ts → backlog-api-key.d.ts} +1 -1
  9. package/dist/connectors/backlog-api-key.js +629 -0
  10. package/dist/connectors/customerio.js +43 -6
  11. package/dist/connectors/dbt.js +43 -6
  12. package/dist/connectors/{google-sheets-oauth.d.ts → gamma.d.ts} +1 -1
  13. package/dist/connectors/gamma.js +866 -0
  14. package/dist/connectors/gemini.js +43 -6
  15. package/dist/connectors/gmail-oauth.js +65 -8
  16. package/dist/connectors/gmail.js +104 -44
  17. package/dist/connectors/google-ads.d.ts +1 -1
  18. package/dist/connectors/google-ads.js +410 -332
  19. package/dist/connectors/google-analytics-oauth.js +61 -8
  20. package/dist/connectors/google-analytics.js +107 -292
  21. package/dist/connectors/google-calendar-oauth.js +61 -8
  22. package/dist/connectors/google-calendar.js +111 -58
  23. package/dist/connectors/{linkedin-ads-oauth.d.ts → google-docs.d.ts} +1 -1
  24. package/dist/connectors/google-docs.js +631 -0
  25. package/dist/connectors/google-drive.d.ts +5 -0
  26. package/dist/connectors/google-drive.js +875 -0
  27. package/dist/connectors/google-sheets.d.ts +1 -1
  28. package/dist/connectors/google-sheets.js +267 -285
  29. package/dist/connectors/google-slides.d.ts +5 -0
  30. package/dist/connectors/google-slides.js +663 -0
  31. package/dist/connectors/grafana.js +43 -6
  32. package/dist/connectors/hubspot-oauth.js +43 -6
  33. package/dist/connectors/hubspot.js +43 -6
  34. package/dist/connectors/intercom-oauth.js +43 -6
  35. package/dist/connectors/intercom.js +43 -6
  36. package/dist/connectors/jira-api-key.js +43 -6
  37. package/dist/connectors/kintone-api-token.js +256 -82
  38. package/dist/connectors/kintone.js +43 -6
  39. package/dist/connectors/linkedin-ads.js +188 -168
  40. package/dist/connectors/mailchimp-oauth.js +43 -6
  41. package/dist/connectors/mailchimp.js +43 -6
  42. package/dist/connectors/mixpanel.d.ts +5 -0
  43. package/dist/connectors/mixpanel.js +779 -0
  44. package/dist/connectors/notion-oauth.js +43 -6
  45. package/dist/connectors/notion.js +43 -6
  46. package/dist/connectors/openai.js +43 -6
  47. package/dist/connectors/sentry.d.ts +5 -0
  48. package/dist/connectors/sentry.js +761 -0
  49. package/dist/connectors/shopify-oauth.js +43 -6
  50. package/dist/connectors/shopify.js +43 -6
  51. package/dist/connectors/stripe-api-key.js +46 -7
  52. package/dist/connectors/stripe-oauth.js +43 -6
  53. package/dist/connectors/wix-store.js +43 -6
  54. package/dist/connectors/zendesk-oauth.js +43 -6
  55. package/dist/connectors/zendesk.js +43 -6
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +4574 -3863
  58. package/dist/main.js +4572 -3862
  59. package/dist/vite-plugin.js +4572 -3862
  60. package/package.json +30 -12
  61. package/dist/connectors/google-ads-oauth.js +0 -890
  62. package/dist/connectors/google-sheets-oauth.js +0 -718
  63. package/dist/connectors/linkedin-ads-oauth.js +0 -848
@@ -0,0 +1,779 @@
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/mixpanel/parameters.ts
46
+ var parameters = {
47
+ serviceAccountUsername: new ParameterDefinition({
48
+ slug: "service-account-username",
49
+ name: "Service Account Username",
50
+ description: "The Mixpanel service account username for authentication.",
51
+ envVarBaseKey: "MIXPANEL_SERVICE_ACCOUNT_USERNAME",
52
+ type: "text",
53
+ secret: true,
54
+ required: true
55
+ }),
56
+ serviceAccountSecret: new ParameterDefinition({
57
+ slug: "service-account-secret",
58
+ name: "Service Account Secret",
59
+ description: "The Mixpanel service account secret for authentication.",
60
+ envVarBaseKey: "MIXPANEL_SERVICE_ACCOUNT_SECRET",
61
+ type: "text",
62
+ secret: true,
63
+ required: true
64
+ }),
65
+ projectId: new ParameterDefinition({
66
+ slug: "project-id",
67
+ name: "Project ID",
68
+ description: "The Mixpanel project ID. Required for service account authentication. Found in Project Settings.",
69
+ envVarBaseKey: "MIXPANEL_PROJECT_ID",
70
+ type: "text",
71
+ secret: false,
72
+ required: true
73
+ }),
74
+ region: new ParameterDefinition({
75
+ slug: "region",
76
+ name: "Region",
77
+ description: 'Mixpanel data residency region. Set to "eu" for EU, "in" for India. Leave empty for US (default).',
78
+ envVarBaseKey: "MIXPANEL_REGION",
79
+ type: "text",
80
+ secret: false,
81
+ required: false
82
+ })
83
+ };
84
+
85
+ // ../connectors/src/connectors/mixpanel/sdk/index.ts
86
+ function createClient(params) {
87
+ const username = params[parameters.serviceAccountUsername.slug];
88
+ const secret = params[parameters.serviceAccountSecret.slug];
89
+ const projectId = params[parameters.projectId.slug];
90
+ const region = params[parameters.region.slug] ?? "us";
91
+ if (!username) {
92
+ throw new Error(
93
+ `mixpanel: missing required parameter: ${parameters.serviceAccountUsername.slug}`
94
+ );
95
+ }
96
+ if (!secret) {
97
+ throw new Error(
98
+ `mixpanel: missing required parameter: ${parameters.serviceAccountSecret.slug}`
99
+ );
100
+ }
101
+ if (!projectId) {
102
+ throw new Error(
103
+ `mixpanel: missing required parameter: ${parameters.projectId.slug}`
104
+ );
105
+ }
106
+ const queryBase = region === "eu" ? "https://eu.mixpanel.com/api/query" : region === "in" ? "https://in.mixpanel.com/api/query" : "https://mixpanel.com/api/query";
107
+ const exportBase = region === "eu" ? "https://data-eu.mixpanel.com/api/2.0/export" : region === "in" ? "https://data-in.mixpanel.com/api/2.0/export" : "https://data.mixpanel.com/api/2.0/export";
108
+ const authToken = btoa(`${username}:${secret}`);
109
+ function authHeaders(extra) {
110
+ const headers = new Headers(extra);
111
+ headers.set("Authorization", `Basic ${authToken}`);
112
+ headers.set("Accept", "application/json");
113
+ return headers;
114
+ }
115
+ async function assertOk(res, label) {
116
+ if (!res.ok) {
117
+ const body = await res.text().catch(() => "(unreadable body)");
118
+ throw new Error(
119
+ `mixpanel ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
120
+ );
121
+ }
122
+ }
123
+ function withProjectId(url) {
124
+ if (!url.searchParams.has("project_id")) {
125
+ url.searchParams.set("project_id", projectId);
126
+ }
127
+ return url;
128
+ }
129
+ return {
130
+ request(url, init) {
131
+ const parsedUrl = withProjectId(new URL(url));
132
+ const headers = new Headers(init?.headers);
133
+ headers.set("Authorization", `Basic ${authToken}`);
134
+ headers.set("Accept", "application/json");
135
+ return fetch(parsedUrl.toString(), { ...init, headers });
136
+ },
137
+ async exportEvents(options) {
138
+ const url = new URL(exportBase);
139
+ url.searchParams.set("project_id", projectId);
140
+ url.searchParams.set("from_date", options.fromDate);
141
+ url.searchParams.set("to_date", options.toDate);
142
+ if (options.event) {
143
+ url.searchParams.set("event", JSON.stringify(options.event));
144
+ }
145
+ if (options.where) {
146
+ url.searchParams.set("where", options.where);
147
+ }
148
+ if (options.limit !== void 0) {
149
+ url.searchParams.set("limit", String(options.limit));
150
+ }
151
+ const res = await fetch(url.toString(), {
152
+ method: "GET",
153
+ headers: authHeaders()
154
+ });
155
+ await assertOk(res, "exportEvents");
156
+ const text = await res.text();
157
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
158
+ return lines.map(
159
+ (line) => JSON.parse(line)
160
+ );
161
+ },
162
+ async queryInsights(bookmarkId) {
163
+ const url = withProjectId(new URL(`${queryBase}/insights`));
164
+ url.searchParams.set("bookmark_id", String(bookmarkId));
165
+ const res = await fetch(url.toString(), {
166
+ method: "GET",
167
+ headers: authHeaders()
168
+ });
169
+ await assertOk(res, "queryInsights");
170
+ return await res.json();
171
+ },
172
+ async queryFunnels(options) {
173
+ const url = withProjectId(new URL(`${queryBase}/funnels`));
174
+ url.searchParams.set("funnel_id", String(options.funnelId));
175
+ url.searchParams.set("from_date", options.fromDate);
176
+ url.searchParams.set("to_date", options.toDate);
177
+ if (options.unit) url.searchParams.set("unit", options.unit);
178
+ if (options.interval !== void 0) {
179
+ url.searchParams.set("interval", String(options.interval));
180
+ }
181
+ const res = await fetch(url.toString(), {
182
+ method: "GET",
183
+ headers: authHeaders()
184
+ });
185
+ await assertOk(res, "queryFunnels");
186
+ return await res.json();
187
+ },
188
+ async queryProfiles(options) {
189
+ const url = withProjectId(new URL(`${queryBase}/engage`));
190
+ const bodyParams = new URLSearchParams();
191
+ if (options?.where) bodyParams.set("where", options.where);
192
+ if (options?.outputProperties) {
193
+ bodyParams.set(
194
+ "output_properties",
195
+ JSON.stringify(options.outputProperties)
196
+ );
197
+ }
198
+ if (options?.page !== void 0) {
199
+ bodyParams.set("page", String(options.page));
200
+ }
201
+ if (options?.sessionId) {
202
+ bodyParams.set("session_id", options.sessionId);
203
+ }
204
+ const res = await fetch(url.toString(), {
205
+ method: "POST",
206
+ headers: authHeaders({
207
+ "Content-Type": "application/x-www-form-urlencoded"
208
+ }),
209
+ body: bodyParams.toString()
210
+ });
211
+ await assertOk(res, "queryProfiles");
212
+ return await res.json();
213
+ }
214
+ };
215
+ }
216
+
217
+ // ../connectors/src/connector-onboarding.ts
218
+ var ConnectorOnboarding = class {
219
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
220
+ connectionSetupInstructions;
221
+ /** Phase 2: Data overview instructions */
222
+ dataOverviewInstructions;
223
+ constructor(config) {
224
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
225
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
226
+ }
227
+ getConnectionSetupPrompt(language) {
228
+ return this.connectionSetupInstructions?.[language] ?? null;
229
+ }
230
+ getDataOverviewInstructions(language) {
231
+ return this.dataOverviewInstructions[language];
232
+ }
233
+ };
234
+
235
+ // ../connectors/src/connector-tool.ts
236
+ var ConnectorTool = class {
237
+ name;
238
+ description;
239
+ inputSchema;
240
+ outputSchema;
241
+ _execute;
242
+ constructor(config) {
243
+ this.name = config.name;
244
+ this.description = config.description;
245
+ this.inputSchema = config.inputSchema;
246
+ this.outputSchema = config.outputSchema;
247
+ this._execute = config.execute;
248
+ }
249
+ createTool(connections, config) {
250
+ return {
251
+ description: this.description,
252
+ inputSchema: this.inputSchema,
253
+ outputSchema: this.outputSchema,
254
+ execute: (input) => this._execute(input, connections, config)
255
+ };
256
+ }
257
+ };
258
+
259
+ // ../connectors/src/connector-plugin.ts
260
+ var ConnectorPlugin = class _ConnectorPlugin {
261
+ slug;
262
+ authType;
263
+ name;
264
+ description;
265
+ iconUrl;
266
+ parameters;
267
+ releaseFlag;
268
+ proxyPolicy;
269
+ experimentalAttributes;
270
+ onboarding;
271
+ systemPrompt;
272
+ tools;
273
+ query;
274
+ checkConnection;
275
+ constructor(config) {
276
+ this.slug = config.slug;
277
+ this.authType = config.authType;
278
+ this.name = config.name;
279
+ this.description = config.description;
280
+ this.iconUrl = config.iconUrl;
281
+ this.parameters = config.parameters;
282
+ this.releaseFlag = config.releaseFlag;
283
+ this.proxyPolicy = config.proxyPolicy;
284
+ this.experimentalAttributes = config.experimentalAttributes;
285
+ this.onboarding = config.onboarding;
286
+ this.systemPrompt = config.systemPrompt;
287
+ this.tools = config.tools;
288
+ this.query = config.query;
289
+ this.checkConnection = config.checkConnection;
290
+ }
291
+ get connectorKey() {
292
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
293
+ }
294
+ /**
295
+ * Create tools for connections that belong to this connector.
296
+ * Filters connections by connectorKey internally.
297
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
298
+ */
299
+ createTools(connections, config, opts) {
300
+ const myConnections = connections.filter(
301
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
302
+ );
303
+ const result = {};
304
+ for (const t of Object.values(this.tools)) {
305
+ const tool = t.createTool(myConnections, config);
306
+ const originalToModelOutput = tool.toModelOutput;
307
+ result[`${this.connectorKey}_${t.name}`] = {
308
+ ...tool,
309
+ toModelOutput: async (options) => {
310
+ if (!originalToModelOutput) {
311
+ return opts.truncateOutput(options.output);
312
+ }
313
+ const modelOutput = await originalToModelOutput(options);
314
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
315
+ return opts.truncateOutput(modelOutput.value);
316
+ }
317
+ return modelOutput;
318
+ }
319
+ };
320
+ }
321
+ return result;
322
+ }
323
+ static deriveKey(slug, authType) {
324
+ if (authType) return `${slug}-${authType}`;
325
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
326
+ // user-password
327
+ "postgresql": "user-password",
328
+ "mysql": "user-password",
329
+ "clickhouse": "user-password",
330
+ "kintone": "user-password",
331
+ "squadbase-db": "user-password",
332
+ // service-account
333
+ "snowflake": "service-account",
334
+ "bigquery": "service-account",
335
+ "google-analytics": "service-account",
336
+ "google-calendar": "service-account",
337
+ "aws-athena": "service-account",
338
+ "redshift": "service-account",
339
+ // api-key
340
+ "databricks": "api-key",
341
+ "dbt": "api-key",
342
+ "airtable": "api-key",
343
+ "openai": "api-key",
344
+ "gemini": "api-key",
345
+ "anthropic": "api-key",
346
+ "wix-store": "api-key"
347
+ };
348
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
349
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
350
+ return slug;
351
+ }
352
+ };
353
+
354
+ // ../connectors/src/auth-types.ts
355
+ var AUTH_TYPES = {
356
+ OAUTH: "oauth",
357
+ API_KEY: "api-key",
358
+ JWT: "jwt",
359
+ SERVICE_ACCOUNT: "service-account",
360
+ PAT: "pat",
361
+ USER_PASSWORD: "user-password"
362
+ };
363
+
364
+ // ../connectors/src/connectors/mixpanel/setup.ts
365
+ var mixpanelOnboarding = new ConnectorOnboarding({
366
+ dataOverviewInstructions: {
367
+ en: `1. Check the connection's region parameter to determine the base URL:
368
+ - US (default): https://mixpanel.com/api/query (Query API), https://data.mixpanel.com/api/2.0/export (Export API)
369
+ - EU: https://eu.mixpanel.com/api/query, https://data-eu.mixpanel.com/api/2.0/export
370
+ - India: https://in.mixpanel.com/api/query, https://data-in.mixpanel.com/api/2.0/export
371
+ 2. Call mixpanel_request with GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD (use a short recent date range, e.g. 1-2 days) to export raw events and discover available event types and properties
372
+ 3. Call mixpanel_request with POST {queryBase}/engage to query user profiles and understand user properties
373
+ NOTE: The Query API endpoints (insights, funnels, retention) require a bookmark_id or funnel_id from existing reports in Mixpanel. Use the Export API to retrieve raw events and aggregate in code if you need ad-hoc analysis.`,
374
+ ja: `1. \u63A5\u7D9A\u306Eregion\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u78BA\u8A8D\u3057\u3066\u30D9\u30FC\u30B9URL\u3092\u6C7A\u5B9A\uFF1A
375
+ - US\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\uFF09: https://mixpanel.com/api/query\uFF08Query API\uFF09\u3001https://data.mixpanel.com/api/2.0/export\uFF08Export API\uFF09
376
+ - EU: https://eu.mixpanel.com/api/query\u3001https://data-eu.mixpanel.com/api/2.0/export
377
+ - India: https://in.mixpanel.com/api/query\u3001https://data-in.mixpanel.com/api/2.0/export
378
+ 2. mixpanel_request \u3067 GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u3092\u547C\u3073\u51FA\u3057\uFF08\u76F4\u8FD11-2\u65E5\u306E\u77ED\u3044\u671F\u9593\u3092\u4F7F\u7528\uFF09\u3001\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3057\u3066\u5229\u7528\u53EF\u80FD\u306A\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u3068\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u78BA\u8A8D
379
+ 3. mixpanel_request \u3067 POST {queryBase}/engage \u3092\u547C\u3073\u51FA\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3092\u7167\u4F1A\u3057\u3066\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u628A\u63E1
380
+ \u6CE8\u610F: Query API\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\uFF08insights\u3001funnels\u3001retention\uFF09\u306FMixpanel\u5185\u306E\u65E2\u5B58\u30EC\u30DD\u30FC\u30C8\u306Ebookmark_id\u307E\u305F\u306Ffunnel_id\u304C\u5FC5\u8981\u3067\u3059\u3002\u30A2\u30C9\u30DB\u30C3\u30AF\u5206\u6790\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u3001Export API\u3067\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B3\u30FC\u30C9\u4E0A\u3067\u96C6\u8A08\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
381
+ }
382
+ });
383
+
384
+ // ../connectors/src/connectors/mixpanel/tools/request.ts
385
+ import { z } from "zod";
386
+ var REQUEST_TIMEOUT_MS = 6e4;
387
+ var inputSchema = z.object({
388
+ toolUseIntent: z.string().optional().describe(
389
+ "Brief description of what you intend to accomplish with this tool call"
390
+ ),
391
+ connectionId: z.string().describe("ID of the Mixpanel connection to use"),
392
+ method: z.enum(["GET", "POST"]).describe(
393
+ "HTTP method. GET for most Query API and Export endpoints. POST for Engage (profile query) endpoint."
394
+ ),
395
+ url: z.string().describe(
396
+ "Full URL including query parameters (e.g., 'https://mixpanel.com/api/query/insights?project_id=12345&bookmark_id=67890'). The project_id query parameter is added automatically if not present."
397
+ ),
398
+ body: z.string().optional().describe(
399
+ "Request body for POST requests. Use application/x-www-form-urlencoded format for the Engage endpoint, or JSON string for other POST endpoints."
400
+ ),
401
+ contentType: z.enum(["application/json", "application/x-www-form-urlencoded"]).optional().describe(
402
+ "Content-Type header. Defaults to application/json. Use application/x-www-form-urlencoded for the Engage endpoint."
403
+ )
404
+ });
405
+ var outputSchema = z.discriminatedUnion("success", [
406
+ z.object({
407
+ success: z.literal(true),
408
+ status: z.number(),
409
+ data: z.unknown()
410
+ }),
411
+ z.object({
412
+ success: z.literal(false),
413
+ error: z.string()
414
+ })
415
+ ]);
416
+ var requestTool = new ConnectorTool({
417
+ name: "request",
418
+ description: `Send authenticated requests to the Mixpanel REST API.
419
+ Authentication is handled automatically using Basic auth (Service Account username + secret).
420
+ Provide the full URL including any query parameters. The project_id query parameter is appended automatically if not present in the URL.
421
+
422
+ Base URLs by region and endpoint type:
423
+ - Query API: US \u2192 https://mixpanel.com/api/query, EU \u2192 https://eu.mixpanel.com/api/query, India \u2192 https://in.mixpanel.com/api/query
424
+ - Export API: US \u2192 https://data.mixpanel.com/api/2.0/export, EU \u2192 https://data-eu.mixpanel.com/api/2.0/export, India \u2192 https://data-in.mixpanel.com/api/2.0/export
425
+
426
+ Key endpoints:
427
+ - GET {queryBase}/insights?project_id=ID&bookmark_id=ID \u2014 Insights report (requires bookmark_id)
428
+ - GET {queryBase}/funnels?project_id=ID&funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Funnel analysis
429
+ - GET {queryBase}/retention?project_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Retention analysis
430
+ - POST {queryBase}/engage?project_id=ID \u2014 Query user/group profiles
431
+ - GET {exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD \u2014 Export raw events (JSONL)
432
+
433
+ Rate limit: 60 queries/hour, 5 concurrent queries for Query API.`,
434
+ inputSchema,
435
+ outputSchema,
436
+ async execute({ connectionId, method, url, body, contentType }, connections) {
437
+ const connection2 = connections.find((c) => c.id === connectionId);
438
+ if (!connection2) {
439
+ return {
440
+ success: false,
441
+ error: `Connection ${connectionId} not found`
442
+ };
443
+ }
444
+ console.log(
445
+ `[connector-request] mixpanel/${connection2.name}: ${method} ${url}`
446
+ );
447
+ try {
448
+ const username = parameters.serviceAccountUsername.getValue(connection2);
449
+ const secret = parameters.serviceAccountSecret.getValue(connection2);
450
+ const projectId = parameters.projectId.getValue(connection2);
451
+ const authToken = btoa(`${username}:${secret}`);
452
+ let finalUrl = url;
453
+ const urlObj = new URL(url);
454
+ if (!urlObj.searchParams.has("project_id")) {
455
+ urlObj.searchParams.set("project_id", projectId);
456
+ finalUrl = urlObj.toString();
457
+ }
458
+ const controller = new AbortController();
459
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
460
+ try {
461
+ const headers = {
462
+ Authorization: `Basic ${authToken}`,
463
+ Accept: "application/json"
464
+ };
465
+ const effectiveContentType = contentType ?? "application/json";
466
+ if (body) {
467
+ headers["Content-Type"] = effectiveContentType;
468
+ }
469
+ const response = await fetch(finalUrl, {
470
+ method,
471
+ headers,
472
+ body: body ?? void 0,
473
+ signal: controller.signal
474
+ });
475
+ const responseContentType = response.headers.get("content-type") ?? "";
476
+ let data;
477
+ if (responseContentType.includes("application/json")) {
478
+ data = await response.json();
479
+ } else {
480
+ const text = await response.text();
481
+ const lines = text.split("\n").filter((line) => line.trim().length > 0);
482
+ if (lines.length > 0) {
483
+ try {
484
+ data = lines.map(
485
+ (line) => JSON.parse(line)
486
+ );
487
+ } catch {
488
+ data = { raw: text };
489
+ }
490
+ } else {
491
+ data = { raw: text };
492
+ }
493
+ }
494
+ if (!response.ok) {
495
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
496
+ return { success: false, error: errorMessage };
497
+ }
498
+ return { success: true, status: response.status, data };
499
+ } finally {
500
+ clearTimeout(timeout);
501
+ }
502
+ } catch (err) {
503
+ const msg = err instanceof Error ? err.message : String(err);
504
+ return { success: false, error: msg };
505
+ }
506
+ }
507
+ });
508
+
509
+ // ../connectors/src/connectors/mixpanel/index.ts
510
+ var tools = { request: requestTool };
511
+ var mixpanelConnector = new ConnectorPlugin({
512
+ slug: "mixpanel",
513
+ authType: AUTH_TYPES.API_KEY,
514
+ name: "Mixpanel",
515
+ description: "Connect to Mixpanel for product analytics, event tracking, and user behavior analysis.",
516
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7ykz0vNRjMqvTVrxS8wsj1/b2cd795be5b373877dee77cc6147c758/images__3_.png",
517
+ parameters,
518
+ releaseFlag: { dev1: true, dev2: false, prod: false },
519
+ onboarding: mixpanelOnboarding,
520
+ systemPrompt: {
521
+ en: `### Tools
522
+
523
+ - \`mixpanel_request\`: The only way to call the Mixpanel REST API. Use it for exporting raw events, querying insights/funnels/retention reports, and querying user profiles. Authentication (Basic auth with Service Account username + secret) is configured automatically. The project_id query parameter is appended automatically. Provide the full URL including query parameters \u2014 the base URL varies by region and endpoint type.
524
+
525
+ ### Business Logic
526
+
527
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
528
+
529
+ SDK methods (client created via \`connection(connectionId)\`):
530
+ - \`client.request(url, init?)\` \u2014 low-level authenticated fetch (provide full URL; project_id is added automatically)
531
+ - \`client.exportEvents(options)\` \u2014 export raw events (fromDate, toDate, event?, where?, limit?)
532
+ - \`client.queryInsights(bookmarkId)\` \u2014 query an Insights report by bookmark ID
533
+ - \`client.queryFunnels(options)\` \u2014 query a Funnel report (funnelId, fromDate, toDate, unit?, interval?)
534
+ - \`client.queryProfiles(options?)\` \u2014 query user/group profiles (where?, outputProperties?, page?, sessionId?)
535
+
536
+ \`\`\`ts
537
+ import type { Context } from "hono";
538
+ import { connection } from "@squadbase/vite-server/connectors/mixpanel";
539
+
540
+ const mp = connection("<connectionId>");
541
+
542
+ export default async function handler(c: Context) {
543
+ const { fromDate, toDate } = await c.req.json<{
544
+ fromDate: string;
545
+ toDate: string;
546
+ }>();
547
+
548
+ const events = await mp.exportEvents({ fromDate, toDate, limit: 1000 });
549
+
550
+ return c.json(events);
551
+ }
552
+ \`\`\`
553
+
554
+ ### Mixpanel REST API Reference
555
+
556
+ - Authentication: Basic auth (Service Account username:secret, handled automatically)
557
+ - project_id is always appended automatically to all requests
558
+
559
+ #### Base URLs by Region
560
+
561
+ | Region | Query API | Export API |
562
+ |--------|-----------|------------|
563
+ | US (default) | https://mixpanel.com/api/query | https://data.mixpanel.com/api/2.0/export |
564
+ | EU | https://eu.mixpanel.com/api/query | https://data-eu.mixpanel.com/api/2.0/export |
565
+ | India | https://in.mixpanel.com/api/query | https://data-in.mixpanel.com/api/2.0/export |
566
+
567
+ #### Export API (Raw Events)
568
+ - GET \`{exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Export raw events (JSONL format)
569
+ - Optional: \`event\` (JSON array of event names), \`where\` (filter expression), \`limit\` (max 100,000)
570
+ - Rate limit: 60 queries/hour, 3 queries/second, 100 concurrent max
571
+
572
+ #### Query API (Reports)
573
+ - GET \`{queryBase}/insights?bookmark_id=ID\` \u2014 Insights report (requires bookmark_id from Mixpanel UI)
574
+ - GET \`{queryBase}/funnels?funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Funnel analysis
575
+ - GET \`{queryBase}/retention?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 Retention analysis
576
+ - POST \`{queryBase}/engage\` \u2014 Query user/group profiles (application/x-www-form-urlencoded body)
577
+ - Rate limit: 60 queries/hour, 5 concurrent max
578
+
579
+ IMPORTANT: The Insights endpoint requires a bookmark_id from an existing report in the Mixpanel UI. For ad-hoc event analysis, use the Export API to retrieve raw events and aggregate them in code. The Export API is the most flexible and universally accessible endpoint.`,
580
+ ja: `### \u30C4\u30FC\u30EB
581
+
582
+ - \`mixpanel_request\`: Mixpanel REST API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3001\u30A4\u30F3\u30B5\u30A4\u30C8/\u30D5\u30A1\u30CD\u30EB/\u30EA\u30C6\u30F3\u30B7\u30E7\u30F3\u30EC\u30DD\u30FC\u30C8\u306E\u7167\u4F1A\u3001\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08Service Account\u306E\u30E6\u30FC\u30B6\u30FC\u540D+\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u306B\u3088\u308BBasic\u8A8D\u8A3C\uFF09\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002project_id\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u306F\u81EA\u52D5\u7684\u306B\u4ED8\u52A0\u3055\u308C\u307E\u3059\u3002\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u542B\u3080\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 \u30D9\u30FC\u30B9URL\u306F\u30EA\u30FC\u30B8\u30E7\u30F3\u3068\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u30BF\u30A4\u30D7\u306B\u3088\u3063\u3066\u7570\u306A\u308A\u307E\u3059\u3002
583
+
584
+ ### Business Logic
585
+
586
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
587
+
588
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
589
+ - \`client.request(url, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch\uFF08\u5B8C\u5168\u306AURL\u3092\u6307\u5B9A\u3001project_id\u306F\u81EA\u52D5\u4ED8\u52A0\uFF09
590
+ - \`client.exportEvents(options)\` \u2014 \u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\uFF08fromDate, toDate, event?, where?, limit?\uFF09
591
+ - \`client.queryInsights(bookmarkId)\` \u2014 \u30D6\u30C3\u30AF\u30DE\u30FC\u30AFID\u3067Insights\u30EC\u30DD\u30FC\u30C8\u3092\u7167\u4F1A
592
+ - \`client.queryFunnels(options)\` \u2014 \u30D5\u30A1\u30CD\u30EB\u30EC\u30DD\u30FC\u30C8\u306E\u7167\u4F1A\uFF08funnelId, fromDate, toDate, unit?, interval?\uFF09
593
+ - \`client.queryProfiles(options?)\` \u2014 \u30E6\u30FC\u30B6\u30FC/\u30B0\u30EB\u30FC\u30D7\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\uFF08where?, outputProperties?, page?, sessionId?\uFF09
594
+
595
+ \`\`\`ts
596
+ import type { Context } from "hono";
597
+ import { connection } from "@squadbase/vite-server/connectors/mixpanel";
598
+
599
+ const mp = connection("<connectionId>");
600
+
601
+ export default async function handler(c: Context) {
602
+ const { fromDate, toDate } = await c.req.json<{
603
+ fromDate: string;
604
+ toDate: string;
605
+ }>();
606
+
607
+ const events = await mp.exportEvents({ fromDate, toDate, limit: 1000 });
608
+
609
+ return c.json(events);
610
+ }
611
+ \`\`\`
612
+
613
+ ### Mixpanel REST API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
614
+
615
+ - \u8A8D\u8A3C: Basic\u8A8D\u8A3C\uFF08Service Account\u306E\u30E6\u30FC\u30B6\u30FC\u540D:\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8\u3001\u81EA\u52D5\u8A2D\u5B9A\uFF09
616
+ - project_id\u306F\u3059\u3079\u3066\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u81EA\u52D5\u7684\u306B\u4ED8\u52A0\u3055\u308C\u307E\u3059
617
+
618
+ #### \u30EA\u30FC\u30B8\u30E7\u30F3\u5225\u30D9\u30FC\u30B9URL
619
+
620
+ | \u30EA\u30FC\u30B8\u30E7\u30F3 | Query API | Export API |
621
+ |-----------|-----------|------------|
622
+ | US\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\uFF09 | https://mixpanel.com/api/query | https://data.mixpanel.com/api/2.0/export |
623
+ | EU | https://eu.mixpanel.com/api/query | https://data-eu.mixpanel.com/api/2.0/export |
624
+ | India | https://in.mixpanel.com/api/query | https://data-in.mixpanel.com/api/2.0/export |
625
+
626
+ #### Export API\uFF08\u751F\u30A4\u30D9\u30F3\u30C8\uFF09
627
+ - GET \`{exportBase}?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u751F\u30A4\u30D9\u30F3\u30C8\u306E\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\uFF08JSONL\u5F62\u5F0F\uFF09
628
+ - \u30AA\u30D7\u30B7\u30E7\u30F3: \`event\`\uFF08\u30A4\u30D9\u30F3\u30C8\u540D\u306EJSON\u914D\u5217\uFF09\u3001\`where\`\uFF08\u30D5\u30A3\u30EB\u30BF\u5F0F\uFF09\u3001\`limit\`\uFF08\u6700\u5927100,000\uFF09
629
+ - \u30EC\u30FC\u30C8\u5236\u9650: 60\u30AF\u30A8\u30EA/\u6642\u30013\u30AF\u30A8\u30EA/\u79D2\u3001\u6700\u5927100\u540C\u6642\u63A5\u7D9A
630
+
631
+ #### Query API\uFF08\u30EC\u30DD\u30FC\u30C8\uFF09
632
+ - GET \`{queryBase}/insights?bookmark_id=ID\` \u2014 Insights\u30EC\u30DD\u30FC\u30C8\uFF08Mixpanel UI\u304B\u3089\u306Ebookmark_id\u304C\u5FC5\u8981\uFF09
633
+ - GET \`{queryBase}/funnels?funnel_id=ID&from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u30D5\u30A1\u30CD\u30EB\u5206\u6790
634
+ - GET \`{queryBase}/retention?from_date=YYYY-MM-DD&to_date=YYYY-MM-DD\` \u2014 \u30EA\u30C6\u30F3\u30B7\u30E7\u30F3\u5206\u6790
635
+ - POST \`{queryBase}/engage\` \u2014 \u30E6\u30FC\u30B6\u30FC/\u30B0\u30EB\u30FC\u30D7\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u7167\u4F1A\uFF08application/x-www-form-urlencoded\u306Ebody\uFF09
636
+ - \u30EC\u30FC\u30C8\u5236\u9650: 60\u30AF\u30A8\u30EA/\u6642\u3001\u6700\u59275\u540C\u6642\u63A5\u7D9A
637
+
638
+ \u91CD\u8981: Insights\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306F\u3001Mixpanel UI\u306E\u65E2\u5B58\u30EC\u30DD\u30FC\u30C8\u306Ebookmark_id\u304C\u5FC5\u8981\u3067\u3059\u3002\u30A2\u30C9\u30DB\u30C3\u30AF\u306A\u30A4\u30D9\u30F3\u30C8\u5206\u6790\u306B\u306F\u3001Export API\u3067\u751F\u30A4\u30D9\u30F3\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B3\u30FC\u30C9\u4E0A\u3067\u96C6\u8A08\u3057\u3066\u304F\u3060\u3055\u3044\u3002Export API\u304C\u6700\u3082\u67D4\u8EDF\u3067\u5E83\u304F\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067\u3059\u3002`
639
+ },
640
+ tools
641
+ });
642
+
643
+ // src/connectors/create-connector-sdk.ts
644
+ import { readFileSync } from "fs";
645
+ import path from "path";
646
+
647
+ // src/connector-client/env.ts
648
+ function resolveEnvVar(entry, key, connectionId) {
649
+ const envVarName = entry.envVars[key];
650
+ if (!envVarName) {
651
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
652
+ }
653
+ const value = process.env[envVarName];
654
+ if (!value) {
655
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
656
+ }
657
+ return value;
658
+ }
659
+ function resolveEnvVarOptional(entry, key) {
660
+ const envVarName = entry.envVars[key];
661
+ if (!envVarName) return void 0;
662
+ return process.env[envVarName] || void 0;
663
+ }
664
+
665
+ // src/connector-client/proxy-fetch.ts
666
+ import { getContext } from "hono/context-storage";
667
+ import { getCookie } from "hono/cookie";
668
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
669
+ function createSandboxProxyFetch(connectionId) {
670
+ return async (input, init) => {
671
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
672
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
673
+ if (!token || !sandboxId) {
674
+ throw new Error(
675
+ "Connection proxy is not configured. Please check your deployment settings."
676
+ );
677
+ }
678
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
679
+ const originalMethod = init?.method ?? "GET";
680
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
681
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
682
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
683
+ return fetch(proxyUrl, {
684
+ method: "POST",
685
+ headers: {
686
+ "Content-Type": "application/json",
687
+ Authorization: `Bearer ${token}`
688
+ },
689
+ body: JSON.stringify({
690
+ url: originalUrl,
691
+ method: originalMethod,
692
+ body: originalBody
693
+ })
694
+ });
695
+ };
696
+ }
697
+ function createDeployedAppProxyFetch(connectionId) {
698
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
699
+ if (!projectId) {
700
+ throw new Error(
701
+ "Connection proxy is not configured. Please check your deployment settings."
702
+ );
703
+ }
704
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
705
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
706
+ return async (input, init) => {
707
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
708
+ const originalMethod = init?.method ?? "GET";
709
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
710
+ const c = getContext();
711
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
712
+ if (!appSession) {
713
+ throw new Error(
714
+ "No authentication method available for connection proxy."
715
+ );
716
+ }
717
+ return fetch(proxyUrl, {
718
+ method: "POST",
719
+ headers: {
720
+ "Content-Type": "application/json",
721
+ Authorization: `Bearer ${appSession}`
722
+ },
723
+ body: JSON.stringify({
724
+ url: originalUrl,
725
+ method: originalMethod,
726
+ body: originalBody
727
+ })
728
+ });
729
+ };
730
+ }
731
+ function createProxyFetch(connectionId) {
732
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
733
+ return createSandboxProxyFetch(connectionId);
734
+ }
735
+ return createDeployedAppProxyFetch(connectionId);
736
+ }
737
+
738
+ // src/connectors/create-connector-sdk.ts
739
+ function loadConnectionsSync() {
740
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
741
+ try {
742
+ const raw = readFileSync(filePath, "utf-8");
743
+ return JSON.parse(raw);
744
+ } catch {
745
+ return {};
746
+ }
747
+ }
748
+ function createConnectorSdk(plugin, createClient2) {
749
+ return (connectionId) => {
750
+ const connections = loadConnectionsSync();
751
+ const entry = connections[connectionId];
752
+ if (!entry) {
753
+ throw new Error(
754
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
755
+ );
756
+ }
757
+ if (entry.connector.slug !== plugin.slug) {
758
+ throw new Error(
759
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
760
+ );
761
+ }
762
+ const params = {};
763
+ for (const param of Object.values(plugin.parameters)) {
764
+ if (param.required) {
765
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
766
+ } else {
767
+ const val = resolveEnvVarOptional(entry, param.slug);
768
+ if (val !== void 0) params[param.slug] = val;
769
+ }
770
+ }
771
+ return createClient2(params, createProxyFetch(connectionId));
772
+ };
773
+ }
774
+
775
+ // src/connectors/entries/mixpanel.ts
776
+ var connection = createConnectorSdk(mixpanelConnector, createClient);
777
+ export {
778
+ connection
779
+ };