@squadbase/connectors 0.1.2-dev.0 → 0.1.2-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.
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  parameters7,
10
10
  parameters8,
11
11
  parameters9
12
- } from "./chunk-Q5TIPE53.js";
12
+ } from "./chunk-LB7J6VXK.js";
13
13
 
14
14
  // src/connector-setup.ts
15
15
  var ConnectorSetup = class {
@@ -2901,79 +2901,152 @@ await airtable.updateRecords("Tasks", [
2901
2901
  tools: tools10
2902
2902
  });
2903
2903
 
2904
- // src/connectors/google-analytics/tools/request.ts
2904
+ // src/connectors/google-ads-oauth/tools/list-customers.ts
2905
2905
  import { z as z15 } from "zod";
2906
- var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
2906
+ var BASE_URL2 = "https://googleads.googleapis.com/v18/";
2907
2907
  var REQUEST_TIMEOUT_MS5 = 6e4;
2908
+ var cachedToken4 = null;
2909
+ async function getProxyToken4(config) {
2910
+ if (cachedToken4 && cachedToken4.expiresAt > Date.now() + 6e4) {
2911
+ return cachedToken4.token;
2912
+ }
2913
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
2914
+ const res = await fetch(url, {
2915
+ method: "POST",
2916
+ headers: {
2917
+ "Content-Type": "application/json",
2918
+ "x-api-key": config.appApiKey,
2919
+ "project-id": config.projectId
2920
+ },
2921
+ body: JSON.stringify({
2922
+ sandboxId: config.sandboxId,
2923
+ issuedBy: "coding-agent"
2924
+ })
2925
+ });
2926
+ if (!res.ok) {
2927
+ const errorText = await res.text().catch(() => res.statusText);
2928
+ throw new Error(
2929
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
2930
+ );
2931
+ }
2932
+ const data = await res.json();
2933
+ cachedToken4 = {
2934
+ token: data.token,
2935
+ expiresAt: new Date(data.expiresAt).getTime()
2936
+ };
2937
+ return data.token;
2938
+ }
2908
2939
  var inputSchema15 = z15.object({
2909
- toolUseIntent: z15.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
2910
- connectionId: z15.string().describe("ID of the Google Analytics connection to use"),
2911
- method: z15.enum(["GET", "POST"]).describe("HTTP method"),
2912
- path: z15.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
2913
- body: z15.record(z15.string(), z15.unknown()).optional().describe("POST request body (JSON)")
2940
+ toolUseIntent: z15.string().optional().describe(
2941
+ "Brief description of what you intend to accomplish with this tool call"
2942
+ ),
2943
+ connectionId: z15.string().describe("ID of the Google Ads OAuth connection to use")
2914
2944
  });
2915
2945
  var outputSchema15 = z15.discriminatedUnion("success", [
2916
2946
  z15.object({
2917
2947
  success: z15.literal(true),
2918
- status: z15.number(),
2919
- data: z15.record(z15.string(), z15.unknown())
2948
+ customers: z15.array(
2949
+ z15.object({
2950
+ customerId: z15.string(),
2951
+ descriptiveName: z15.string()
2952
+ })
2953
+ )
2920
2954
  }),
2921
2955
  z15.object({
2922
2956
  success: z15.literal(false),
2923
2957
  error: z15.string()
2924
2958
  })
2925
2959
  ]);
2926
- var requestTool2 = new ConnectorTool({
2927
- name: "request",
2928
- description: `Send authenticated requests to the Google Analytics Data API.
2929
- Authentication is handled automatically using a service account.
2930
- {propertyId} in the path is automatically replaced with the connection's property-id.`,
2960
+ var listCustomersTool = new ConnectorTool({
2961
+ name: "listCustomers",
2962
+ description: "List Google Ads customer accounts accessible with the current OAuth credentials.",
2931
2963
  inputSchema: inputSchema15,
2932
2964
  outputSchema: outputSchema15,
2933
- async execute({ connectionId, method, path, body }, connections) {
2965
+ async execute({ connectionId }, connections, config) {
2934
2966
  const connection = connections.find((c) => c.id === connectionId);
2935
2967
  if (!connection) {
2936
- return { success: false, error: `Connection ${connectionId} not found` };
2968
+ return {
2969
+ success: false,
2970
+ error: `Connection ${connectionId} not found`
2971
+ };
2937
2972
  }
2938
- console.log(`[connector-request] google-analytics/${connection.name}: ${method} ${path}`);
2973
+ console.log(
2974
+ `[connector-request] google-ads-oauth/${connection.name}: listCustomers`
2975
+ );
2939
2976
  try {
2940
- const { GoogleAuth } = await import("google-auth-library");
2941
- const keyJsonBase64 = parameters2.serviceAccountKeyJsonBase64.getValue(connection);
2942
- const propertyId = parameters2.propertyId.getValue(connection);
2943
- const credentials = JSON.parse(
2944
- Buffer.from(keyJsonBase64, "base64").toString("utf-8")
2945
- );
2946
- const auth = new GoogleAuth({
2947
- credentials,
2948
- scopes: ["https://www.googleapis.com/auth/analytics.readonly"]
2949
- });
2950
- const token = await auth.getAccessToken();
2951
- if (!token) {
2952
- return { success: false, error: "Failed to obtain access token" };
2953
- }
2954
- const resolvedPath = path.replace(/\{propertyId\}/g, propertyId);
2955
- const url = `${BASE_URL2}${resolvedPath}`;
2977
+ const developerToken = parameters2.developerToken.getValue(connection);
2978
+ const token = await getProxyToken4(config.oauthProxy);
2979
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
2956
2980
  const controller = new AbortController();
2957
2981
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS5);
2958
2982
  try {
2959
- const response = await fetch(url, {
2960
- method,
2983
+ const response = await fetch(proxyUrl, {
2984
+ method: "POST",
2961
2985
  headers: {
2962
- Authorization: `Bearer ${token}`,
2963
- "Content-Type": "application/json"
2986
+ "Content-Type": "application/json",
2987
+ Authorization: `Bearer ${token}`
2964
2988
  },
2965
- body: method === "POST" && body ? JSON.stringify(body) : void 0,
2989
+ body: JSON.stringify({
2990
+ url: `${BASE_URL2}customers:listAccessibleCustomers`,
2991
+ method: "GET",
2992
+ headers: {
2993
+ "developer-token": developerToken
2994
+ }
2995
+ }),
2966
2996
  signal: controller.signal
2967
2997
  });
2968
2998
  const data = await response.json();
2969
2999
  if (!response.ok) {
2970
- const errorObj = data?.error;
2971
- return {
2972
- success: false,
2973
- error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
2974
- };
3000
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
3001
+ return { success: false, error: errorMessage };
2975
3002
  }
2976
- return { success: true, status: response.status, data };
3003
+ const customerIds = (data.resourceNames ?? []).map(
3004
+ (rn) => rn.replace(/^customers\//, "")
3005
+ );
3006
+ const customers = [];
3007
+ for (const cid of customerIds) {
3008
+ try {
3009
+ const detailResponse = await fetch(proxyUrl, {
3010
+ method: "POST",
3011
+ headers: {
3012
+ "Content-Type": "application/json",
3013
+ Authorization: `Bearer ${token}`
3014
+ },
3015
+ body: JSON.stringify({
3016
+ url: `${BASE_URL2}customers/${cid}/googleAds:searchStream`,
3017
+ method: "POST",
3018
+ headers: {
3019
+ "Content-Type": "application/json",
3020
+ "developer-token": developerToken,
3021
+ "login-customer-id": cid
3022
+ },
3023
+ body: JSON.stringify({
3024
+ query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
3025
+ })
3026
+ }),
3027
+ signal: controller.signal
3028
+ });
3029
+ if (detailResponse.ok) {
3030
+ const detailData = await detailResponse.json();
3031
+ const customer = detailData?.[0]?.results?.[0]?.customer;
3032
+ customers.push({
3033
+ customerId: cid,
3034
+ descriptiveName: customer?.descriptiveName ?? cid
3035
+ });
3036
+ } else {
3037
+ customers.push({
3038
+ customerId: cid,
3039
+ descriptiveName: cid
3040
+ });
3041
+ }
3042
+ } catch {
3043
+ customers.push({
3044
+ customerId: cid,
3045
+ descriptiveName: cid
3046
+ });
3047
+ }
3048
+ }
3049
+ return { success: true, customers };
2977
3050
  } finally {
2978
3051
  clearTimeout(timeout);
2979
3052
  }
@@ -2984,136 +3057,182 @@ Authentication is handled automatically using a service account.
2984
3057
  }
2985
3058
  });
2986
3059
 
2987
- // src/connectors/google-analytics/index.ts
2988
- var tools11 = { request: requestTool2 };
2989
- var googleAnalyticsConnector = new ConnectorPlugin({
2990
- slug: "google-analytics",
2991
- authType: null,
2992
- name: "Google Analytics",
2993
- description: "Connect to Google Analytics for web analytics and reporting.",
2994
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
2995
- parameters: parameters2,
2996
- releaseFlag: { dev1: true, dev2: true, prod: true },
2997
- systemPrompt: `## Google Analytics Data API
2998
- - Call the GA4 Data API using the authenticated request tool
2999
- - {propertyId} in the path is automatically replaced
3060
+ // src/connectors/google-ads-oauth/setup.ts
3061
+ var listCustomersToolName = `google-ads-oauth_${listCustomersTool.name}`;
3062
+ var googleAdsSetup = new ConnectorSetup({
3063
+ ja: `## Google Ads \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
3000
3064
 
3001
- ### Get Metadata (Check available dimensions and metrics)
3002
- - GET properties/{propertyId}/metadata
3065
+ \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Ads (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
3003
3066
 
3004
- ### Get Report
3005
- - POST properties/{propertyId}:runReport
3006
- - Body example:
3007
- {
3008
- "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
3009
- "dimensions": [{"name": "date"}],
3010
- "metrics": [{"name": "activeUsers"}],
3011
- "limit": 100
3012
- }
3067
+ ### \u624B\u9806
3013
3068
 
3014
- ### Common Dimensions
3015
- date, country, city, deviceCategory, browser, pagePath, pageTitle,
3016
- sessionSource, sessionMedium, eventName
3069
+ #### \u30B9\u30C6\u30C3\u30D71: Developer Token \u8A2D\u5B9A
3070
+ 1. \u30E6\u30FC\u30B6\u30FC\u306B\u300CGoogle Ads API \u306E Developer Token \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08Google Ads \u7BA1\u7406\u753B\u9762 > \u30C4\u30FC\u30EB\u3068\u8A2D\u5B9A > API \u30BB\u30F3\u30BF\u30FC\u3067\u53D6\u5F97\u3067\u304D\u307E\u3059\uFF09\u300D\u3068\u4F1D\u3048\u308B
3071
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
3072
+ - \`parameterSlug\`: \`"developer-token"\`
3073
+ - \`value\`: \u30E6\u30FC\u30B6\u30FC\u304C\u63D0\u4F9B\u3057\u305F Developer Token
3017
3074
 
3018
- ### Common Metrics
3019
- activeUsers, sessions, screenPageViews, bounceRate,
3020
- averageSessionDuration, conversions, totalRevenue
3075
+ #### \u30B9\u30C6\u30C3\u30D72: \u30AB\u30B9\u30BF\u30DE\u30FC\u9078\u629E
3076
+ 1. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
3077
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
3078
+ - \`parameterSlug\`: \`"customer-id"\`
3079
+ - \`options\`: \u30AB\u30B9\u30BF\u30DE\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30AB\u30B9\u30BF\u30DE\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30B9\u30BF\u30DE\u30FCID
3080
+ 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D73\u306B\u9032\u3080
3021
3081
 
3022
- ### Date Specification
3023
- - Absolute: "2024-01-01"
3024
- - Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
3082
+ #### \u30B9\u30C6\u30C3\u30D73: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
3083
+ 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
3084
+ - \`customer\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E\u8868\u793A\u540D
3085
+ - \`customerId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FCID
3086
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
3025
3087
 
3026
- ## Google Analytics SDK (TypeScript handler)
3027
- Non-SQL connectors like Google Analytics can also be used via the SDK in TypeScript handlers:
3088
+ ### \u91CD\u8981\u306A\u5236\u7D04
3089
+ - **\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
3028
3090
 
3029
- \`\`\`ts
3030
- import { connection } from "@squadbase/vite-server/connectors/google-analytics";
3091
+ ### \u5B9F\u884C\u65B9\u91DD
3092
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
3093
+ - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
3094
+ en: `## Google Ads Setup Instructions
3031
3095
 
3032
- const ga = connection("<connectionId>");
3096
+ Follow these steps to set up the Google Ads (OAuth) connection.
3033
3097
 
3034
- // Authenticated fetch (returns standard Response)
3035
- const res = await ga.request("properties/{propertyId}:runReport", {
3036
- method: "POST",
3037
- body: JSON.stringify({ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], metrics: [{ name: "activeUsers" }] }),
3038
- });
3039
- const data = await res.json();
3098
+ ### Steps
3040
3099
 
3041
- // Convenience methods
3042
- const { rows, rowCount } = await ga.runReport({
3043
- dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
3044
- dimensions: [{ name: "date" }],
3045
- metrics: [{ name: "activeUsers" }, { name: "sessions" }],
3046
- limit: 100,
3047
- });
3048
- const metadata = await ga.getMetadata();
3049
- const realtime = await ga.runRealtimeReport({
3050
- metrics: [{ name: "activeUsers" }],
3051
- dimensions: [{ name: "country" }],
3052
- });
3053
- \`\`\``,
3054
- tools: tools11
3100
+ #### Step 1: Developer Token Setup
3101
+ 1. Ask the user to provide their Google Ads API Developer Token (available in Google Ads UI > Tools & Settings > API Center)
3102
+ 2. Call \`updateConnectionParameters\`:
3103
+ - \`parameterSlug\`: \`"developer-token"\`
3104
+ - \`value\`: The Developer Token provided by the user
3105
+
3106
+ #### Step 2: Customer Selection
3107
+ 1. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
3108
+ 2. Call \`updateConnectionParameters\`:
3109
+ - \`parameterSlug\`: \`"customer-id"\`
3110
+ - \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
3111
+ 3. The \`label\` of the user's selected customer will arrive as a message. Proceed to Step 3
3112
+
3113
+ #### Step 3: Complete Setup
3114
+ 1. Call \`updateConnectionContext\`:
3115
+ - \`customer\`: The selected customer's display name
3116
+ - \`customerId\`: The selected customer ID
3117
+ - \`note\`: Brief description of the setup
3118
+
3119
+ ### Important Constraints
3120
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
3121
+
3122
+ ### Execution Policy
3123
+ - Write only 1 sentence between tool calls, then immediately call the next tool
3124
+ - Skip unnecessary explanations and proceed efficiently`
3055
3125
  });
3056
3126
 
3057
- // src/connectors/kintone/tools/request.ts
3127
+ // src/connectors/google-ads-oauth/tools/request.ts
3058
3128
  import { z as z16 } from "zod";
3129
+ var BASE_URL3 = "https://googleads.googleapis.com/v18/";
3059
3130
  var REQUEST_TIMEOUT_MS6 = 6e4;
3060
- var inputSchema16 = z16.object({
3061
- toolUseIntent: z16.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
3062
- connectionId: z16.string().describe("ID of the kintone connection to use"),
3063
- method: z16.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP method"),
3064
- path: z16.string().describe("API path (e.g., 'apps.json', 'records.json?app=1&query=...')"),
3065
- body: z16.record(z16.string(), z16.unknown()).optional().describe("Request body (JSON)")
3066
- });
3067
- var outputSchema16 = z16.discriminatedUnion("success", [
3068
- z16.object({
3069
- success: z16.literal(true),
3070
- status: z16.number(),
3071
- data: z16.record(z16.string(), z16.unknown())
3072
- }),
3073
- z16.object({
3074
- success: z16.literal(false),
3075
- error: z16.string()
3076
- })
3077
- ]);
3078
- var requestTool3 = new ConnectorTool({
3079
- name: "request",
3080
- description: `Send authenticated requests to the kintone REST API.
3081
- Authentication is handled automatically using username and password.`,
3131
+ var cachedToken5 = null;
3132
+ async function getProxyToken5(config) {
3133
+ if (cachedToken5 && cachedToken5.expiresAt > Date.now() + 6e4) {
3134
+ return cachedToken5.token;
3135
+ }
3136
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3137
+ const res = await fetch(url, {
3138
+ method: "POST",
3139
+ headers: {
3140
+ "Content-Type": "application/json",
3141
+ "x-api-key": config.appApiKey,
3142
+ "project-id": config.projectId
3143
+ },
3144
+ body: JSON.stringify({
3145
+ sandboxId: config.sandboxId,
3146
+ issuedBy: "coding-agent"
3147
+ })
3148
+ });
3149
+ if (!res.ok) {
3150
+ const errorText = await res.text().catch(() => res.statusText);
3151
+ throw new Error(
3152
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3153
+ );
3154
+ }
3155
+ const data = await res.json();
3156
+ cachedToken5 = {
3157
+ token: data.token,
3158
+ expiresAt: new Date(data.expiresAt).getTime()
3159
+ };
3160
+ return data.token;
3161
+ }
3162
+ var inputSchema16 = z16.object({
3163
+ toolUseIntent: z16.string().optional().describe(
3164
+ "Brief description of what you intend to accomplish with this tool call"
3165
+ ),
3166
+ connectionId: z16.string().describe("ID of the Google Ads OAuth connection to use"),
3167
+ method: z16.enum(["GET", "POST"]).describe("HTTP method"),
3168
+ path: z16.string().describe(
3169
+ "API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
3170
+ ),
3171
+ body: z16.record(z16.string(), z16.unknown()).optional().describe("POST request body (JSON)")
3172
+ });
3173
+ var outputSchema16 = z16.discriminatedUnion("success", [
3174
+ z16.object({
3175
+ success: z16.literal(true),
3176
+ status: z16.number(),
3177
+ data: z16.unknown()
3178
+ }),
3179
+ z16.object({
3180
+ success: z16.literal(false),
3181
+ error: z16.string()
3182
+ })
3183
+ ]);
3184
+ var requestTool2 = new ConnectorTool({
3185
+ name: "request",
3186
+ description: `Send authenticated requests to the Google Ads API v18.
3187
+ Authentication is handled automatically via OAuth proxy.
3188
+ {customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
3082
3189
  inputSchema: inputSchema16,
3083
3190
  outputSchema: outputSchema16,
3084
- async execute({ connectionId, method, path, body }, connections) {
3191
+ async execute({ connectionId, method, path, body }, connections, config) {
3085
3192
  const connection = connections.find((c) => c.id === connectionId);
3086
3193
  if (!connection) {
3087
- return { success: false, error: `Connection ${connectionId} not found` };
3194
+ return {
3195
+ success: false,
3196
+ error: `Connection ${connectionId} not found`
3197
+ };
3088
3198
  }
3089
- console.log(`[connector-request] kintone/${connection.name}: ${method} ${path}`);
3199
+ console.log(
3200
+ `[connector-request] google-ads-oauth/${connection.name}: ${method} ${path}`
3201
+ );
3090
3202
  try {
3091
- const baseUrl = parameters3.baseUrl.getValue(connection);
3092
- const username = parameters3.username.getValue(connection);
3093
- const password = parameters3.password.getValue(connection);
3094
- const authToken = Buffer.from(`${username}:${password}`).toString("base64");
3095
- const url = `${baseUrl.replace(/\/+$/, "")}/k/v1/${path}`;
3203
+ const rawCustomerId = parameters2.customerId.tryGetValue(connection);
3204
+ const customerId = rawCustomerId?.replace(/-/g, "") ?? "";
3205
+ const resolvedPath = customerId ? path.replace(/\{customerId\}/g, customerId) : path;
3206
+ const url = `${BASE_URL3}${resolvedPath}`;
3207
+ const token = await getProxyToken5(config.oauthProxy);
3208
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3096
3209
  const controller = new AbortController();
3097
3210
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS6);
3098
3211
  try {
3099
- const headers = {
3100
- "X-Cybozu-Authorization": authToken
3101
- };
3102
- if (body) {
3103
- headers["Content-Type"] = "application/json";
3104
- }
3105
- const response = await fetch(url, {
3106
- method,
3107
- headers,
3108
- body: body ? JSON.stringify(body) : void 0,
3212
+ const developerToken = parameters2.developerToken.getValue(connection);
3213
+ const response = await fetch(proxyUrl, {
3214
+ method: "POST",
3215
+ headers: {
3216
+ "Content-Type": "application/json",
3217
+ Authorization: `Bearer ${token}`
3218
+ },
3219
+ body: JSON.stringify({
3220
+ url,
3221
+ method,
3222
+ headers: {
3223
+ "Content-Type": "application/json",
3224
+ "developer-token": developerToken,
3225
+ ...customerId ? { "login-customer-id": customerId } : {}
3226
+ },
3227
+ ...method === "POST" && body ? { body: JSON.stringify(body) } : {}
3228
+ }),
3109
3229
  signal: controller.signal
3110
3230
  });
3111
3231
  const data = await response.json();
3112
3232
  if (!response.ok) {
3113
- return {
3114
- success: false,
3115
- error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
3116
- };
3233
+ const dataObj = data;
3234
+ const errorMessage = typeof dataObj?.error === "string" ? dataObj.error : typeof dataObj?.message === "string" ? dataObj.message : `HTTP ${response.status} ${response.statusText}`;
3235
+ return { success: false, error: errorMessage };
3117
3236
  }
3118
3237
  return { success: true, status: response.status, data };
3119
3238
  } finally {
@@ -3126,80 +3245,129 @@ Authentication is handled automatically using username and password.`,
3126
3245
  }
3127
3246
  });
3128
3247
 
3129
- // src/connectors/kintone/index.ts
3130
- var tools12 = { request: requestTool3 };
3131
- var kintoneConnector = new ConnectorPlugin({
3132
- slug: "kintone",
3133
- authType: null,
3134
- name: "kintone",
3135
- description: "Connect to kintone for business application data retrieval and analytics.",
3136
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/76nPGMJFZkMFE3UQNo2JFy/e71dc5f5d5cec1306ce0e17aafbfd9f0/kintone.png",
3137
- parameters: parameters3,
3138
- releaseFlag: { dev1: true, dev2: true, prod: true },
3139
- systemPrompt: `## kintone REST API
3140
- - Call the kintone REST API using the authenticated request tool
3141
- - The base URL (e.g., https://example.cybozu.com) is automatically resolved
3248
+ // src/connectors/google-ads-oauth/index.ts
3249
+ var tools11 = {
3250
+ request: requestTool2,
3251
+ listCustomers: listCustomersTool
3252
+ };
3253
+ var googleAdsOauthConnector = new ConnectorPlugin({
3254
+ slug: "google-ads",
3255
+ authType: AUTH_TYPES.OAUTH,
3256
+ name: "Google Ads (OAuth)",
3257
+ description: "Connect to Google Ads for advertising campaign data and reporting using OAuth.",
3258
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1NGvmgvCxX7Tn11EST2N3N/a745fe7c63d360ed40a27ddaad3af168/google-ads.svg",
3259
+ parameters: parameters2,
3260
+ releaseFlag: { dev1: true, dev2: false, prod: false },
3261
+ setup: googleAdsSetup,
3262
+ proxyPolicy: {
3263
+ allowlist: [
3264
+ {
3265
+ host: "googleads.googleapis.com",
3266
+ methods: ["GET", "POST"]
3267
+ }
3268
+ ]
3269
+ },
3270
+ systemPrompt: `## Google Ads API (OAuth, Read-Only)
3271
+ - Use GAQL (Google Ads Query Language) to query campaign data
3272
+ - {customerId} in the path is automatically replaced (hyphens removed)
3142
3273
 
3143
- ### List Apps
3144
- - GET apps.json
3274
+ ### Query Data (searchStream)
3275
+ - POST customers/{customerId}/googleAds:searchStream
3276
+ - Body: { "query": "SELECT campaign.id, campaign.name, metrics.impressions FROM campaign WHERE segments.date DURING LAST_30_DAYS" }
3145
3277
 
3146
- ### Get Field Definitions
3147
- - GET app/form/fields.json?app={appId}
3278
+ ### Common GAQL Resources
3279
+ - \`campaign\`: Campaign data (campaign.id, campaign.name, campaign.status)
3280
+ - \`ad_group\`: Ad group data (ad_group.id, ad_group.name, ad_group.status)
3281
+ - \`ad_group_ad\`: Ad data (ad_group_ad.ad.id, ad_group_ad.status)
3282
+ - \`keyword_view\`: Keyword performance data
3148
3283
 
3149
- ### Get Records
3150
- - GET records.json?app={appId}&query={query}
3151
- - Query example: records.json?app=1&query=updatedTime > "2024-01-01" order by recordNumber asc limit 100
3284
+ ### Common Metrics
3285
+ metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions,
3286
+ metrics.ctr, metrics.average_cpc, metrics.conversions_value
3152
3287
 
3153
- ### Add Record
3154
- - POST record.json
3155
- - Body: { "app": 1, "record": { "fieldName": { "value": "value" } } }
3288
+ ### Common Segments
3289
+ segments.date, segments.device, segments.ad_network_type
3156
3290
 
3157
- ### kintone Query Syntax
3158
- - Comparison: fieldName = "value", fieldName > 100
3159
- - Operators: and, or, not
3160
- - Sort: order by fieldName asc/desc
3161
- - Limit: limit 100 offset 0
3162
- - String: like "partial match"
3291
+ ### Date Filters
3292
+ - \`DURING LAST_7_DAYS\`, \`DURING LAST_30_DAYS\`, \`DURING THIS_MONTH\`
3293
+ - \`WHERE segments.date BETWEEN '2024-01-01' AND '2024-01-31'\`
3163
3294
 
3164
- ## kintone SDK (TypeScript handler)
3165
- Non-SQL connectors like kintone can also be used via the SDK in TypeScript handlers:
3295
+ ### Tips
3296
+ - cost_micros is in micros (divide by 1,000,000 for actual currency)
3297
+ - Use LIMIT to restrict result count
3298
+ - Always include relevant WHERE clauses to filter data
3166
3299
 
3167
- \`\`\`ts
3168
- import { connection } from "@squadbase/vite-server/connectors/kintone";
3300
+ ## Google Ads SDK (TypeScript handler)
3169
3301
 
3170
- const kintone = connection("<connectionId>");
3302
+ \`\`\`ts
3303
+ import { connection } from "@squadbase/vite-server/connectors/google-ads-oauth";
3171
3304
 
3172
- // Authenticated fetch (returns standard Response)
3173
- const res = await kintone.request("/k/v1/records.json?app=1&query=limit 10");
3174
- const data = await res.json();
3305
+ const ads = connection("<connectionId>");
3175
3306
 
3176
- await kintone.request("/k/v1/record.json", {
3177
- method: "POST",
3178
- body: JSON.stringify({ app: 1, record: { title: { value: "Hello" } } }),
3179
- });
3307
+ // Execute a GAQL query
3308
+ const rows = await ads.search(
3309
+ "SELECT campaign.name, metrics.impressions, metrics.clicks FROM campaign WHERE segments.date DURING LAST_7_DAYS"
3310
+ );
3311
+ rows.forEach(row => console.log(row));
3180
3312
 
3181
- // Convenience methods (uses @kintone/rest-api-client)
3182
- const { records, totalCount } = await kintone.getRecords(1, {
3183
- query: 'status = "Active"',
3184
- fields: ["name", "email"],
3185
- totalCount: true,
3186
- });
3187
- const { record } = await kintone.getRecord(1, 100);
3188
- const { apps } = await kintone.listApps();
3313
+ // List accessible customer accounts
3314
+ const customerIds = await ads.listAccessibleCustomers();
3189
3315
  \`\`\``,
3190
- tools: tools12
3316
+ tools: tools11,
3317
+ async checkConnection(params, config) {
3318
+ const { proxyFetch } = config;
3319
+ const rawCustomerId = params[parameters2.customerId.slug];
3320
+ const customerId = rawCustomerId?.replace(/-/g, "");
3321
+ if (!customerId) {
3322
+ return { success: true };
3323
+ }
3324
+ const developerToken = params[parameters2.developerToken.slug];
3325
+ if (!developerToken) {
3326
+ return {
3327
+ success: false,
3328
+ error: "Developer token is required"
3329
+ };
3330
+ }
3331
+ const url = `https://googleads.googleapis.com/v18/customers/${customerId}/googleAds:searchStream`;
3332
+ try {
3333
+ const res = await proxyFetch(url, {
3334
+ method: "POST",
3335
+ headers: {
3336
+ "Content-Type": "application/json",
3337
+ "developer-token": developerToken,
3338
+ "login-customer-id": customerId
3339
+ },
3340
+ body: JSON.stringify({
3341
+ query: "SELECT customer.id FROM customer LIMIT 1"
3342
+ })
3343
+ });
3344
+ if (!res.ok) {
3345
+ const errorText = await res.text().catch(() => res.statusText);
3346
+ return {
3347
+ success: false,
3348
+ error: `Google Ads API failed: HTTP ${res.status} ${errorText}`
3349
+ };
3350
+ }
3351
+ return { success: true };
3352
+ } catch (error) {
3353
+ return {
3354
+ success: false,
3355
+ error: error instanceof Error ? error.message : String(error)
3356
+ };
3357
+ }
3358
+ }
3191
3359
  });
3192
3360
 
3193
- // src/connectors/wix-store/tools/request.ts
3361
+ // src/connectors/google-analytics/tools/request.ts
3194
3362
  import { z as z17 } from "zod";
3195
- var BASE_URL3 = "https://www.wixapis.com/";
3363
+ var BASE_URL4 = "https://analyticsdata.googleapis.com/v1beta/";
3196
3364
  var REQUEST_TIMEOUT_MS7 = 6e4;
3197
3365
  var inputSchema17 = z17.object({
3198
3366
  toolUseIntent: z17.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
3199
- connectionId: z17.string().describe("ID of the Wix Store connection to use"),
3367
+ connectionId: z17.string().describe("ID of the Google Analytics connection to use"),
3200
3368
  method: z17.enum(["GET", "POST"]).describe("HTTP method"),
3201
- path: z17.string().describe("API path (e.g., 'stores/v1/products/query', 'stores/v2/orders/query')"),
3202
- body: z17.record(z17.string(), z17.unknown()).optional().describe("Request body (JSON)")
3369
+ path: z17.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
3370
+ body: z17.record(z17.string(), z17.unknown()).optional().describe("POST request body (JSON)")
3203
3371
  });
3204
3372
  var outputSchema17 = z17.discriminatedUnion("success", [
3205
3373
  z17.object({
@@ -3212,10 +3380,11 @@ var outputSchema17 = z17.discriminatedUnion("success", [
3212
3380
  error: z17.string()
3213
3381
  })
3214
3382
  ]);
3215
- var requestTool4 = new ConnectorTool({
3383
+ var requestTool3 = new ConnectorTool({
3216
3384
  name: "request",
3217
- description: `Send authenticated requests to the Wix Store API.
3218
- Authentication is handled automatically using the API Key and Site ID.`,
3385
+ description: `Send authenticated requests to the Google Analytics Data API.
3386
+ Authentication is handled automatically using a service account.
3387
+ {propertyId} in the path is automatically replaced with the connection's property-id.`,
3219
3388
  inputSchema: inputSchema17,
3220
3389
  outputSchema: outputSchema17,
3221
3390
  async execute({ connectionId, method, path, body }, connections) {
@@ -3223,30 +3392,42 @@ Authentication is handled automatically using the API Key and Site ID.`,
3223
3392
  if (!connection) {
3224
3393
  return { success: false, error: `Connection ${connectionId} not found` };
3225
3394
  }
3226
- console.log(`[connector-request] wix-store/${connection.name}: ${method} ${path}`);
3395
+ console.log(`[connector-request] google-analytics/${connection.name}: ${method} ${path}`);
3227
3396
  try {
3228
- const apiKey = parameters4.apiKey.getValue(connection);
3229
- const siteId = parameters4.siteId.getValue(connection);
3230
- const url = `${BASE_URL3}${path}`;
3397
+ const { GoogleAuth } = await import("google-auth-library");
3398
+ const keyJsonBase64 = parameters3.serviceAccountKeyJsonBase64.getValue(connection);
3399
+ const propertyId = parameters3.propertyId.getValue(connection);
3400
+ const credentials = JSON.parse(
3401
+ Buffer.from(keyJsonBase64, "base64").toString("utf-8")
3402
+ );
3403
+ const auth = new GoogleAuth({
3404
+ credentials,
3405
+ scopes: ["https://www.googleapis.com/auth/analytics.readonly"]
3406
+ });
3407
+ const token = await auth.getAccessToken();
3408
+ if (!token) {
3409
+ return { success: false, error: "Failed to obtain access token" };
3410
+ }
3411
+ const resolvedPath = path.replace(/\{propertyId\}/g, propertyId);
3412
+ const url = `${BASE_URL4}${resolvedPath}`;
3231
3413
  const controller = new AbortController();
3232
3414
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS7);
3233
3415
  try {
3234
3416
  const response = await fetch(url, {
3235
3417
  method,
3236
3418
  headers: {
3237
- Authorization: apiKey,
3238
- "wix-site-id": siteId,
3419
+ Authorization: `Bearer ${token}`,
3239
3420
  "Content-Type": "application/json"
3240
3421
  },
3241
- body: body ? JSON.stringify(body) : void 0,
3422
+ body: method === "POST" && body ? JSON.stringify(body) : void 0,
3242
3423
  signal: controller.signal
3243
3424
  });
3244
3425
  const data = await response.json();
3245
3426
  if (!response.ok) {
3246
- const errorObj = data?.message;
3427
+ const errorObj = data?.error;
3247
3428
  return {
3248
3429
  success: false,
3249
- error: errorObj ?? `HTTP ${response.status} ${response.statusText}`
3430
+ error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
3250
3431
  };
3251
3432
  }
3252
3433
  return { success: true, status: response.status, data };
@@ -3260,144 +3441,176 @@ Authentication is handled automatically using the API Key and Site ID.`,
3260
3441
  }
3261
3442
  });
3262
3443
 
3263
- // src/connectors/wix-store/index.ts
3264
- var tools13 = { request: requestTool4 };
3265
- var wixStoreConnector = new ConnectorPlugin({
3266
- slug: "wix-store",
3444
+ // src/connectors/google-analytics/index.ts
3445
+ var tools12 = { request: requestTool3 };
3446
+ var googleAnalyticsConnector = new ConnectorPlugin({
3447
+ slug: "google-analytics",
3267
3448
  authType: null,
3268
- name: "Wix Store",
3269
- description: "Connect to Wix Store.",
3270
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/YyFxclQFzROIYpFam6vRK/e7e75d3feac49a1cc5e433c147216d23/Wix_logo_black.svg",
3271
- parameters: parameters4,
3449
+ name: "Google Analytics",
3450
+ description: "Connect to Google Analytics for web analytics and reporting.",
3451
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
3452
+ parameters: parameters3,
3272
3453
  releaseFlag: { dev1: true, dev2: true, prod: true },
3273
- systemPrompt: `## Wix Store API
3274
- - Call the Wix Store REST API using the authenticated request tool
3275
- - Authentication (API Key, Site ID) is automatically configured
3276
- - Wix API uses POST for query endpoints as well
3277
- - Wix uses WQL (Wix Query Language) for filters
3278
- - Max 100 items per page
3454
+ systemPrompt: `## Google Analytics Data API
3455
+ - Call the GA4 Data API using the authenticated request tool
3456
+ - {propertyId} in the path is automatically replaced
3279
3457
 
3280
- ### List Products
3281
- - POST stores/v1/products/query
3282
- - Body: { "query": { "paging": { "limit": 50, "offset": 0 } } }
3458
+ ### Get Metadata (Check available dimensions and metrics)
3459
+ - GET properties/{propertyId}/metadata
3283
3460
 
3284
- ### Search Products (with filter)
3285
- - POST stores/v1/products/query
3286
- - Body: { "query": { "filter": { "name": { "$contains": "keyword" } }, "paging": { "limit": 50 } } }
3461
+ ### Get Report
3462
+ - POST properties/{propertyId}:runReport
3463
+ - Body example:
3464
+ {
3465
+ "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
3466
+ "dimensions": [{"name": "date"}],
3467
+ "metrics": [{"name": "activeUsers"}],
3468
+ "limit": 100
3469
+ }
3287
3470
 
3288
- ### Search Orders (eCommerce Orders V1)
3289
- - POST ecom/v1/orders/search
3290
- - Body: { "search": { "cursorPaging": { "limit": 50 } } }
3291
- - Uses cursor-based pagination (cursorPaging, not paging)
3292
- - Response includes pagingMetadata.cursors.next for pagination
3471
+ ### Common Dimensions
3472
+ date, country, city, deviceCategory, browser, pagePath, pageTitle,
3473
+ sessionSource, sessionMedium, eventName
3293
3474
 
3294
- ### List Collections (Categories)
3295
- - POST stores/v1/collections/query
3296
- - Body: { "query": { "paging": { "limit": 50 } } }
3475
+ ### Common Metrics
3476
+ activeUsers, sessions, screenPageViews, bounceRate,
3477
+ averageSessionDuration, conversions, totalRevenue
3297
3478
 
3298
- ### List Inventory Items
3299
- - POST stores/v2/inventoryItems/query
3300
- - Body: { "query": { "paging": { "limit": 50 } } }
3479
+ ### Date Specification
3480
+ - Absolute: "2024-01-01"
3481
+ - Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
3301
3482
 
3302
- ## Wix Store SDK (TypeScript handler)
3303
- Non-SQL connectors like Wix Store can also be used via the SDK in TypeScript handlers:
3483
+ ## Google Analytics SDK (TypeScript handler)
3484
+ Non-SQL connectors like Google Analytics can also be used via the SDK in TypeScript handlers:
3304
3485
 
3305
3486
  \`\`\`ts
3306
- import { connection } from "@squadbase/vite-server/connectors/wix-store";
3487
+ import { connection } from "@squadbase/vite-server/connectors/google-analytics";
3307
3488
 
3308
- const wix = connection("<connectionId>");
3489
+ const ga = connection("<connectionId>");
3309
3490
 
3310
3491
  // Authenticated fetch (returns standard Response)
3311
- const res = await wix.request("/stores/v1/products/query", {
3492
+ const res = await ga.request("properties/{propertyId}:runReport", {
3312
3493
  method: "POST",
3313
- headers: { "Content-Type": "application/json" },
3314
- body: JSON.stringify({ query: { paging: { limit: 10 } } }),
3494
+ body: JSON.stringify({ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], metrics: [{ name: "activeUsers" }] }),
3315
3495
  });
3316
3496
  const data = await res.json();
3317
3497
 
3318
3498
  // Convenience methods
3319
- const { products, totalResults } = await wix.queryProducts({ paging: { limit: 50 } });
3320
- const { product } = await wix.getProduct("product-id");
3321
- const { orders, pagingMetadata } = await wix.queryOrders({ cursorPaging: { limit: 50 } });
3322
- const { order } = await wix.getOrder("order-id");
3323
- const { inventoryItems } = await wix.queryInventory({ paging: { limit: 50 } });
3324
- const { collections } = await wix.queryCollections({ paging: { limit: 50 } });
3499
+ const { rows, rowCount } = await ga.runReport({
3500
+ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
3501
+ dimensions: [{ name: "date" }],
3502
+ metrics: [{ name: "activeUsers" }, { name: "sessions" }],
3503
+ limit: 100,
3504
+ });
3505
+ const metadata = await ga.getMetadata();
3506
+ const realtime = await ga.runRealtimeReport({
3507
+ metrics: [{ name: "activeUsers" }],
3508
+ dimensions: [{ name: "country" }],
3509
+ });
3325
3510
  \`\`\``,
3326
- tools: tools13
3511
+ tools: tools12
3327
3512
  });
3328
3513
 
3329
- // src/connectors/dbt/tools/request.ts
3514
+ // src/connectors/google-analytics-oauth/tools/list-accounts.ts
3330
3515
  import { z as z18 } from "zod";
3516
+ var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta/";
3331
3517
  var REQUEST_TIMEOUT_MS8 = 6e4;
3332
- function resolveGraphqlEndpoint(host) {
3333
- if (host.includes("emea")) return "https://metadata.emea.dbt.com/graphql";
3334
- if (host.includes(".au.")) return "https://metadata.au.dbt.com/graphql";
3335
- return "https://metadata.cloud.getdbt.com/graphql";
3518
+ var cachedToken6 = null;
3519
+ async function getProxyToken6(config) {
3520
+ if (cachedToken6 && cachedToken6.expiresAt > Date.now() + 6e4) {
3521
+ return cachedToken6.token;
3522
+ }
3523
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3524
+ const res = await fetch(url, {
3525
+ method: "POST",
3526
+ headers: {
3527
+ "Content-Type": "application/json",
3528
+ "x-api-key": config.appApiKey,
3529
+ "project-id": config.projectId
3530
+ },
3531
+ body: JSON.stringify({
3532
+ sandboxId: config.sandboxId,
3533
+ issuedBy: "coding-agent"
3534
+ })
3535
+ });
3536
+ if (!res.ok) {
3537
+ const errorText = await res.text().catch(() => res.statusText);
3538
+ throw new Error(
3539
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3540
+ );
3541
+ }
3542
+ const data = await res.json();
3543
+ cachedToken6 = {
3544
+ token: data.token,
3545
+ expiresAt: new Date(data.expiresAt).getTime()
3546
+ };
3547
+ return data.token;
3336
3548
  }
3337
3549
  var inputSchema18 = z18.object({
3338
- toolUseIntent: z18.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
3339
- connectionId: z18.string().describe("ID of the dbt Cloud connection to use"),
3340
- query: z18.string().describe("GraphQL query"),
3341
- variables: z18.record(z18.string(), z18.unknown()).optional().describe("GraphQL variables (JSON)")
3550
+ toolUseIntent: z18.string().optional().describe(
3551
+ "Brief description of what you intend to accomplish with this tool call"
3552
+ ),
3553
+ connectionId: z18.string().describe("ID of the Google Analytics OAuth connection to use")
3342
3554
  });
3343
3555
  var outputSchema18 = z18.discriminatedUnion("success", [
3344
3556
  z18.object({
3345
3557
  success: z18.literal(true),
3346
- data: z18.record(z18.string(), z18.unknown())
3558
+ accounts: z18.array(
3559
+ z18.object({
3560
+ name: z18.string(),
3561
+ displayName: z18.string()
3562
+ })
3563
+ )
3347
3564
  }),
3348
3565
  z18.object({
3349
3566
  success: z18.literal(false),
3350
3567
  error: z18.string()
3351
3568
  })
3352
3569
  ]);
3353
- var requestTool5 = new ConnectorTool({
3354
- name: "request",
3355
- description: `Send authenticated requests to the dbt Cloud Discovery API (GraphQL).
3356
- Authentication is handled automatically using the API token.
3357
- {environmentId} in GraphQL variables is automatically replaced with the prod-env-id.`,
3570
+ var listAccountsTool = new ConnectorTool({
3571
+ name: "listAccounts",
3572
+ description: "List Google Analytics accounts accessible with the current OAuth credentials. Returns account names and display names.",
3358
3573
  inputSchema: inputSchema18,
3359
3574
  outputSchema: outputSchema18,
3360
- async execute({ connectionId, query, variables }, connections) {
3575
+ async execute({ connectionId }, connections, config) {
3361
3576
  const connection = connections.find((c) => c.id === connectionId);
3362
3577
  if (!connection) {
3363
- return { success: false, error: `Connection ${connectionId} not found` };
3578
+ return {
3579
+ success: false,
3580
+ error: `Connection ${connectionId} not found`
3581
+ };
3364
3582
  }
3365
- console.log(`[connector-request] dbt/${connection.name}: GraphQL query`);
3583
+ console.log(
3584
+ `[connector-request] google-analytics-oauth/${connection.name}: listAccounts`
3585
+ );
3366
3586
  try {
3367
- const host = parameters5.host.getValue(connection);
3368
- const token = parameters5.token.getValue(connection);
3369
- const environmentId = parameters5.prodEnvId.getValue(connection);
3370
- const resolvedVariables = variables ? JSON.parse(
3371
- JSON.stringify(variables).replace(/\{environmentId\}/g, environmentId)
3372
- ) : void 0;
3373
- const endpoint = resolveGraphqlEndpoint(host);
3587
+ const token = await getProxyToken6(config.oauthProxy);
3588
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3374
3589
  const controller = new AbortController();
3375
3590
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS8);
3376
3591
  try {
3377
- const response = await fetch(endpoint, {
3592
+ const response = await fetch(proxyUrl, {
3378
3593
  method: "POST",
3379
3594
  headers: {
3380
- Authorization: `Bearer ${token}`,
3381
- "Content-Type": "application/json"
3595
+ "Content-Type": "application/json",
3596
+ Authorization: `Bearer ${token}`
3382
3597
  },
3383
- body: JSON.stringify({ query, variables: resolvedVariables }),
3598
+ body: JSON.stringify({
3599
+ url: `${ADMIN_BASE_URL}accounts`,
3600
+ method: "GET"
3601
+ }),
3384
3602
  signal: controller.signal
3385
3603
  });
3386
3604
  const data = await response.json();
3387
3605
  if (!response.ok) {
3388
- return {
3389
- success: false,
3390
- error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
3391
- };
3392
- }
3393
- if (Array.isArray(data?.errors) && data.errors.length > 0) {
3394
- const errors = data.errors;
3395
- return {
3396
- success: false,
3397
- error: errors.map((e) => e.message).join("; ")
3398
- };
3606
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
3607
+ return { success: false, error: errorMessage };
3399
3608
  }
3400
- return { success: true, data };
3609
+ const accounts = (data.accounts ?? []).map((a) => ({
3610
+ name: a.name ?? "",
3611
+ displayName: a.displayName ?? ""
3612
+ }));
3613
+ return { success: true, accounts };
3401
3614
  } finally {
3402
3615
  clearTimeout(timeout);
3403
3616
  }
@@ -3408,144 +3621,72 @@ Authentication is handled automatically using the API token.
3408
3621
  }
3409
3622
  });
3410
3623
 
3411
- // src/connectors/dbt/index.ts
3412
- var tools14 = { request: requestTool5 };
3413
- var dbtConnector = new ConnectorPlugin({
3414
- slug: "dbt",
3415
- authType: null,
3416
- name: "dbt",
3417
- description: "Connect to dbt Cloud for data transformation and analytics engineering.",
3418
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4iT6ncXtdtHdkXexU0WgfZ/0367a38d245f2568eab5eb511f9ee692/dbt.png",
3419
- parameters: parameters5,
3420
- releaseFlag: { dev1: true, dev2: true, prod: true },
3421
- systemPrompt: `## dbt Cloud Discovery API (GraphQL)
3422
- - Call the dbt Cloud Discovery API using the authenticated request tool
3423
- - {environmentId} in GraphQL variables is automatically replaced with the prod-env-id
3424
-
3425
- ### List Models
3426
- query:
3427
- query($environmentId: BigInt!) {
3428
- environment(id: $environmentId) {
3429
- applied {
3430
- models(first: 100) {
3431
- edges {
3432
- node { uniqueId name description database schema alias materializedType }
3433
- }
3434
- }
3435
- }
3436
- }
3624
+ // src/connectors/google-analytics-oauth/tools/list-properties.ts
3625
+ import { z as z19 } from "zod";
3626
+ var ADMIN_BASE_URL2 = "https://analyticsadmin.googleapis.com/v1beta/";
3627
+ var REQUEST_TIMEOUT_MS9 = 6e4;
3628
+ var cachedToken7 = null;
3629
+ async function getProxyToken7(config) {
3630
+ if (cachedToken7 && cachedToken7.expiresAt > Date.now() + 6e4) {
3631
+ return cachedToken7.token;
3437
3632
  }
3438
- variables: { "environmentId": "{environmentId}" }
3439
-
3440
- ### List Sources
3441
- query:
3442
- query($environmentId: BigInt!) {
3443
- environment(id: $environmentId) {
3444
- applied {
3445
- sources(first: 100) {
3446
- edges {
3447
- node { uniqueId name description database schema identifier }
3448
- }
3449
- }
3450
- }
3451
- }
3452
- }
3453
- variables: { "environmentId": "{environmentId}" }
3454
-
3455
- ### Model Column Information
3456
- query:
3457
- query($environmentId: BigInt!, $uniqueId: String!) {
3458
- environment(id: $environmentId) {
3459
- applied {
3460
- models(filter: { uniqueId: { eq: $uniqueId } }) {
3461
- edges {
3462
- node { uniqueId name columns { name description type } }
3463
- }
3464
- }
3465
- }
3466
- }
3467
- }
3468
-
3469
- ### Lineage (Dependencies)
3470
- - Traverse relationships using ancestors / children fields
3471
- - Get ancestors { uniqueId name } or children { uniqueId name } within node
3472
-
3473
- ## dbt SDK (TypeScript handler)
3474
- Non-SQL connectors like dbt can also be used via the SDK in TypeScript handlers:
3475
-
3476
- \`\`\`ts
3477
- import { connection } from "@squadbase/vite-server/connectors/dbt";
3478
-
3479
- const dbt = connection("<connectionId>");
3480
-
3481
- // Authenticated fetch (returns standard Response)
3482
- const res = await dbt.request("/graphql", {
3483
- method: "POST",
3484
- headers: { "Content-Type": "application/json" },
3485
- body: JSON.stringify({ query: "{ ... }", variables: {} }),
3486
- });
3487
- const data = await res.json();
3488
-
3489
- // Convenience methods
3490
- const result = await dbt.query(\`
3491
- query($environmentId: BigInt!) {
3492
- environment(id: $environmentId) { applied { models(first: 10) { edges { node { name } } } } }
3633
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3634
+ const res = await fetch(url, {
3635
+ method: "POST",
3636
+ headers: {
3637
+ "Content-Type": "application/json",
3638
+ "x-api-key": config.appApiKey,
3639
+ "project-id": config.projectId
3640
+ },
3641
+ body: JSON.stringify({
3642
+ sandboxId: config.sandboxId,
3643
+ issuedBy: "coding-agent"
3644
+ })
3645
+ });
3646
+ if (!res.ok) {
3647
+ const errorText = await res.text().catch(() => res.statusText);
3648
+ throw new Error(
3649
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3650
+ );
3493
3651
  }
3494
- \`);
3495
- const models = await dbt.getModels({ limit: 100 });
3496
- const model = await dbt.getModelByUniqueId("model.project.my_model");
3497
- const sources = await dbt.getSources({ limit: 100 });
3498
- const tests = await dbt.getTests({ limit: 100 });
3499
- const metrics = await dbt.getMetrics({ limit: 100 });
3500
- \`\`\``,
3501
- tools: tools14
3502
- });
3503
-
3504
- // src/connectors/squadbase-db/parameters.ts
3505
- var parameters19 = {
3506
- connectionUrl: new ParameterDefinition({
3507
- slug: "connection-url",
3508
- name: "Connection URL",
3509
- description: "PostgreSQL connection URL for Squadbase DB.",
3510
- envVarBaseKey: "SQUADBASE_DB_CONNECTION_URL",
3511
- type: "text",
3512
- secret: true,
3513
- required: true
3514
- })
3515
- };
3516
-
3517
- // src/connectors/squadbase-db/tools/execute-query.ts
3518
- import { z as z19 } from "zod";
3519
- var MAX_ROWS10 = 500;
3520
- var CONNECT_TIMEOUT_MS3 = 1e4;
3521
- var STATEMENT_TIMEOUT_MS2 = 6e4;
3652
+ const data = await res.json();
3653
+ cachedToken7 = {
3654
+ token: data.token,
3655
+ expiresAt: new Date(data.expiresAt).getTime()
3656
+ };
3657
+ return data.token;
3658
+ }
3522
3659
  var inputSchema19 = z19.object({
3523
3660
  toolUseIntent: z19.string().optional().describe(
3524
3661
  "Brief description of what you intend to accomplish with this tool call"
3525
3662
  ),
3526
- connectionId: z19.string().describe("ID of the Squadbase DB connection to use"),
3527
- sql: z19.string().describe("PostgreSQL SQL query. Always include LIMIT in queries.")
3663
+ connectionId: z19.string().describe("ID of the Google Analytics OAuth connection to use"),
3664
+ accountName: z19.string().describe(
3665
+ "The account resource name (e.g., 'accounts/123456'). Obtained from the listAccounts tool."
3666
+ )
3528
3667
  });
3529
3668
  var outputSchema19 = z19.discriminatedUnion("success", [
3530
3669
  z19.object({
3531
3670
  success: z19.literal(true),
3532
- rowCount: z19.number(),
3533
- truncated: z19.boolean(),
3534
- rows: z19.array(z19.record(z19.string(), z19.unknown()))
3671
+ properties: z19.array(
3672
+ z19.object({
3673
+ name: z19.string(),
3674
+ displayName: z19.string(),
3675
+ propertyId: z19.string()
3676
+ })
3677
+ )
3535
3678
  }),
3536
3679
  z19.object({
3537
3680
  success: z19.literal(false),
3538
3681
  error: z19.string()
3539
3682
  })
3540
3683
  ]);
3541
- var executeQueryTool10 = new ConnectorTool({
3542
- name: "executeQuery",
3543
- description: `Execute SQL against Squadbase DB (PostgreSQL). Returns up to ${MAX_ROWS10} rows.
3544
- Use for: schema exploration (information_schema), data sampling, analytical queries.
3545
- Avoid loading large amounts of data; always include LIMIT in queries.`,
3684
+ var listPropertiesTool = new ConnectorTool({
3685
+ name: "listProperties",
3686
+ description: "List GA4 properties for a given Google Analytics account. Returns property names, display names, and property IDs.",
3546
3687
  inputSchema: inputSchema19,
3547
3688
  outputSchema: outputSchema19,
3548
- async execute({ connectionId, sql }, connections) {
3689
+ async execute({ connectionId, accountName }, connections, config) {
3549
3690
  const connection = connections.find((c) => c.id === connectionId);
3550
3691
  if (!connection) {
3551
3692
  return {
@@ -3554,128 +3695,352 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
3554
3695
  };
3555
3696
  }
3556
3697
  console.log(
3557
- `[connector-query] squadbase-db/${connection.name}: ${sql}`
3698
+ `[connector-request] google-analytics-oauth/${connection.name}: listProperties for ${accountName}`
3558
3699
  );
3559
- let connectionUrl;
3560
3700
  try {
3561
- const { Pool } = await import("pg");
3562
- connectionUrl = parameters19.connectionUrl.getValue(connection);
3563
- const pool = new Pool({
3564
- connectionString: connectionUrl,
3565
- ssl: { rejectUnauthorized: false },
3566
- connectionTimeoutMillis: CONNECT_TIMEOUT_MS3,
3567
- statement_timeout: STATEMENT_TIMEOUT_MS2
3568
- });
3701
+ const token = await getProxyToken7(config.oauthProxy);
3702
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3703
+ const controller = new AbortController();
3704
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS9);
3569
3705
  try {
3570
- const result = await pool.query(sql);
3571
- const rows = result.rows;
3572
- const truncated = rows.length > MAX_ROWS10;
3573
- return {
3574
- success: true,
3575
- rowCount: Math.min(rows.length, MAX_ROWS10),
3576
- truncated,
3577
- rows: rows.slice(0, MAX_ROWS10)
3578
- };
3706
+ const filter = encodeURIComponent(`parent:${accountName}`);
3707
+ const response = await fetch(proxyUrl, {
3708
+ method: "POST",
3709
+ headers: {
3710
+ "Content-Type": "application/json",
3711
+ Authorization: `Bearer ${token}`
3712
+ },
3713
+ body: JSON.stringify({
3714
+ url: `${ADMIN_BASE_URL2}properties?filter=${filter}`,
3715
+ method: "GET"
3716
+ }),
3717
+ signal: controller.signal
3718
+ });
3719
+ const data = await response.json();
3720
+ if (!response.ok) {
3721
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
3722
+ return { success: false, error: errorMessage };
3723
+ }
3724
+ const properties = (data.properties ?? []).map((p) => {
3725
+ const name = p.name ?? "";
3726
+ const propertyId = name.replace(/^properties\//, "");
3727
+ return {
3728
+ name,
3729
+ displayName: p.displayName ?? "",
3730
+ propertyId
3731
+ };
3732
+ });
3733
+ return { success: true, properties };
3579
3734
  } finally {
3580
- await pool.end();
3735
+ clearTimeout(timeout);
3581
3736
  }
3582
3737
  } catch (err) {
3583
- let msg = err instanceof Error ? err.message : String(err);
3584
- if (connectionUrl) {
3585
- msg = msg.replaceAll(connectionUrl, "***");
3586
- }
3738
+ const msg = err instanceof Error ? err.message : String(err);
3587
3739
  return { success: false, error: msg };
3588
3740
  }
3589
3741
  }
3590
3742
  });
3591
3743
 
3592
- // src/connectors/squadbase-db/index.ts
3593
- var tools15 = { executeQuery: executeQueryTool10 };
3594
- var squadbaseDbConnector = new ConnectorPlugin({
3595
- slug: "squadbase-db",
3596
- authType: null,
3597
- name: "Squadbase DB",
3598
- description: "Connect to Squadbase DB (PostgreSQL).",
3599
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/25y0XqMxIufeD3egWH3bEl/659b4ade405890654cfaf91c03a4b458/icon.svg",
3600
- parameters: parameters19,
3601
- releaseFlag: { dev1: true, dev2: true, prod: true },
3602
- systemPrompt: `## Squadbase DB SQL Notes
3603
- - Uses PostgreSQL based SQL syntax
3604
- - Schema exploration:
3605
- - List tables: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
3606
- - List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
3607
- - Always include LIMIT in queries`,
3608
- tools: tools15,
3609
- async checkConnection(params, _config) {
3610
- const { Pool } = await import("pg");
3611
- const pool = new Pool({
3612
- connectionString: params[parameters19.connectionUrl.slug],
3613
- ssl: { rejectUnauthorized: false },
3614
- connectionTimeoutMillis: 1e4
3615
- });
3616
- try {
3617
- await pool.query("SELECT 1");
3618
- return { success: true };
3619
- } catch (error) {
3620
- return { success: false, error: error instanceof Error ? error.message : String(error) };
3621
- } finally {
3622
- await pool.end();
3744
+ // src/connectors/google-analytics-oauth/setup.ts
3745
+ var listAccountsToolName = `google-analytics-oauth_${listAccountsTool.name}`;
3746
+ var listPropertiesToolName = `google-analytics-oauth_${listPropertiesTool.name}`;
3747
+ var googleAnalyticsOauthSetup = new ConnectorSetup({
3748
+ ja: `## Google Analytics \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
3749
+
3750
+ \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Analytics (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
3751
+
3752
+ ### \u624B\u9806
3753
+
3754
+ #### \u30B9\u30C6\u30C3\u30D71: \u30A2\u30AB\u30A6\u30F3\u30C8\u9078\u629E
3755
+ 1. \`${listAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Analytics\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
3756
+ 2. \u300C\u4F7F\u7528\u3059\u308B\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D\u3068\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u305F\u4E0A\u3067\u3001\`askUserQuestion\` \u3092\u547C\u3073\u51FA\u3059:
3757
+ - \`options\`: \u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (name)\` \u306E\u5F62\u5F0F
3758
+
3759
+ #### \u30B9\u30C6\u30C3\u30D72: \u30D7\u30ED\u30D1\u30C6\u30A3\u9078\u629E
3760
+ 1. \`${listPropertiesToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
3761
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
3762
+ - \`parameterSlug\`: \`"property-id"\`
3763
+ - \`options\`: \u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (id: \u30D7\u30ED\u30D1\u30C6\u30A3ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30D1\u30C6\u30A3ID
3764
+ 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D73\u306B\u9032\u3080
3765
+
3766
+ #### \u30B9\u30C6\u30C3\u30D73: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
3767
+ 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
3768
+ - \`property\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E\u8868\u793A\u540D
3769
+ - \`propertyId\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3ID
3770
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
3771
+
3772
+ ### \u91CD\u8981\u306A\u5236\u7D04
3773
+ - **\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
3774
+
3775
+ ### \u5B9F\u884C\u65B9\u91DD
3776
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
3777
+ - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
3778
+ en: `## Google Analytics Setup Instructions
3779
+
3780
+ Follow these steps to set up the Google Analytics (OAuth) connection.
3781
+
3782
+ ### Steps
3783
+
3784
+ #### Step 1: Account Selection
3785
+ 1. Call \`${listAccountsToolName}\` to get the list of Google Analytics accounts accessible with the OAuth credentials
3786
+ 2. Tell the user "Please select an account.", then call \`askUserQuestion\`:
3787
+ - \`options\`: The account list. Each option's \`label\` should be \`Display Name (name)\`
3788
+
3789
+ #### Step 2: Property Selection
3790
+ 1. Call \`${listPropertiesToolName}\` to get the list of properties for the selected account
3791
+ 2. Call \`updateConnectionParameters\`:
3792
+ - \`parameterSlug\`: \`"property-id"\`
3793
+ - \`options\`: The property list. Each option's \`label\` should be \`Display Name (id: propertyId)\`, \`value\` should be the property ID
3794
+ 3. The \`label\` of the user's selected property will arrive as a message. Proceed to Step 3
3795
+
3796
+ #### Step 3: Complete Setup
3797
+ 1. Call \`updateConnectionContext\`:
3798
+ - \`property\`: The selected property's display name
3799
+ - \`propertyId\`: The selected property ID
3800
+ - \`note\`: Brief description of the setup
3801
+
3802
+ ### Important Constraints
3803
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
3804
+
3805
+ ### Execution Policy
3806
+ - Write only 1 sentence between tool calls, then immediately call the next tool
3807
+ - Skip unnecessary explanations and proceed efficiently`
3808
+ });
3809
+
3810
+ // src/connectors/google-analytics-oauth/tools/request.ts
3811
+ import { z as z20 } from "zod";
3812
+ var BASE_URL5 = "https://analyticsdata.googleapis.com/v1beta/";
3813
+ var REQUEST_TIMEOUT_MS10 = 6e4;
3814
+ var cachedToken8 = null;
3815
+ async function getProxyToken8(config) {
3816
+ if (cachedToken8 && cachedToken8.expiresAt > Date.now() + 6e4) {
3817
+ return cachedToken8.token;
3818
+ }
3819
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3820
+ const res = await fetch(url, {
3821
+ method: "POST",
3822
+ headers: {
3823
+ "Content-Type": "application/json",
3824
+ "x-api-key": config.appApiKey,
3825
+ "project-id": config.projectId
3826
+ },
3827
+ body: JSON.stringify({
3828
+ sandboxId: config.sandboxId,
3829
+ issuedBy: "coding-agent"
3830
+ })
3831
+ });
3832
+ if (!res.ok) {
3833
+ const errorText = await res.text().catch(() => res.statusText);
3834
+ throw new Error(
3835
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3836
+ );
3837
+ }
3838
+ const data = await res.json();
3839
+ cachedToken8 = {
3840
+ token: data.token,
3841
+ expiresAt: new Date(data.expiresAt).getTime()
3842
+ };
3843
+ return data.token;
3844
+ }
3845
+ var inputSchema20 = z20.object({
3846
+ toolUseIntent: z20.string().optional().describe(
3847
+ "Brief description of what you intend to accomplish with this tool call"
3848
+ ),
3849
+ connectionId: z20.string().describe("ID of the Google Analytics OAuth connection to use"),
3850
+ method: z20.enum(["GET", "POST"]).describe("HTTP method"),
3851
+ path: z20.string().describe(
3852
+ "API path appended to https://analyticsdata.googleapis.com/v1beta/ (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."
3853
+ ),
3854
+ body: z20.record(z20.string(), z20.unknown()).optional().describe("POST request body (JSON)")
3855
+ });
3856
+ var outputSchema20 = z20.discriminatedUnion("success", [
3857
+ z20.object({
3858
+ success: z20.literal(true),
3859
+ status: z20.number(),
3860
+ data: z20.record(z20.string(), z20.unknown())
3861
+ }),
3862
+ z20.object({
3863
+ success: z20.literal(false),
3864
+ error: z20.string()
3865
+ })
3866
+ ]);
3867
+ var requestTool4 = new ConnectorTool({
3868
+ name: "request",
3869
+ description: `Send authenticated requests to the Google Analytics Data API v1beta.
3870
+ Authentication is handled automatically via OAuth proxy.
3871
+ {propertyId} in the path is automatically replaced with the connection's property ID.`,
3872
+ inputSchema: inputSchema20,
3873
+ outputSchema: outputSchema20,
3874
+ async execute({ connectionId, method, path, body }, connections, config) {
3875
+ const connection = connections.find((c) => c.id === connectionId);
3876
+ if (!connection) {
3877
+ return {
3878
+ success: false,
3879
+ error: `Connection ${connectionId} not found`
3880
+ };
3623
3881
  }
3624
- },
3625
- async query(params, sql, namedParams) {
3626
- const { Pool } = await import("pg");
3627
- const { text, values } = buildPositionalParams(sql, namedParams);
3628
- const pool = new Pool({
3629
- connectionString: params[parameters19.connectionUrl.slug],
3630
- ssl: { rejectUnauthorized: false },
3631
- connectionTimeoutMillis: 1e4,
3632
- statement_timeout: 6e4
3633
- });
3882
+ console.log(
3883
+ `[connector-request] google-analytics-oauth/${connection.name}: ${method} ${path}`
3884
+ );
3634
3885
  try {
3635
- const result = await pool.query(text, values);
3636
- return { rows: result.rows };
3637
- } finally {
3638
- await pool.end();
3886
+ const propertyId = parameters4.propertyId.tryGetValue(connection);
3887
+ const resolvedPath = propertyId ? path.replace(/\{propertyId\}/g, propertyId) : path;
3888
+ const url = `${BASE_URL5}${resolvedPath}`;
3889
+ const token = await getProxyToken8(config.oauthProxy);
3890
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3891
+ const controller = new AbortController();
3892
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS10);
3893
+ try {
3894
+ const response = await fetch(proxyUrl, {
3895
+ method: "POST",
3896
+ headers: {
3897
+ "Content-Type": "application/json",
3898
+ Authorization: `Bearer ${token}`
3899
+ },
3900
+ body: JSON.stringify({
3901
+ url,
3902
+ method,
3903
+ ...method === "POST" && body ? {
3904
+ headers: { "Content-Type": "application/json" },
3905
+ body: JSON.stringify(body)
3906
+ } : {}
3907
+ }),
3908
+ signal: controller.signal
3909
+ });
3910
+ const data = await response.json();
3911
+ if (!response.ok) {
3912
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
3913
+ return { success: false, error: errorMessage };
3914
+ }
3915
+ return { success: true, status: response.status, data };
3916
+ } finally {
3917
+ clearTimeout(timeout);
3918
+ }
3919
+ } catch (err) {
3920
+ const msg = err instanceof Error ? err.message : String(err);
3921
+ return { success: false, error: msg };
3639
3922
  }
3640
3923
  }
3641
3924
  });
3642
3925
 
3643
- // src/connectors/openai/index.ts
3644
- var tools16 = {};
3645
- var openaiConnector = new ConnectorPlugin({
3646
- slug: "openai",
3647
- authType: null,
3648
- name: "OpenAI",
3649
- description: "Connect to OpenAI for AI model inference, embeddings, and image generation.",
3650
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/53XJtCgUlW10x6i1X8xpxM/0bfd634069f1d74241296543cb20427a/openai.svg",
3651
- parameters: parameters6,
3652
- releaseFlag: { dev1: true, dev2: true, prod: true },
3653
- systemPrompt: `## OpenAI SDK (TypeScript handler)
3654
- Use the OpenAI connector via the SDK in TypeScript handlers:
3926
+ // src/connectors/google-analytics-oauth/index.ts
3927
+ var tools13 = {
3928
+ request: requestTool4,
3929
+ listAccounts: listAccountsTool,
3930
+ listProperties: listPropertiesTool
3931
+ };
3932
+ var googleAnalyticsOauthConnector = new ConnectorPlugin({
3933
+ slug: "google-analytics",
3934
+ authType: AUTH_TYPES.OAUTH,
3935
+ name: "Google Analytics (OAuth)",
3936
+ description: "Connect to Google Analytics for web analytics and reporting using OAuth.",
3937
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
3938
+ parameters: parameters4,
3939
+ releaseFlag: { dev1: true, dev2: false, prod: false },
3940
+ setup: googleAnalyticsOauthSetup,
3941
+ proxyPolicy: {
3942
+ allowlist: [
3943
+ {
3944
+ host: "analyticsdata.googleapis.com",
3945
+ methods: ["GET", "POST"]
3946
+ },
3947
+ {
3948
+ host: "analyticsadmin.googleapis.com",
3949
+ methods: ["GET"]
3950
+ }
3951
+ ]
3952
+ },
3953
+ systemPrompt: `## Google Analytics Data API (OAuth, Read-Only)
3954
+ - Call the GA4 Data API using the authenticated request tool
3955
+ - {propertyId} in the path is automatically replaced
3956
+
3957
+ ### Get Metadata (Check available dimensions and metrics)
3958
+ - GET properties/{propertyId}/metadata
3959
+
3960
+ ### Get Report
3961
+ - POST properties/{propertyId}:runReport
3962
+ - Body example:
3963
+ {
3964
+ "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
3965
+ "dimensions": [{"name": "date"}],
3966
+ "metrics": [{"name": "activeUsers"}],
3967
+ "limit": 100
3968
+ }
3969
+
3970
+ ### Common Dimensions
3971
+ date, country, city, deviceCategory, browser, pagePath, pageTitle,
3972
+ sessionSource, sessionMedium, eventName
3973
+
3974
+ ### Common Metrics
3975
+ activeUsers, sessions, screenPageViews, bounceRate,
3976
+ averageSessionDuration, conversions, totalRevenue
3977
+
3978
+ ### Date Specification
3979
+ - Absolute: "2024-01-01"
3980
+ - Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
3981
+
3982
+ ## Google Analytics SDK (TypeScript handler)
3655
3983
 
3656
3984
  \`\`\`ts
3657
- import { connection } from "@squadbase/vite-server/connectors/openai";
3658
- import OpenAI from "openai";
3985
+ import { connection } from "@squadbase/vite-server/connectors/google-analytics-oauth";
3659
3986
 
3660
- const { apiKey } = connection("<connectionId>");
3661
- const client = new OpenAI({ apiKey });
3987
+ const ga = connection("<connectionId>");
3662
3988
 
3663
- const response = await client.chat.completions.create({
3664
- model: "gpt-4o",
3665
- messages: [{ role: "user", content: "Hello" }],
3989
+ // Authenticated fetch (returns standard Response)
3990
+ const res = await ga.request("properties/{propertyId}:runReport", {
3991
+ method: "POST",
3992
+ body: JSON.stringify({ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], metrics: [{ name: "activeUsers" }] }),
3993
+ });
3994
+ const data = await res.json();
3995
+
3996
+ // Convenience methods
3997
+ const { rows, rowCount } = await ga.runReport({
3998
+ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
3999
+ dimensions: [{ name: "date" }],
4000
+ metrics: [{ name: "activeUsers" }, { name: "sessions" }],
4001
+ limit: 100,
4002
+ });
4003
+ const metadata = await ga.getMetadata();
4004
+ const realtime = await ga.runRealtimeReport({
4005
+ metrics: [{ name: "activeUsers" }],
4006
+ dimensions: [{ name: "country" }],
3666
4007
  });
3667
4008
  \`\`\``,
3668
- tools: tools16
4009
+ tools: tools13,
4010
+ async checkConnection(params, config) {
4011
+ const { proxyFetch } = config;
4012
+ const propertyId = params[parameters4.propertyId.slug];
4013
+ if (!propertyId) {
4014
+ return { success: true };
4015
+ }
4016
+ const url = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}/metadata`;
4017
+ try {
4018
+ const res = await proxyFetch(url, { method: "GET" });
4019
+ if (!res.ok) {
4020
+ const errorText = await res.text().catch(() => res.statusText);
4021
+ return {
4022
+ success: false,
4023
+ error: `Google Analytics API failed: HTTP ${res.status} ${errorText}`
4024
+ };
4025
+ }
4026
+ return { success: true };
4027
+ } catch (error) {
4028
+ return {
4029
+ success: false,
4030
+ error: error instanceof Error ? error.message : String(error)
4031
+ };
4032
+ }
4033
+ }
3669
4034
  });
3670
4035
 
3671
4036
  // src/connectors/google-sheets-oauth/tools/request.ts
3672
- import { z as z20 } from "zod";
3673
- var BASE_URL4 = "https://sheets.googleapis.com/v4/spreadsheets";
3674
- var REQUEST_TIMEOUT_MS9 = 6e4;
3675
- var cachedToken4 = null;
3676
- async function getProxyToken4(config) {
3677
- if (cachedToken4 && cachedToken4.expiresAt > Date.now() + 6e4) {
3678
- return cachedToken4.token;
4037
+ import { z as z21 } from "zod";
4038
+ var BASE_URL6 = "https://sheets.googleapis.com/v4/spreadsheets";
4039
+ var REQUEST_TIMEOUT_MS11 = 6e4;
4040
+ var cachedToken9 = null;
4041
+ async function getProxyToken9(config) {
4042
+ if (cachedToken9 && cachedToken9.expiresAt > Date.now() + 6e4) {
4043
+ return cachedToken9.token;
3679
4044
  }
3680
4045
  const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3681
4046
  const res = await fetch(url, {
@@ -3697,41 +4062,41 @@ async function getProxyToken4(config) {
3697
4062
  );
3698
4063
  }
3699
4064
  const data = await res.json();
3700
- cachedToken4 = {
4065
+ cachedToken9 = {
3701
4066
  token: data.token,
3702
4067
  expiresAt: new Date(data.expiresAt).getTime()
3703
4068
  };
3704
4069
  return data.token;
3705
4070
  }
3706
- var inputSchema20 = z20.object({
3707
- toolUseIntent: z20.string().optional().describe(
4071
+ var inputSchema21 = z21.object({
4072
+ toolUseIntent: z21.string().optional().describe(
3708
4073
  "Brief description of what you intend to accomplish with this tool call"
3709
4074
  ),
3710
- connectionId: z20.string().describe("ID of the Google Sheets OAuth connection to use"),
3711
- method: z20.enum(["GET"]).describe("HTTP method (read-only, GET only)"),
3712
- path: z20.string().describe(
4075
+ connectionId: z21.string().describe("ID of the Google Sheets OAuth connection to use"),
4076
+ method: z21.enum(["GET"]).describe("HTTP method (read-only, GET only)"),
4077
+ path: z21.string().describe(
3713
4078
  "API path appended to https://sheets.googleapis.com/v4/spreadsheets (e.g., '/{spreadsheetId}', '/{spreadsheetId}/values/Sheet1!A1:D10'). {spreadsheetId} is automatically replaced if a default is configured."
3714
4079
  ),
3715
- queryParams: z20.record(z20.string(), z20.string()).optional().describe("Query parameters to append to the URL")
4080
+ queryParams: z21.record(z21.string(), z21.string()).optional().describe("Query parameters to append to the URL")
3716
4081
  });
3717
- var outputSchema20 = z20.discriminatedUnion("success", [
3718
- z20.object({
3719
- success: z20.literal(true),
3720
- status: z20.number(),
3721
- data: z20.record(z20.string(), z20.unknown())
4082
+ var outputSchema21 = z21.discriminatedUnion("success", [
4083
+ z21.object({
4084
+ success: z21.literal(true),
4085
+ status: z21.number(),
4086
+ data: z21.record(z21.string(), z21.unknown())
3722
4087
  }),
3723
- z20.object({
3724
- success: z20.literal(false),
3725
- error: z20.string()
4088
+ z21.object({
4089
+ success: z21.literal(false),
4090
+ error: z21.string()
3726
4091
  })
3727
4092
  ]);
3728
- var requestTool6 = new ConnectorTool({
4093
+ var requestTool5 = new ConnectorTool({
3729
4094
  name: "request",
3730
4095
  description: `Send authenticated GET requests to the Google Sheets API v4.
3731
4096
  Authentication is handled automatically via OAuth proxy.
3732
4097
  {spreadsheetId} in the path is automatically replaced with the connection's default spreadsheet ID if configured.`,
3733
- inputSchema: inputSchema20,
3734
- outputSchema: outputSchema20,
4098
+ inputSchema: inputSchema21,
4099
+ outputSchema: outputSchema21,
3735
4100
  async execute({ connectionId, method, path, queryParams }, connections, config) {
3736
4101
  const connection = connections.find((c) => c.id === connectionId);
3737
4102
  if (!connection) {
@@ -3744,17 +4109,17 @@ Authentication is handled automatically via OAuth proxy.
3744
4109
  `[connector-request] google-sheets-oauth/${connection.name}: ${method} ${path}`
3745
4110
  );
3746
4111
  try {
3747
- const spreadsheetId = parameters7.spreadsheetId.tryGetValue(connection);
4112
+ const spreadsheetId = parameters5.spreadsheetId.tryGetValue(connection);
3748
4113
  const resolvedPath = spreadsheetId ? path.replace(/\{spreadsheetId\}/g, spreadsheetId) : path;
3749
- let url = `${BASE_URL4}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
4114
+ let url = `${BASE_URL6}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
3750
4115
  if (queryParams) {
3751
4116
  const searchParams = new URLSearchParams(queryParams);
3752
4117
  url += `?${searchParams.toString()}`;
3753
4118
  }
3754
- const token = await getProxyToken4(config.oauthProxy);
4119
+ const token = await getProxyToken9(config.oauthProxy);
3755
4120
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3756
4121
  const controller = new AbortController();
3757
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS9);
4122
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS11);
3758
4123
  try {
3759
4124
  const response = await fetch(proxyUrl, {
3760
4125
  method: "POST",
@@ -3785,7 +4150,7 @@ Authentication is handled automatically via OAuth proxy.
3785
4150
  });
3786
4151
 
3787
4152
  // src/connectors/google-sheets-oauth/setup.ts
3788
- var requestToolName = `google-sheets-oauth_${requestTool6.name}`;
4153
+ var requestToolName = `google-sheets-oauth_${requestTool5.name}`;
3789
4154
  var googleSheetsSetup = new ConnectorSetup({
3790
4155
  ja: `## Google Sheets \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
3791
4156
 
@@ -3852,15 +4217,15 @@ Follow these steps to set up the Google Sheets connection.
3852
4217
  });
3853
4218
 
3854
4219
  // src/connectors/google-sheets-oauth/index.ts
3855
- var tools17 = { request: requestTool6 };
4220
+ var tools14 = { request: requestTool5 };
3856
4221
  var googleSheetsOauthConnector = new ConnectorPlugin({
3857
4222
  slug: "google-sheets",
3858
4223
  authType: AUTH_TYPES.OAUTH,
3859
4224
  name: "Google Sheets (OAuth)",
3860
4225
  description: "Connect to Google Sheets for spreadsheet data access using OAuth.",
3861
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5lJMKlzv5lYbsBQbXGqacn/f56205b34e55c37d1dc69b34dcbbc379/google-sheets.svg",
3862
- parameters: parameters7,
3863
- releaseFlag: { dev1: true, dev2: true, prod: false },
4226
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1UPQuggyiZmbb26CuaSr2h/032770e8739b183fa00b7625f024e536/google-sheets.svg",
4227
+ parameters: parameters5,
4228
+ releaseFlag: { dev1: true, dev2: false, prod: false },
3864
4229
  setup: googleSheetsSetup,
3865
4230
  proxyPolicy: {
3866
4231
  allowlist: [
@@ -3910,253 +4275,95 @@ console.log(values.values); // 2D array
3910
4275
  const batch = await sheets.batchGetValues(["Sheet1!A1:B5", "Sheet2!A1:C3"]);
3911
4276
  batch.valueRanges.forEach(vr => console.log(vr.range, vr.values));
3912
4277
  \`\`\``,
3913
- tools: tools17,
4278
+ tools: tools14,
3914
4279
  async checkConnection(params, config) {
3915
4280
  const { proxyFetch } = config;
3916
- const spreadsheetId = params[parameters7.spreadsheetId.slug];
4281
+ const spreadsheetId = params[parameters5.spreadsheetId.slug];
3917
4282
  if (!spreadsheetId) {
3918
- return { success: true };
3919
- }
3920
- const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}?fields=spreadsheetId,properties.title`;
3921
- try {
3922
- const res = await proxyFetch(url, { method: "GET" });
3923
- if (!res.ok) {
3924
- const errorText = await res.text().catch(() => res.statusText);
3925
- return {
3926
- success: false,
3927
- error: `Google Sheets API failed: HTTP ${res.status} ${errorText}`
3928
- };
3929
- }
3930
- return { success: true };
3931
- } catch (error) {
3932
- return {
3933
- success: false,
3934
- error: error instanceof Error ? error.message : String(error)
3935
- };
3936
- }
3937
- }
3938
- });
3939
-
3940
- // src/connectors/google-analytics-oauth/tools/list-accounts.ts
3941
- import { z as z21 } from "zod";
3942
- var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta/";
3943
- var REQUEST_TIMEOUT_MS10 = 6e4;
3944
- var cachedToken5 = null;
3945
- async function getProxyToken5(config) {
3946
- if (cachedToken5 && cachedToken5.expiresAt > Date.now() + 6e4) {
3947
- return cachedToken5.token;
3948
- }
3949
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3950
- const res = await fetch(url, {
3951
- method: "POST",
3952
- headers: {
3953
- "Content-Type": "application/json",
3954
- "x-api-key": config.appApiKey,
3955
- "project-id": config.projectId
3956
- },
3957
- body: JSON.stringify({
3958
- sandboxId: config.sandboxId,
3959
- issuedBy: "coding-agent"
3960
- })
3961
- });
3962
- if (!res.ok) {
3963
- const errorText = await res.text().catch(() => res.statusText);
3964
- throw new Error(
3965
- `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3966
- );
3967
- }
3968
- const data = await res.json();
3969
- cachedToken5 = {
3970
- token: data.token,
3971
- expiresAt: new Date(data.expiresAt).getTime()
3972
- };
3973
- return data.token;
3974
- }
3975
- var inputSchema21 = z21.object({
3976
- toolUseIntent: z21.string().optional().describe(
3977
- "Brief description of what you intend to accomplish with this tool call"
3978
- ),
3979
- connectionId: z21.string().describe("ID of the Google Analytics OAuth connection to use")
3980
- });
3981
- var outputSchema21 = z21.discriminatedUnion("success", [
3982
- z21.object({
3983
- success: z21.literal(true),
3984
- accounts: z21.array(
3985
- z21.object({
3986
- name: z21.string(),
3987
- displayName: z21.string()
3988
- })
3989
- )
3990
- }),
3991
- z21.object({
3992
- success: z21.literal(false),
3993
- error: z21.string()
3994
- })
3995
- ]);
3996
- var listAccountsTool = new ConnectorTool({
3997
- name: "listAccounts",
3998
- description: "List Google Analytics accounts accessible with the current OAuth credentials. Returns account names and display names.",
3999
- inputSchema: inputSchema21,
4000
- outputSchema: outputSchema21,
4001
- async execute({ connectionId }, connections, config) {
4002
- const connection = connections.find((c) => c.id === connectionId);
4003
- if (!connection) {
4004
- return {
4005
- success: false,
4006
- error: `Connection ${connectionId} not found`
4007
- };
4283
+ return { success: true };
4008
4284
  }
4009
- console.log(
4010
- `[connector-request] google-analytics-oauth/${connection.name}: listAccounts`
4011
- );
4285
+ const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}?fields=spreadsheetId,properties.title`;
4012
4286
  try {
4013
- const token = await getProxyToken5(config.oauthProxy);
4014
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4015
- const controller = new AbortController();
4016
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS10);
4017
- try {
4018
- const response = await fetch(proxyUrl, {
4019
- method: "POST",
4020
- headers: {
4021
- "Content-Type": "application/json",
4022
- Authorization: `Bearer ${token}`
4023
- },
4024
- body: JSON.stringify({
4025
- url: `${ADMIN_BASE_URL}accounts`,
4026
- method: "GET"
4027
- }),
4028
- signal: controller.signal
4029
- });
4030
- const data = await response.json();
4031
- if (!response.ok) {
4032
- const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4033
- return { success: false, error: errorMessage };
4034
- }
4035
- const accounts = (data.accounts ?? []).map((a) => ({
4036
- name: a.name ?? "",
4037
- displayName: a.displayName ?? ""
4038
- }));
4039
- return { success: true, accounts };
4040
- } finally {
4041
- clearTimeout(timeout);
4287
+ const res = await proxyFetch(url, { method: "GET" });
4288
+ if (!res.ok) {
4289
+ const errorText = await res.text().catch(() => res.statusText);
4290
+ return {
4291
+ success: false,
4292
+ error: `Google Sheets API failed: HTTP ${res.status} ${errorText}`
4293
+ };
4042
4294
  }
4043
- } catch (err) {
4044
- const msg = err instanceof Error ? err.message : String(err);
4045
- return { success: false, error: msg };
4295
+ return { success: true };
4296
+ } catch (error) {
4297
+ return {
4298
+ success: false,
4299
+ error: error instanceof Error ? error.message : String(error)
4300
+ };
4046
4301
  }
4047
4302
  }
4048
4303
  });
4049
4304
 
4050
- // src/connectors/google-analytics-oauth/tools/list-properties.ts
4305
+ // src/connectors/kintone/tools/request.ts
4051
4306
  import { z as z22 } from "zod";
4052
- var ADMIN_BASE_URL2 = "https://analyticsadmin.googleapis.com/v1beta/";
4053
- var REQUEST_TIMEOUT_MS11 = 6e4;
4054
- var cachedToken6 = null;
4055
- async function getProxyToken6(config) {
4056
- if (cachedToken6 && cachedToken6.expiresAt > Date.now() + 6e4) {
4057
- return cachedToken6.token;
4058
- }
4059
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4060
- const res = await fetch(url, {
4061
- method: "POST",
4062
- headers: {
4063
- "Content-Type": "application/json",
4064
- "x-api-key": config.appApiKey,
4065
- "project-id": config.projectId
4066
- },
4067
- body: JSON.stringify({
4068
- sandboxId: config.sandboxId,
4069
- issuedBy: "coding-agent"
4070
- })
4071
- });
4072
- if (!res.ok) {
4073
- const errorText = await res.text().catch(() => res.statusText);
4074
- throw new Error(
4075
- `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4076
- );
4077
- }
4078
- const data = await res.json();
4079
- cachedToken6 = {
4080
- token: data.token,
4081
- expiresAt: new Date(data.expiresAt).getTime()
4082
- };
4083
- return data.token;
4084
- }
4307
+ var REQUEST_TIMEOUT_MS12 = 6e4;
4085
4308
  var inputSchema22 = z22.object({
4086
- toolUseIntent: z22.string().optional().describe(
4087
- "Brief description of what you intend to accomplish with this tool call"
4088
- ),
4089
- connectionId: z22.string().describe("ID of the Google Analytics OAuth connection to use"),
4090
- accountName: z22.string().describe(
4091
- "The account resource name (e.g., 'accounts/123456'). Obtained from the listAccounts tool."
4092
- )
4309
+ toolUseIntent: z22.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
4310
+ connectionId: z22.string().describe("ID of the kintone connection to use"),
4311
+ method: z22.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP method"),
4312
+ path: z22.string().describe("API path (e.g., 'apps.json', 'records.json?app=1&query=...')"),
4313
+ body: z22.record(z22.string(), z22.unknown()).optional().describe("Request body (JSON)")
4093
4314
  });
4094
4315
  var outputSchema22 = z22.discriminatedUnion("success", [
4095
4316
  z22.object({
4096
4317
  success: z22.literal(true),
4097
- properties: z22.array(
4098
- z22.object({
4099
- name: z22.string(),
4100
- displayName: z22.string(),
4101
- propertyId: z22.string()
4102
- })
4103
- )
4318
+ status: z22.number(),
4319
+ data: z22.record(z22.string(), z22.unknown())
4104
4320
  }),
4105
4321
  z22.object({
4106
4322
  success: z22.literal(false),
4107
4323
  error: z22.string()
4108
4324
  })
4109
4325
  ]);
4110
- var listPropertiesTool = new ConnectorTool({
4111
- name: "listProperties",
4112
- description: "List GA4 properties for a given Google Analytics account. Returns property names, display names, and property IDs.",
4326
+ var requestTool6 = new ConnectorTool({
4327
+ name: "request",
4328
+ description: `Send authenticated requests to the kintone REST API.
4329
+ Authentication is handled automatically using username and password.`,
4113
4330
  inputSchema: inputSchema22,
4114
4331
  outputSchema: outputSchema22,
4115
- async execute({ connectionId, accountName }, connections, config) {
4332
+ async execute({ connectionId, method, path, body }, connections) {
4116
4333
  const connection = connections.find((c) => c.id === connectionId);
4117
4334
  if (!connection) {
4118
- return {
4119
- success: false,
4120
- error: `Connection ${connectionId} not found`
4121
- };
4335
+ return { success: false, error: `Connection ${connectionId} not found` };
4122
4336
  }
4123
- console.log(
4124
- `[connector-request] google-analytics-oauth/${connection.name}: listProperties for ${accountName}`
4125
- );
4337
+ console.log(`[connector-request] kintone/${connection.name}: ${method} ${path}`);
4126
4338
  try {
4127
- const token = await getProxyToken6(config.oauthProxy);
4128
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4339
+ const baseUrl = parameters6.baseUrl.getValue(connection);
4340
+ const username = parameters6.username.getValue(connection);
4341
+ const password = parameters6.password.getValue(connection);
4342
+ const authToken = Buffer.from(`${username}:${password}`).toString("base64");
4343
+ const url = `${baseUrl.replace(/\/+$/, "")}/k/v1/${path}`;
4129
4344
  const controller = new AbortController();
4130
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS11);
4345
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS12);
4131
4346
  try {
4132
- const filter = encodeURIComponent(`parent:${accountName}`);
4133
- const response = await fetch(proxyUrl, {
4134
- method: "POST",
4135
- headers: {
4136
- "Content-Type": "application/json",
4137
- Authorization: `Bearer ${token}`
4138
- },
4139
- body: JSON.stringify({
4140
- url: `${ADMIN_BASE_URL2}properties?filter=${filter}`,
4141
- method: "GET"
4142
- }),
4347
+ const headers = {
4348
+ "X-Cybozu-Authorization": authToken
4349
+ };
4350
+ if (body) {
4351
+ headers["Content-Type"] = "application/json";
4352
+ }
4353
+ const response = await fetch(url, {
4354
+ method,
4355
+ headers,
4356
+ body: body ? JSON.stringify(body) : void 0,
4143
4357
  signal: controller.signal
4144
4358
  });
4145
4359
  const data = await response.json();
4146
4360
  if (!response.ok) {
4147
- const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4148
- return { success: false, error: errorMessage };
4149
- }
4150
- const properties = (data.properties ?? []).map((p) => {
4151
- const name = p.name ?? "";
4152
- const propertyId = name.replace(/^properties\//, "");
4153
4361
  return {
4154
- name,
4155
- displayName: p.displayName ?? "",
4156
- propertyId
4362
+ success: false,
4363
+ error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
4157
4364
  };
4158
- });
4159
- return { success: true, properties };
4365
+ }
4366
+ return { success: true, status: response.status, data };
4160
4367
  } finally {
4161
4368
  clearTimeout(timeout);
4162
4369
  }
@@ -4167,117 +4374,80 @@ var listPropertiesTool = new ConnectorTool({
4167
4374
  }
4168
4375
  });
4169
4376
 
4170
- // src/connectors/google-analytics-oauth/setup.ts
4171
- var listAccountsToolName = `google-analytics-oauth_${listAccountsTool.name}`;
4172
- var listPropertiesToolName = `google-analytics-oauth_${listPropertiesTool.name}`;
4173
- var googleAnalyticsOauthSetup = new ConnectorSetup({
4174
- ja: `## Google Analytics \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
4175
-
4176
- \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Analytics (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
4177
-
4178
- ### \u624B\u9806
4179
-
4180
- #### \u30B9\u30C6\u30C3\u30D71: \u30A2\u30AB\u30A6\u30F3\u30C8\u9078\u629E
4181
- 1. \`${listAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Analytics\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4182
- 2. \u300C\u4F7F\u7528\u3059\u308B\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D\u3068\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u305F\u4E0A\u3067\u3001\`askUserQuestion\` \u3092\u547C\u3073\u51FA\u3059:
4183
- - \`options\`: \u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (name)\` \u306E\u5F62\u5F0F
4377
+ // src/connectors/kintone/index.ts
4378
+ var tools15 = { request: requestTool6 };
4379
+ var kintoneConnector = new ConnectorPlugin({
4380
+ slug: "kintone",
4381
+ authType: null,
4382
+ name: "kintone",
4383
+ description: "Connect to kintone for business application data retrieval and analytics.",
4384
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/76nPGMJFZkMFE3UQNo2JFy/e71dc5f5d5cec1306ce0e17aafbfd9f0/kintone.png",
4385
+ parameters: parameters6,
4386
+ releaseFlag: { dev1: true, dev2: true, prod: true },
4387
+ systemPrompt: `## kintone REST API
4388
+ - Call the kintone REST API using the authenticated request tool
4389
+ - The base URL (e.g., https://example.cybozu.com) is automatically resolved
4184
4390
 
4185
- #### \u30B9\u30C6\u30C3\u30D72: \u30D7\u30ED\u30D1\u30C6\u30A3\u9078\u629E
4186
- 1. \`${listPropertiesToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4187
- 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
4188
- - \`parameterSlug\`: \`"property-id"\`
4189
- - \`options\`: \u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (id: \u30D7\u30ED\u30D1\u30C6\u30A3ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30D1\u30C6\u30A3ID
4190
- 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D73\u306B\u9032\u3080
4391
+ ### List Apps
4392
+ - GET apps.json
4191
4393
 
4192
- #### \u30B9\u30C6\u30C3\u30D73: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
4193
- 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
4194
- - \`property\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E\u8868\u793A\u540D
4195
- - \`propertyId\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3ID
4196
- - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
4394
+ ### Get Field Definitions
4395
+ - GET app/form/fields.json?app={appId}
4197
4396
 
4198
- ### \u91CD\u8981\u306A\u5236\u7D04
4199
- - **\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
4397
+ ### Get Records
4398
+ - GET records.json?app={appId}&query={query}
4399
+ - Query example: records.json?app=1&query=updatedTime > "2024-01-01" order by recordNumber asc limit 100
4200
4400
 
4201
- ### \u5B9F\u884C\u65B9\u91DD
4202
- - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
4203
- - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
4204
- en: `## Google Analytics Setup Instructions
4401
+ ### Add Record
4402
+ - POST record.json
4403
+ - Body: { "app": 1, "record": { "fieldName": { "value": "value" } } }
4205
4404
 
4206
- Follow these steps to set up the Google Analytics (OAuth) connection.
4405
+ ### kintone Query Syntax
4406
+ - Comparison: fieldName = "value", fieldName > 100
4407
+ - Operators: and, or, not
4408
+ - Sort: order by fieldName asc/desc
4409
+ - Limit: limit 100 offset 0
4410
+ - String: like "partial match"
4207
4411
 
4208
- ### Steps
4412
+ ## kintone SDK (TypeScript handler)
4413
+ Non-SQL connectors like kintone can also be used via the SDK in TypeScript handlers:
4209
4414
 
4210
- #### Step 1: Account Selection
4211
- 1. Call \`${listAccountsToolName}\` to get the list of Google Analytics accounts accessible with the OAuth credentials
4212
- 2. Tell the user "Please select an account.", then call \`askUserQuestion\`:
4213
- - \`options\`: The account list. Each option's \`label\` should be \`Display Name (name)\`
4415
+ \`\`\`ts
4416
+ import { connection } from "@squadbase/vite-server/connectors/kintone";
4214
4417
 
4215
- #### Step 2: Property Selection
4216
- 1. Call \`${listPropertiesToolName}\` to get the list of properties for the selected account
4217
- 2. Call \`updateConnectionParameters\`:
4218
- - \`parameterSlug\`: \`"property-id"\`
4219
- - \`options\`: The property list. Each option's \`label\` should be \`Display Name (id: propertyId)\`, \`value\` should be the property ID
4220
- 3. The \`label\` of the user's selected property will arrive as a message. Proceed to Step 3
4418
+ const kintone = connection("<connectionId>");
4221
4419
 
4222
- #### Step 3: Complete Setup
4223
- 1. Call \`updateConnectionContext\`:
4224
- - \`property\`: The selected property's display name
4225
- - \`propertyId\`: The selected property ID
4226
- - \`note\`: Brief description of the setup
4420
+ // Authenticated fetch (returns standard Response)
4421
+ const res = await kintone.request("/k/v1/records.json?app=1&query=limit 10");
4422
+ const data = await res.json();
4227
4423
 
4228
- ### Important Constraints
4229
- - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
4424
+ await kintone.request("/k/v1/record.json", {
4425
+ method: "POST",
4426
+ body: JSON.stringify({ app: 1, record: { title: { value: "Hello" } } }),
4427
+ });
4230
4428
 
4231
- ### Execution Policy
4232
- - Write only 1 sentence between tool calls, then immediately call the next tool
4233
- - Skip unnecessary explanations and proceed efficiently`
4429
+ // Convenience methods (uses @kintone/rest-api-client)
4430
+ const { records, totalCount } = await kintone.getRecords(1, {
4431
+ query: 'status = "Active"',
4432
+ fields: ["name", "email"],
4433
+ totalCount: true,
4434
+ });
4435
+ const { record } = await kintone.getRecord(1, 100);
4436
+ const { apps } = await kintone.listApps();
4437
+ \`\`\``,
4438
+ tools: tools15
4234
4439
  });
4235
4440
 
4236
- // src/connectors/google-analytics-oauth/tools/request.ts
4441
+ // src/connectors/wix-store/tools/request.ts
4237
4442
  import { z as z23 } from "zod";
4238
- var BASE_URL5 = "https://analyticsdata.googleapis.com/v1beta/";
4239
- var REQUEST_TIMEOUT_MS12 = 6e4;
4240
- var cachedToken7 = null;
4241
- async function getProxyToken7(config) {
4242
- if (cachedToken7 && cachedToken7.expiresAt > Date.now() + 6e4) {
4243
- return cachedToken7.token;
4244
- }
4245
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4246
- const res = await fetch(url, {
4247
- method: "POST",
4248
- headers: {
4249
- "Content-Type": "application/json",
4250
- "x-api-key": config.appApiKey,
4251
- "project-id": config.projectId
4252
- },
4253
- body: JSON.stringify({
4254
- sandboxId: config.sandboxId,
4255
- issuedBy: "coding-agent"
4256
- })
4257
- });
4258
- if (!res.ok) {
4259
- const errorText = await res.text().catch(() => res.statusText);
4260
- throw new Error(
4261
- `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4262
- );
4263
- }
4264
- const data = await res.json();
4265
- cachedToken7 = {
4266
- token: data.token,
4267
- expiresAt: new Date(data.expiresAt).getTime()
4268
- };
4269
- return data.token;
4270
- }
4443
+ var BASE_URL7 = "https://www.wixapis.com/";
4444
+ var REQUEST_TIMEOUT_MS13 = 6e4;
4271
4445
  var inputSchema23 = z23.object({
4272
- toolUseIntent: z23.string().optional().describe(
4273
- "Brief description of what you intend to accomplish with this tool call"
4274
- ),
4275
- connectionId: z23.string().describe("ID of the Google Analytics OAuth connection to use"),
4446
+ toolUseIntent: z23.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
4447
+ connectionId: z23.string().describe("ID of the Wix Store connection to use"),
4276
4448
  method: z23.enum(["GET", "POST"]).describe("HTTP method"),
4277
- path: z23.string().describe(
4278
- "API path appended to https://analyticsdata.googleapis.com/v1beta/ (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."
4279
- ),
4280
- body: z23.record(z23.string(), z23.unknown()).optional().describe("POST request body (JSON)")
4449
+ path: z23.string().describe("API path (e.g., 'stores/v1/products/query', 'stores/v2/orders/query')"),
4450
+ body: z23.record(z23.string(), z23.unknown()).optional().describe("Request body (JSON)")
4281
4451
  });
4282
4452
  var outputSchema23 = z23.discriminatedUnion("success", [
4283
4453
  z23.object({
@@ -4292,51 +4462,40 @@ var outputSchema23 = z23.discriminatedUnion("success", [
4292
4462
  ]);
4293
4463
  var requestTool7 = new ConnectorTool({
4294
4464
  name: "request",
4295
- description: `Send authenticated requests to the Google Analytics Data API v1beta.
4296
- Authentication is handled automatically via OAuth proxy.
4297
- {propertyId} in the path is automatically replaced with the connection's property ID.`,
4465
+ description: `Send authenticated requests to the Wix Store API.
4466
+ Authentication is handled automatically using the API Key and Site ID.`,
4298
4467
  inputSchema: inputSchema23,
4299
4468
  outputSchema: outputSchema23,
4300
- async execute({ connectionId, method, path, body }, connections, config) {
4469
+ async execute({ connectionId, method, path, body }, connections) {
4301
4470
  const connection = connections.find((c) => c.id === connectionId);
4302
4471
  if (!connection) {
4303
- return {
4304
- success: false,
4305
- error: `Connection ${connectionId} not found`
4306
- };
4472
+ return { success: false, error: `Connection ${connectionId} not found` };
4307
4473
  }
4308
- console.log(
4309
- `[connector-request] google-analytics-oauth/${connection.name}: ${method} ${path}`
4310
- );
4474
+ console.log(`[connector-request] wix-store/${connection.name}: ${method} ${path}`);
4311
4475
  try {
4312
- const propertyId = parameters8.propertyId.tryGetValue(connection);
4313
- const resolvedPath = propertyId ? path.replace(/\{propertyId\}/g, propertyId) : path;
4314
- const url = `${BASE_URL5}${resolvedPath}`;
4315
- const token = await getProxyToken7(config.oauthProxy);
4316
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4476
+ const apiKey = parameters7.apiKey.getValue(connection);
4477
+ const siteId = parameters7.siteId.getValue(connection);
4478
+ const url = `${BASE_URL7}${path}`;
4317
4479
  const controller = new AbortController();
4318
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS12);
4480
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS13);
4319
4481
  try {
4320
- const response = await fetch(proxyUrl, {
4321
- method: "POST",
4482
+ const response = await fetch(url, {
4483
+ method,
4322
4484
  headers: {
4323
- "Content-Type": "application/json",
4324
- Authorization: `Bearer ${token}`
4485
+ Authorization: apiKey,
4486
+ "wix-site-id": siteId,
4487
+ "Content-Type": "application/json"
4325
4488
  },
4326
- body: JSON.stringify({
4327
- url,
4328
- method,
4329
- ...method === "POST" && body ? {
4330
- headers: { "Content-Type": "application/json" },
4331
- body: JSON.stringify(body)
4332
- } : {}
4333
- }),
4489
+ body: body ? JSON.stringify(body) : void 0,
4334
4490
  signal: controller.signal
4335
4491
  });
4336
4492
  const data = await response.json();
4337
4493
  if (!response.ok) {
4338
- const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4339
- return { success: false, error: errorMessage };
4494
+ const errorObj = data?.message;
4495
+ return {
4496
+ success: false,
4497
+ error: errorObj ?? `HTTP ${response.status} ${response.statusText}`
4498
+ };
4340
4499
  }
4341
4500
  return { success: true, status: response.status, data };
4342
4501
  } finally {
@@ -4349,262 +4508,144 @@ Authentication is handled automatically via OAuth proxy.
4349
4508
  }
4350
4509
  });
4351
4510
 
4352
- // src/connectors/google-analytics-oauth/index.ts
4353
- var tools18 = {
4354
- request: requestTool7,
4355
- listAccounts: listAccountsTool,
4356
- listProperties: listPropertiesTool
4357
- };
4358
- var googleAnalyticsOauthConnector = new ConnectorPlugin({
4359
- slug: "google-analytics",
4360
- authType: AUTH_TYPES.OAUTH,
4361
- name: "Google Analytics (OAuth)",
4362
- description: "Connect to Google Analytics for web analytics and reporting using OAuth.",
4363
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
4364
- parameters: parameters8,
4365
- releaseFlag: { dev1: true, dev2: true, prod: false },
4366
- setup: googleAnalyticsOauthSetup,
4367
- proxyPolicy: {
4368
- allowlist: [
4369
- {
4370
- host: "analyticsdata.googleapis.com",
4371
- methods: ["GET", "POST"]
4372
- },
4373
- {
4374
- host: "analyticsadmin.googleapis.com",
4375
- methods: ["GET"]
4376
- }
4377
- ]
4378
- },
4379
- systemPrompt: `## Google Analytics Data API (OAuth, Read-Only)
4380
- - Call the GA4 Data API using the authenticated request tool
4381
- - {propertyId} in the path is automatically replaced
4511
+ // src/connectors/wix-store/index.ts
4512
+ var tools16 = { request: requestTool7 };
4513
+ var wixStoreConnector = new ConnectorPlugin({
4514
+ slug: "wix-store",
4515
+ authType: null,
4516
+ name: "Wix Store",
4517
+ description: "Connect to Wix Store.",
4518
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/YyFxclQFzROIYpFam6vRK/e7e75d3feac49a1cc5e433c147216d23/Wix_logo_black.svg",
4519
+ parameters: parameters7,
4520
+ releaseFlag: { dev1: true, dev2: true, prod: true },
4521
+ systemPrompt: `## Wix Store API
4522
+ - Call the Wix Store REST API using the authenticated request tool
4523
+ - Authentication (API Key, Site ID) is automatically configured
4524
+ - Wix API uses POST for query endpoints as well
4525
+ - Wix uses WQL (Wix Query Language) for filters
4526
+ - Max 100 items per page
4382
4527
 
4383
- ### Get Metadata (Check available dimensions and metrics)
4384
- - GET properties/{propertyId}/metadata
4528
+ ### List Products
4529
+ - POST stores/v1/products/query
4530
+ - Body: { "query": { "paging": { "limit": 50, "offset": 0 } } }
4385
4531
 
4386
- ### Get Report
4387
- - POST properties/{propertyId}:runReport
4388
- - Body example:
4389
- {
4390
- "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
4391
- "dimensions": [{"name": "date"}],
4392
- "metrics": [{"name": "activeUsers"}],
4393
- "limit": 100
4394
- }
4532
+ ### Search Products (with filter)
4533
+ - POST stores/v1/products/query
4534
+ - Body: { "query": { "filter": { "name": { "$contains": "keyword" } }, "paging": { "limit": 50 } } }
4395
4535
 
4396
- ### Common Dimensions
4397
- date, country, city, deviceCategory, browser, pagePath, pageTitle,
4398
- sessionSource, sessionMedium, eventName
4536
+ ### Search Orders (eCommerce Orders V1)
4537
+ - POST ecom/v1/orders/search
4538
+ - Body: { "search": { "cursorPaging": { "limit": 50 } } }
4539
+ - Uses cursor-based pagination (cursorPaging, not paging)
4540
+ - Response includes pagingMetadata.cursors.next for pagination
4399
4541
 
4400
- ### Common Metrics
4401
- activeUsers, sessions, screenPageViews, bounceRate,
4402
- averageSessionDuration, conversions, totalRevenue
4542
+ ### List Collections (Categories)
4543
+ - POST stores/v1/collections/query
4544
+ - Body: { "query": { "paging": { "limit": 50 } } }
4403
4545
 
4404
- ### Date Specification
4405
- - Absolute: "2024-01-01"
4406
- - Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
4546
+ ### List Inventory Items
4547
+ - POST stores/v2/inventoryItems/query
4548
+ - Body: { "query": { "paging": { "limit": 50 } } }
4407
4549
 
4408
- ## Google Analytics SDK (TypeScript handler)
4550
+ ## Wix Store SDK (TypeScript handler)
4551
+ Non-SQL connectors like Wix Store can also be used via the SDK in TypeScript handlers:
4409
4552
 
4410
4553
  \`\`\`ts
4411
- import { connection } from "@squadbase/vite-server/connectors/google-analytics-oauth";
4554
+ import { connection } from "@squadbase/vite-server/connectors/wix-store";
4412
4555
 
4413
- const ga = connection("<connectionId>");
4556
+ const wix = connection("<connectionId>");
4414
4557
 
4415
4558
  // Authenticated fetch (returns standard Response)
4416
- const res = await ga.request("properties/{propertyId}:runReport", {
4559
+ const res = await wix.request("/stores/v1/products/query", {
4417
4560
  method: "POST",
4418
- body: JSON.stringify({ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], metrics: [{ name: "activeUsers" }] }),
4561
+ headers: { "Content-Type": "application/json" },
4562
+ body: JSON.stringify({ query: { paging: { limit: 10 } } }),
4419
4563
  });
4420
4564
  const data = await res.json();
4421
4565
 
4422
4566
  // Convenience methods
4423
- const { rows, rowCount } = await ga.runReport({
4424
- dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
4425
- dimensions: [{ name: "date" }],
4426
- metrics: [{ name: "activeUsers" }, { name: "sessions" }],
4427
- limit: 100,
4428
- });
4429
- const metadata = await ga.getMetadata();
4430
- const realtime = await ga.runRealtimeReport({
4431
- metrics: [{ name: "activeUsers" }],
4432
- dimensions: [{ name: "country" }],
4433
- });
4567
+ const { products, totalResults } = await wix.queryProducts({ paging: { limit: 50 } });
4568
+ const { product } = await wix.getProduct("product-id");
4569
+ const { orders, pagingMetadata } = await wix.queryOrders({ cursorPaging: { limit: 50 } });
4570
+ const { order } = await wix.getOrder("order-id");
4571
+ const { inventoryItems } = await wix.queryInventory({ paging: { limit: 50 } });
4572
+ const { collections } = await wix.queryCollections({ paging: { limit: 50 } });
4434
4573
  \`\`\``,
4435
- tools: tools18,
4436
- async checkConnection(params, config) {
4437
- const { proxyFetch } = config;
4438
- const propertyId = params[parameters8.propertyId.slug];
4439
- if (!propertyId) {
4440
- return { success: true };
4441
- }
4442
- const url = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}/metadata`;
4443
- try {
4444
- const res = await proxyFetch(url, { method: "GET" });
4445
- if (!res.ok) {
4446
- const errorText = await res.text().catch(() => res.statusText);
4447
- return {
4448
- success: false,
4449
- error: `Google Analytics API failed: HTTP ${res.status} ${errorText}`
4450
- };
4451
- }
4452
- return { success: true };
4453
- } catch (error) {
4454
- return {
4455
- success: false,
4456
- error: error instanceof Error ? error.message : String(error)
4457
- };
4458
- }
4459
- }
4574
+ tools: tools16
4460
4575
  });
4461
4576
 
4462
- // src/connectors/google-ads-oauth/tools/list-customers.ts
4577
+ // src/connectors/dbt/tools/request.ts
4463
4578
  import { z as z24 } from "zod";
4464
- var BASE_URL6 = "https://googleads.googleapis.com/v18/";
4465
- var REQUEST_TIMEOUT_MS13 = 6e4;
4466
- var cachedToken8 = null;
4467
- async function getProxyToken8(config) {
4468
- if (cachedToken8 && cachedToken8.expiresAt > Date.now() + 6e4) {
4469
- return cachedToken8.token;
4470
- }
4471
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4472
- const res = await fetch(url, {
4473
- method: "POST",
4474
- headers: {
4475
- "Content-Type": "application/json",
4476
- "x-api-key": config.appApiKey,
4477
- "project-id": config.projectId
4478
- },
4479
- body: JSON.stringify({
4480
- sandboxId: config.sandboxId,
4481
- issuedBy: "coding-agent"
4482
- })
4483
- });
4484
- if (!res.ok) {
4485
- const errorText = await res.text().catch(() => res.statusText);
4486
- throw new Error(
4487
- `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4488
- );
4489
- }
4490
- const data = await res.json();
4491
- cachedToken8 = {
4492
- token: data.token,
4493
- expiresAt: new Date(data.expiresAt).getTime()
4494
- };
4495
- return data.token;
4579
+ var REQUEST_TIMEOUT_MS14 = 6e4;
4580
+ function resolveGraphqlEndpoint(host) {
4581
+ if (host.includes("emea")) return "https://metadata.emea.dbt.com/graphql";
4582
+ if (host.includes(".au.")) return "https://metadata.au.dbt.com/graphql";
4583
+ return "https://metadata.cloud.getdbt.com/graphql";
4496
4584
  }
4497
4585
  var inputSchema24 = z24.object({
4498
- toolUseIntent: z24.string().optional().describe(
4499
- "Brief description of what you intend to accomplish with this tool call"
4500
- ),
4501
- connectionId: z24.string().describe("ID of the Google Ads OAuth connection to use")
4586
+ toolUseIntent: z24.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
4587
+ connectionId: z24.string().describe("ID of the dbt Cloud connection to use"),
4588
+ query: z24.string().describe("GraphQL query"),
4589
+ variables: z24.record(z24.string(), z24.unknown()).optional().describe("GraphQL variables (JSON)")
4502
4590
  });
4503
4591
  var outputSchema24 = z24.discriminatedUnion("success", [
4504
4592
  z24.object({
4505
4593
  success: z24.literal(true),
4506
- customers: z24.array(
4507
- z24.object({
4508
- customerId: z24.string(),
4509
- descriptiveName: z24.string()
4510
- })
4511
- )
4594
+ data: z24.record(z24.string(), z24.unknown())
4512
4595
  }),
4513
4596
  z24.object({
4514
4597
  success: z24.literal(false),
4515
4598
  error: z24.string()
4516
4599
  })
4517
4600
  ]);
4518
- var listCustomersTool = new ConnectorTool({
4519
- name: "listCustomers",
4520
- description: "List Google Ads customer accounts accessible with the current OAuth credentials.",
4601
+ var requestTool8 = new ConnectorTool({
4602
+ name: "request",
4603
+ description: `Send authenticated requests to the dbt Cloud Discovery API (GraphQL).
4604
+ Authentication is handled automatically using the API token.
4605
+ {environmentId} in GraphQL variables is automatically replaced with the prod-env-id.`,
4521
4606
  inputSchema: inputSchema24,
4522
4607
  outputSchema: outputSchema24,
4523
- async execute({ connectionId }, connections, config) {
4608
+ async execute({ connectionId, query, variables }, connections) {
4524
4609
  const connection = connections.find((c) => c.id === connectionId);
4525
4610
  if (!connection) {
4526
- return {
4527
- success: false,
4528
- error: `Connection ${connectionId} not found`
4529
- };
4611
+ return { success: false, error: `Connection ${connectionId} not found` };
4530
4612
  }
4531
- console.log(
4532
- `[connector-request] google-ads-oauth/${connection.name}: listCustomers`
4533
- );
4613
+ console.log(`[connector-request] dbt/${connection.name}: GraphQL query`);
4534
4614
  try {
4535
- const developerToken = parameters9.developerToken.getValue(connection);
4536
- const token = await getProxyToken8(config.oauthProxy);
4537
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4615
+ const host = parameters8.host.getValue(connection);
4616
+ const token = parameters8.token.getValue(connection);
4617
+ const environmentId = parameters8.prodEnvId.getValue(connection);
4618
+ const resolvedVariables = variables ? JSON.parse(
4619
+ JSON.stringify(variables).replace(/\{environmentId\}/g, environmentId)
4620
+ ) : void 0;
4621
+ const endpoint = resolveGraphqlEndpoint(host);
4538
4622
  const controller = new AbortController();
4539
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS13);
4623
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS14);
4540
4624
  try {
4541
- const response = await fetch(proxyUrl, {
4625
+ const response = await fetch(endpoint, {
4542
4626
  method: "POST",
4543
4627
  headers: {
4544
- "Content-Type": "application/json",
4545
- Authorization: `Bearer ${token}`
4628
+ Authorization: `Bearer ${token}`,
4629
+ "Content-Type": "application/json"
4546
4630
  },
4547
- body: JSON.stringify({
4548
- url: `${BASE_URL6}customers:listAccessibleCustomers`,
4549
- method: "GET",
4550
- headers: {
4551
- "developer-token": developerToken
4552
- }
4553
- }),
4631
+ body: JSON.stringify({ query, variables: resolvedVariables }),
4554
4632
  signal: controller.signal
4555
4633
  });
4556
4634
  const data = await response.json();
4557
4635
  if (!response.ok) {
4558
- const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4559
- return { success: false, error: errorMessage };
4636
+ return {
4637
+ success: false,
4638
+ error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
4639
+ };
4560
4640
  }
4561
- const customerIds = (data.resourceNames ?? []).map(
4562
- (rn) => rn.replace(/^customers\//, "")
4563
- );
4564
- const customers = [];
4565
- for (const cid of customerIds) {
4566
- try {
4567
- const detailResponse = await fetch(proxyUrl, {
4568
- method: "POST",
4569
- headers: {
4570
- "Content-Type": "application/json",
4571
- Authorization: `Bearer ${token}`
4572
- },
4573
- body: JSON.stringify({
4574
- url: `${BASE_URL6}customers/${cid}/googleAds:searchStream`,
4575
- method: "POST",
4576
- headers: {
4577
- "Content-Type": "application/json",
4578
- "developer-token": developerToken,
4579
- "login-customer-id": cid
4580
- },
4581
- body: JSON.stringify({
4582
- query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
4583
- })
4584
- }),
4585
- signal: controller.signal
4586
- });
4587
- if (detailResponse.ok) {
4588
- const detailData = await detailResponse.json();
4589
- const customer = detailData?.[0]?.results?.[0]?.customer;
4590
- customers.push({
4591
- customerId: cid,
4592
- descriptiveName: customer?.descriptiveName ?? cid
4593
- });
4594
- } else {
4595
- customers.push({
4596
- customerId: cid,
4597
- descriptiveName: cid
4598
- });
4599
- }
4600
- } catch {
4601
- customers.push({
4602
- customerId: cid,
4603
- descriptiveName: cid
4604
- });
4605
- }
4641
+ if (Array.isArray(data?.errors) && data.errors.length > 0) {
4642
+ const errors = data.errors;
4643
+ return {
4644
+ success: false,
4645
+ error: errors.map((e) => e.message).join("; ")
4646
+ };
4606
4647
  }
4607
- return { success: true, customers };
4648
+ return { success: true, data };
4608
4649
  } finally {
4609
4650
  clearTimeout(timeout);
4610
4651
  }
@@ -4615,126 +4656,144 @@ var listCustomersTool = new ConnectorTool({
4615
4656
  }
4616
4657
  });
4617
4658
 
4618
- // src/connectors/google-ads-oauth/setup.ts
4619
- var listCustomersToolName = `google-ads-oauth_${listCustomersTool.name}`;
4620
- var googleAdsSetup = new ConnectorSetup({
4621
- ja: `## Google Ads \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
4622
-
4623
- \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Ads (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
4624
-
4625
- ### \u624B\u9806
4626
-
4627
- #### \u30B9\u30C6\u30C3\u30D71: \u30AB\u30B9\u30BF\u30DE\u30FC\u9078\u629E
4628
- 1. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4629
- 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
4630
- - \`parameterSlug\`: \`"customer-id"\`
4631
- - \`options\`: \u30AB\u30B9\u30BF\u30DE\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30AB\u30B9\u30BF\u30DE\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30B9\u30BF\u30DE\u30FCID
4632
- 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
4633
-
4634
- #### \u30B9\u30C6\u30C3\u30D72: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
4635
- 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
4636
- - \`customer\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E\u8868\u793A\u540D
4637
- - \`customerId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FCID
4638
- - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
4659
+ // src/connectors/dbt/index.ts
4660
+ var tools17 = { request: requestTool8 };
4661
+ var dbtConnector = new ConnectorPlugin({
4662
+ slug: "dbt",
4663
+ authType: null,
4664
+ name: "dbt",
4665
+ description: "Connect to dbt Cloud for data transformation and analytics engineering.",
4666
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4iT6ncXtdtHdkXexU0WgfZ/0367a38d245f2568eab5eb511f9ee692/dbt.png",
4667
+ parameters: parameters8,
4668
+ releaseFlag: { dev1: true, dev2: true, prod: true },
4669
+ systemPrompt: `## dbt Cloud Discovery API (GraphQL)
4670
+ - Call the dbt Cloud Discovery API using the authenticated request tool
4671
+ - {environmentId} in GraphQL variables is automatically replaced with the prod-env-id
4639
4672
 
4640
- ### \u91CD\u8981\u306A\u5236\u7D04
4641
- - **\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
4673
+ ### List Models
4674
+ query:
4675
+ query($environmentId: BigInt!) {
4676
+ environment(id: $environmentId) {
4677
+ applied {
4678
+ models(first: 100) {
4679
+ edges {
4680
+ node { uniqueId name description database schema alias materializedType }
4681
+ }
4682
+ }
4683
+ }
4684
+ }
4685
+ }
4686
+ variables: { "environmentId": "{environmentId}" }
4642
4687
 
4643
- ### \u5B9F\u884C\u65B9\u91DD
4644
- - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
4645
- - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
4646
- en: `## Google Ads Setup Instructions
4688
+ ### List Sources
4689
+ query:
4690
+ query($environmentId: BigInt!) {
4691
+ environment(id: $environmentId) {
4692
+ applied {
4693
+ sources(first: 100) {
4694
+ edges {
4695
+ node { uniqueId name description database schema identifier }
4696
+ }
4697
+ }
4698
+ }
4699
+ }
4700
+ }
4701
+ variables: { "environmentId": "{environmentId}" }
4647
4702
 
4648
- Follow these steps to set up the Google Ads (OAuth) connection.
4703
+ ### Model Column Information
4704
+ query:
4705
+ query($environmentId: BigInt!, $uniqueId: String!) {
4706
+ environment(id: $environmentId) {
4707
+ applied {
4708
+ models(filter: { uniqueId: { eq: $uniqueId } }) {
4709
+ edges {
4710
+ node { uniqueId name columns { name description type } }
4711
+ }
4712
+ }
4713
+ }
4714
+ }
4715
+ }
4649
4716
 
4650
- ### Steps
4717
+ ### Lineage (Dependencies)
4718
+ - Traverse relationships using ancestors / children fields
4719
+ - Get ancestors { uniqueId name } or children { uniqueId name } within node
4651
4720
 
4652
- #### Step 1: Customer Selection
4653
- 1. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
4654
- 2. Call \`updateConnectionParameters\`:
4655
- - \`parameterSlug\`: \`"customer-id"\`
4656
- - \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
4657
- 3. The \`label\` of the user's selected customer will arrive as a message. Proceed to Step 2
4721
+ ## dbt SDK (TypeScript handler)
4722
+ Non-SQL connectors like dbt can also be used via the SDK in TypeScript handlers:
4658
4723
 
4659
- #### Step 2: Complete Setup
4660
- 1. Call \`updateConnectionContext\`:
4661
- - \`customer\`: The selected customer's display name
4662
- - \`customerId\`: The selected customer ID
4663
- - \`note\`: Brief description of the setup
4724
+ \`\`\`ts
4725
+ import { connection } from "@squadbase/vite-server/connectors/dbt";
4664
4726
 
4665
- ### Important Constraints
4666
- - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
4727
+ const dbt = connection("<connectionId>");
4667
4728
 
4668
- ### Execution Policy
4669
- - Write only 1 sentence between tool calls, then immediately call the next tool
4670
- - Skip unnecessary explanations and proceed efficiently`
4729
+ // Authenticated fetch (returns standard Response)
4730
+ const res = await dbt.request("/graphql", {
4731
+ method: "POST",
4732
+ headers: { "Content-Type": "application/json" },
4733
+ body: JSON.stringify({ query: "{ ... }", variables: {} }),
4671
4734
  });
4735
+ const data = await res.json();
4672
4736
 
4673
- // src/connectors/google-ads-oauth/tools/request.ts
4674
- import { z as z25 } from "zod";
4675
- var BASE_URL7 = "https://googleads.googleapis.com/v18/";
4676
- var REQUEST_TIMEOUT_MS14 = 6e4;
4677
- var cachedToken9 = null;
4678
- async function getProxyToken9(config) {
4679
- if (cachedToken9 && cachedToken9.expiresAt > Date.now() + 6e4) {
4680
- return cachedToken9.token;
4681
- }
4682
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4683
- const res = await fetch(url, {
4684
- method: "POST",
4685
- headers: {
4686
- "Content-Type": "application/json",
4687
- "x-api-key": config.appApiKey,
4688
- "project-id": config.projectId
4689
- },
4690
- body: JSON.stringify({
4691
- sandboxId: config.sandboxId,
4692
- issuedBy: "coding-agent"
4693
- })
4694
- });
4695
- if (!res.ok) {
4696
- const errorText = await res.text().catch(() => res.statusText);
4697
- throw new Error(
4698
- `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4699
- );
4737
+ // Convenience methods
4738
+ const result = await dbt.query(\`
4739
+ query($environmentId: BigInt!) {
4740
+ environment(id: $environmentId) { applied { models(first: 10) { edges { node { name } } } } }
4700
4741
  }
4701
- const data = await res.json();
4702
- cachedToken9 = {
4703
- token: data.token,
4704
- expiresAt: new Date(data.expiresAt).getTime()
4705
- };
4706
- return data.token;
4707
- }
4742
+ \`);
4743
+ const models = await dbt.getModels({ limit: 100 });
4744
+ const model = await dbt.getModelByUniqueId("model.project.my_model");
4745
+ const sources = await dbt.getSources({ limit: 100 });
4746
+ const tests = await dbt.getTests({ limit: 100 });
4747
+ const metrics = await dbt.getMetrics({ limit: 100 });
4748
+ \`\`\``,
4749
+ tools: tools17
4750
+ });
4751
+
4752
+ // src/connectors/squadbase-db/parameters.ts
4753
+ var parameters19 = {
4754
+ connectionUrl: new ParameterDefinition({
4755
+ slug: "connection-url",
4756
+ name: "Connection URL",
4757
+ description: "PostgreSQL connection URL for Squadbase DB.",
4758
+ envVarBaseKey: "SQUADBASE_DB_CONNECTION_URL",
4759
+ type: "text",
4760
+ secret: true,
4761
+ required: true
4762
+ })
4763
+ };
4764
+
4765
+ // src/connectors/squadbase-db/tools/execute-query.ts
4766
+ import { z as z25 } from "zod";
4767
+ var MAX_ROWS10 = 500;
4768
+ var CONNECT_TIMEOUT_MS3 = 1e4;
4769
+ var STATEMENT_TIMEOUT_MS2 = 6e4;
4708
4770
  var inputSchema25 = z25.object({
4709
4771
  toolUseIntent: z25.string().optional().describe(
4710
4772
  "Brief description of what you intend to accomplish with this tool call"
4711
4773
  ),
4712
- connectionId: z25.string().describe("ID of the Google Ads OAuth connection to use"),
4713
- method: z25.enum(["GET", "POST"]).describe("HTTP method"),
4714
- path: z25.string().describe(
4715
- "API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
4716
- ),
4717
- body: z25.record(z25.string(), z25.unknown()).optional().describe("POST request body (JSON)")
4774
+ connectionId: z25.string().describe("ID of the Squadbase DB connection to use"),
4775
+ sql: z25.string().describe("PostgreSQL SQL query. Always include LIMIT in queries.")
4718
4776
  });
4719
4777
  var outputSchema25 = z25.discriminatedUnion("success", [
4720
4778
  z25.object({
4721
4779
  success: z25.literal(true),
4722
- status: z25.number(),
4723
- data: z25.unknown()
4780
+ rowCount: z25.number(),
4781
+ truncated: z25.boolean(),
4782
+ rows: z25.array(z25.record(z25.string(), z25.unknown()))
4724
4783
  }),
4725
4784
  z25.object({
4726
4785
  success: z25.literal(false),
4727
4786
  error: z25.string()
4728
4787
  })
4729
4788
  ]);
4730
- var requestTool8 = new ConnectorTool({
4731
- name: "request",
4732
- description: `Send authenticated requests to the Google Ads API v18.
4733
- Authentication is handled automatically via OAuth proxy.
4734
- {customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
4789
+ var executeQueryTool10 = new ConnectorTool({
4790
+ name: "executeQuery",
4791
+ description: `Execute SQL against Squadbase DB (PostgreSQL). Returns up to ${MAX_ROWS10} rows.
4792
+ Use for: schema exploration (information_schema), data sampling, analytical queries.
4793
+ Avoid loading large amounts of data; always include LIMIT in queries.`,
4735
4794
  inputSchema: inputSchema25,
4736
4795
  outputSchema: outputSchema25,
4737
- async execute({ connectionId, method, path, body }, connections, config) {
4796
+ async execute({ connectionId, sql }, connections) {
4738
4797
  const connection = connections.find((c) => c.id === connectionId);
4739
4798
  if (!connection) {
4740
4799
  return {
@@ -4743,165 +4802,118 @@ Authentication is handled automatically via OAuth proxy.
4743
4802
  };
4744
4803
  }
4745
4804
  console.log(
4746
- `[connector-request] google-ads-oauth/${connection.name}: ${method} ${path}`
4805
+ `[connector-query] squadbase-db/${connection.name}: ${sql}`
4747
4806
  );
4807
+ let connectionUrl;
4748
4808
  try {
4749
- const rawCustomerId = parameters9.customerId.tryGetValue(connection);
4750
- const customerId = rawCustomerId?.replace(/-/g, "") ?? "";
4751
- const resolvedPath = customerId ? path.replace(/\{customerId\}/g, customerId) : path;
4752
- const url = `${BASE_URL7}${resolvedPath}`;
4753
- const token = await getProxyToken9(config.oauthProxy);
4754
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4755
- const controller = new AbortController();
4756
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS14);
4809
+ const { Pool } = await import("pg");
4810
+ connectionUrl = parameters19.connectionUrl.getValue(connection);
4811
+ const pool = new Pool({
4812
+ connectionString: connectionUrl,
4813
+ ssl: { rejectUnauthorized: false },
4814
+ connectionTimeoutMillis: CONNECT_TIMEOUT_MS3,
4815
+ statement_timeout: STATEMENT_TIMEOUT_MS2
4816
+ });
4757
4817
  try {
4758
- const developerToken = parameters9.developerToken.getValue(connection);
4759
- const response = await fetch(proxyUrl, {
4760
- method: "POST",
4761
- headers: {
4762
- "Content-Type": "application/json",
4763
- Authorization: `Bearer ${token}`
4764
- },
4765
- body: JSON.stringify({
4766
- url,
4767
- method,
4768
- headers: {
4769
- "Content-Type": "application/json",
4770
- "developer-token": developerToken,
4771
- ...customerId ? { "login-customer-id": customerId } : {}
4772
- },
4773
- ...method === "POST" && body ? { body: JSON.stringify(body) } : {}
4774
- }),
4775
- signal: controller.signal
4776
- });
4777
- const data = await response.json();
4778
- if (!response.ok) {
4779
- const dataObj = data;
4780
- const errorMessage = typeof dataObj?.error === "string" ? dataObj.error : typeof dataObj?.message === "string" ? dataObj.message : `HTTP ${response.status} ${response.statusText}`;
4781
- return { success: false, error: errorMessage };
4782
- }
4783
- return { success: true, status: response.status, data };
4818
+ const result = await pool.query(sql);
4819
+ const rows = result.rows;
4820
+ const truncated = rows.length > MAX_ROWS10;
4821
+ return {
4822
+ success: true,
4823
+ rowCount: Math.min(rows.length, MAX_ROWS10),
4824
+ truncated,
4825
+ rows: rows.slice(0, MAX_ROWS10)
4826
+ };
4784
4827
  } finally {
4785
- clearTimeout(timeout);
4828
+ await pool.end();
4786
4829
  }
4787
4830
  } catch (err) {
4788
- const msg = err instanceof Error ? err.message : String(err);
4831
+ let msg = err instanceof Error ? err.message : String(err);
4832
+ if (connectionUrl) {
4833
+ msg = msg.replaceAll(connectionUrl, "***");
4834
+ }
4789
4835
  return { success: false, error: msg };
4790
4836
  }
4791
4837
  }
4792
4838
  });
4793
4839
 
4794
- // src/connectors/google-ads-oauth/index.ts
4795
- var tools19 = {
4796
- request: requestTool8,
4797
- listCustomers: listCustomersTool
4798
- };
4799
- var googleAdsOauthConnector = new ConnectorPlugin({
4800
- slug: "google-ads",
4801
- authType: AUTH_TYPES.OAUTH,
4802
- name: "Google Ads (OAuth)",
4803
- description: "Connect to Google Ads for advertising campaign data and reporting using OAuth.",
4804
- iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6lQFddkMPLCzLJBPn1TiSe/4b22b78cd2a9cc91e9ee9283e5c80a51/google-ads.svg",
4805
- parameters: parameters9,
4806
- releaseFlag: { dev1: true, dev2: true, prod: false },
4807
- setup: googleAdsSetup,
4808
- proxyPolicy: {
4809
- allowlist: [
4810
- {
4811
- host: "googleads.googleapis.com",
4812
- methods: ["GET", "POST"]
4813
- }
4814
- ]
4840
+ // src/connectors/squadbase-db/index.ts
4841
+ var tools18 = { executeQuery: executeQueryTool10 };
4842
+ var squadbaseDbConnector = new ConnectorPlugin({
4843
+ slug: "squadbase-db",
4844
+ authType: null,
4845
+ name: "Squadbase DB",
4846
+ description: "Connect to Squadbase DB (PostgreSQL).",
4847
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/25y0XqMxIufeD3egWH3bEl/659b4ade405890654cfaf91c03a4b458/icon.svg",
4848
+ parameters: parameters19,
4849
+ releaseFlag: { dev1: true, dev2: true, prod: true },
4850
+ systemPrompt: `## Squadbase DB SQL Notes
4851
+ - Uses PostgreSQL based SQL syntax
4852
+ - Schema exploration:
4853
+ - List tables: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
4854
+ - List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
4855
+ - Always include LIMIT in queries`,
4856
+ tools: tools18,
4857
+ async checkConnection(params, _config) {
4858
+ const { Pool } = await import("pg");
4859
+ const pool = new Pool({
4860
+ connectionString: params[parameters19.connectionUrl.slug],
4861
+ ssl: { rejectUnauthorized: false },
4862
+ connectionTimeoutMillis: 1e4
4863
+ });
4864
+ try {
4865
+ await pool.query("SELECT 1");
4866
+ return { success: true };
4867
+ } catch (error) {
4868
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
4869
+ } finally {
4870
+ await pool.end();
4871
+ }
4815
4872
  },
4816
- systemPrompt: `## Google Ads API (OAuth, Read-Only)
4817
- - Use GAQL (Google Ads Query Language) to query campaign data
4818
- - {customerId} in the path is automatically replaced (hyphens removed)
4819
-
4820
- ### Query Data (searchStream)
4821
- - POST customers/{customerId}/googleAds:searchStream
4822
- - Body: { "query": "SELECT campaign.id, campaign.name, metrics.impressions FROM campaign WHERE segments.date DURING LAST_30_DAYS" }
4823
-
4824
- ### Common GAQL Resources
4825
- - \`campaign\`: Campaign data (campaign.id, campaign.name, campaign.status)
4826
- - \`ad_group\`: Ad group data (ad_group.id, ad_group.name, ad_group.status)
4827
- - \`ad_group_ad\`: Ad data (ad_group_ad.ad.id, ad_group_ad.status)
4828
- - \`keyword_view\`: Keyword performance data
4829
-
4830
- ### Common Metrics
4831
- metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions,
4832
- metrics.ctr, metrics.average_cpc, metrics.conversions_value
4833
-
4834
- ### Common Segments
4835
- segments.date, segments.device, segments.ad_network_type
4836
-
4837
- ### Date Filters
4838
- - \`DURING LAST_7_DAYS\`, \`DURING LAST_30_DAYS\`, \`DURING THIS_MONTH\`
4839
- - \`WHERE segments.date BETWEEN '2024-01-01' AND '2024-01-31'\`
4840
-
4841
- ### Tips
4842
- - cost_micros is in micros (divide by 1,000,000 for actual currency)
4843
- - Use LIMIT to restrict result count
4844
- - Always include relevant WHERE clauses to filter data
4873
+ async query(params, sql, namedParams) {
4874
+ const { Pool } = await import("pg");
4875
+ const { text, values } = buildPositionalParams(sql, namedParams);
4876
+ const pool = new Pool({
4877
+ connectionString: params[parameters19.connectionUrl.slug],
4878
+ ssl: { rejectUnauthorized: false },
4879
+ connectionTimeoutMillis: 1e4,
4880
+ statement_timeout: 6e4
4881
+ });
4882
+ try {
4883
+ const result = await pool.query(text, values);
4884
+ return { rows: result.rows };
4885
+ } finally {
4886
+ await pool.end();
4887
+ }
4888
+ }
4889
+ });
4845
4890
 
4846
- ## Google Ads SDK (TypeScript handler)
4891
+ // src/connectors/openai/index.ts
4892
+ var tools19 = {};
4893
+ var openaiConnector = new ConnectorPlugin({
4894
+ slug: "openai",
4895
+ authType: null,
4896
+ name: "OpenAI",
4897
+ description: "Connect to OpenAI for AI model inference, embeddings, and image generation.",
4898
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/53XJtCgUlW10x6i1X8xpxM/0bfd634069f1d74241296543cb20427a/openai.svg",
4899
+ parameters: parameters9,
4900
+ releaseFlag: { dev1: true, dev2: true, prod: true },
4901
+ systemPrompt: `## OpenAI SDK (TypeScript handler)
4902
+ Use the OpenAI connector via the SDK in TypeScript handlers:
4847
4903
 
4848
4904
  \`\`\`ts
4849
- import { connection } from "@squadbase/vite-server/connectors/google-ads-oauth";
4850
-
4851
- const ads = connection("<connectionId>");
4905
+ import { connection } from "@squadbase/vite-server/connectors/openai";
4906
+ import OpenAI from "openai";
4852
4907
 
4853
- // Execute a GAQL query
4854
- const rows = await ads.search(
4855
- "SELECT campaign.name, metrics.impressions, metrics.clicks FROM campaign WHERE segments.date DURING LAST_7_DAYS"
4856
- );
4857
- rows.forEach(row => console.log(row));
4908
+ const { apiKey } = connection("<connectionId>");
4909
+ const client = new OpenAI({ apiKey });
4858
4910
 
4859
- // List accessible customer accounts
4860
- const customerIds = await ads.listAccessibleCustomers();
4911
+ const response = await client.chat.completions.create({
4912
+ model: "gpt-4o",
4913
+ messages: [{ role: "user", content: "Hello" }],
4914
+ });
4861
4915
  \`\`\``,
4862
- tools: tools19,
4863
- async checkConnection(params, config) {
4864
- const { proxyFetch } = config;
4865
- const rawCustomerId = params[parameters9.customerId.slug];
4866
- const customerId = rawCustomerId?.replace(/-/g, "");
4867
- if (!customerId) {
4868
- return { success: true };
4869
- }
4870
- const developerToken = params[parameters9.developerToken.slug];
4871
- if (!developerToken) {
4872
- return {
4873
- success: false,
4874
- error: "Developer token is required"
4875
- };
4876
- }
4877
- const url = `https://googleads.googleapis.com/v18/customers/${customerId}/googleAds:searchStream`;
4878
- try {
4879
- const res = await proxyFetch(url, {
4880
- method: "POST",
4881
- headers: {
4882
- "Content-Type": "application/json",
4883
- "developer-token": developerToken,
4884
- "login-customer-id": customerId
4885
- },
4886
- body: JSON.stringify({
4887
- query: "SELECT customer.id FROM customer LIMIT 1"
4888
- })
4889
- });
4890
- if (!res.ok) {
4891
- const errorText = await res.text().catch(() => res.statusText);
4892
- return {
4893
- success: false,
4894
- error: `Google Ads API failed: HTTP ${res.status} ${errorText}`
4895
- };
4896
- }
4897
- return { success: true };
4898
- } catch (error) {
4899
- return {
4900
- success: false,
4901
- error: error instanceof Error ? error.message : String(error)
4902
- };
4903
- }
4904
- }
4916
+ tools: tools19
4905
4917
  });
4906
4918
 
4907
4919
  // src/connectors/registry.ts
@@ -4916,15 +4928,15 @@ var plugins = {
4916
4928
  awsAthena: awsAthenaConnector,
4917
4929
  postgresql: postgresqlConnector,
4918
4930
  mysql: mysqlConnector,
4931
+ googleAdsOauth: googleAdsOauthConnector,
4919
4932
  googleAnalytics: googleAnalyticsConnector,
4933
+ googleAnalyticsOauth: googleAnalyticsOauthConnector,
4934
+ googleSheetsOauth: googleSheetsOauthConnector,
4920
4935
  airtable: airtableConnector,
4921
4936
  squadbaseDb: squadbaseDbConnector,
4922
4937
  kintone: kintoneConnector,
4923
4938
  wixStore: wixStoreConnector,
4924
- openai: openaiConnector,
4925
- googleSheetsOauth: googleSheetsOauthConnector,
4926
- googleAnalyticsOauth: googleAnalyticsOauthConnector,
4927
- googleAdsOauth: googleAdsOauthConnector
4939
+ openai: openaiConnector
4928
4940
  };
4929
4941
  var connectors = {
4930
4942
  ...plugins,