@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/{chunk-Q5TIPE53.js → chunk-LB7J6VXK.js} +53 -53
- package/dist/index.d.ts +149 -149
- package/dist/index.js +1338 -1326
- package/dist/sdk.d.ts +215 -215
- package/dist/sdk.js +255 -255
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
parameters7,
|
|
10
10
|
parameters8,
|
|
11
11
|
parameters9
|
|
12
|
-
} from "./chunk-
|
|
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-
|
|
2904
|
+
// src/connectors/google-ads-oauth/tools/list-customers.ts
|
|
2905
2905
|
import { z as z15 } from "zod";
|
|
2906
|
-
var BASE_URL2 = "https://
|
|
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(
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
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
|
-
|
|
2919
|
-
|
|
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
|
|
2927
|
-
name: "
|
|
2928
|
-
description:
|
|
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
|
|
2965
|
+
async execute({ connectionId }, connections, config) {
|
|
2934
2966
|
const connection = connections.find((c) => c.id === connectionId);
|
|
2935
2967
|
if (!connection) {
|
|
2936
|
-
return {
|
|
2968
|
+
return {
|
|
2969
|
+
success: false,
|
|
2970
|
+
error: `Connection ${connectionId} not found`
|
|
2971
|
+
};
|
|
2937
2972
|
}
|
|
2938
|
-
console.log(
|
|
2973
|
+
console.log(
|
|
2974
|
+
`[connector-request] google-ads-oauth/${connection.name}: listCustomers`
|
|
2975
|
+
);
|
|
2939
2976
|
try {
|
|
2940
|
-
const
|
|
2941
|
-
const
|
|
2942
|
-
const
|
|
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(
|
|
2960
|
-
method,
|
|
2983
|
+
const response = await fetch(proxyUrl, {
|
|
2984
|
+
method: "POST",
|
|
2961
2985
|
headers: {
|
|
2962
|
-
|
|
2963
|
-
|
|
2986
|
+
"Content-Type": "application/json",
|
|
2987
|
+
Authorization: `Bearer ${token}`
|
|
2964
2988
|
},
|
|
2965
|
-
body:
|
|
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
|
|
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
|
-
|
|
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-
|
|
2988
|
-
var
|
|
2989
|
-
var
|
|
2990
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
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
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
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
|
-
|
|
3023
|
-
|
|
3024
|
-
-
|
|
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
|
-
|
|
3027
|
-
|
|
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
|
-
|
|
3030
|
-
|
|
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
|
-
|
|
3096
|
+
Follow these steps to set up the Google Ads (OAuth) connection.
|
|
3033
3097
|
|
|
3034
|
-
|
|
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
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
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/
|
|
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
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
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 {
|
|
3194
|
+
return {
|
|
3195
|
+
success: false,
|
|
3196
|
+
error: `Connection ${connectionId} not found`
|
|
3197
|
+
};
|
|
3088
3198
|
}
|
|
3089
|
-
console.log(
|
|
3199
|
+
console.log(
|
|
3200
|
+
`[connector-request] google-ads-oauth/${connection.name}: ${method} ${path}`
|
|
3201
|
+
);
|
|
3090
3202
|
try {
|
|
3091
|
-
const
|
|
3092
|
-
const
|
|
3093
|
-
const
|
|
3094
|
-
const
|
|
3095
|
-
const
|
|
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
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
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
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
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/
|
|
3130
|
-
var
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
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
|
-
###
|
|
3144
|
-
-
|
|
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
|
-
###
|
|
3147
|
-
-
|
|
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
|
-
###
|
|
3150
|
-
|
|
3151
|
-
|
|
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
|
-
###
|
|
3154
|
-
|
|
3155
|
-
- Body: { "app": 1, "record": { "fieldName": { "value": "value" } } }
|
|
3288
|
+
### Common Segments
|
|
3289
|
+
segments.date, segments.device, segments.ad_network_type
|
|
3156
3290
|
|
|
3157
|
-
###
|
|
3158
|
-
-
|
|
3159
|
-
-
|
|
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
|
-
|
|
3165
|
-
|
|
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
|
-
|
|
3168
|
-
import { connection } from "@squadbase/vite-server/connectors/kintone";
|
|
3300
|
+
## Google Ads SDK (TypeScript handler)
|
|
3169
3301
|
|
|
3170
|
-
|
|
3302
|
+
\`\`\`ts
|
|
3303
|
+
import { connection } from "@squadbase/vite-server/connectors/google-ads-oauth";
|
|
3171
3304
|
|
|
3172
|
-
|
|
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
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
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
|
-
//
|
|
3182
|
-
const
|
|
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:
|
|
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/
|
|
3361
|
+
// src/connectors/google-analytics/tools/request.ts
|
|
3194
3362
|
import { z as z17 } from "zod";
|
|
3195
|
-
var
|
|
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
|
|
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., '
|
|
3202
|
-
body: z17.record(z17.string(), z17.unknown()).optional().describe("
|
|
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
|
|
3383
|
+
var requestTool3 = new ConnectorTool({
|
|
3216
3384
|
name: "request",
|
|
3217
|
-
description: `Send authenticated requests to the
|
|
3218
|
-
Authentication is handled automatically using
|
|
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]
|
|
3395
|
+
console.log(`[connector-request] google-analytics/${connection.name}: ${method} ${path}`);
|
|
3227
3396
|
try {
|
|
3228
|
-
const
|
|
3229
|
-
const
|
|
3230
|
-
const
|
|
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:
|
|
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?.
|
|
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/
|
|
3264
|
-
var
|
|
3265
|
-
var
|
|
3266
|
-
slug: "
|
|
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: "
|
|
3269
|
-
description: "Connect to
|
|
3270
|
-
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/
|
|
3271
|
-
parameters:
|
|
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: `##
|
|
3274
|
-
- Call the
|
|
3275
|
-
-
|
|
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
|
-
###
|
|
3281
|
-
-
|
|
3282
|
-
- Body: { "query": { "paging": { "limit": 50, "offset": 0 } } }
|
|
3458
|
+
### Get Metadata (Check available dimensions and metrics)
|
|
3459
|
+
- GET properties/{propertyId}/metadata
|
|
3283
3460
|
|
|
3284
|
-
###
|
|
3285
|
-
- POST
|
|
3286
|
-
- Body
|
|
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
|
-
###
|
|
3289
|
-
|
|
3290
|
-
|
|
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
|
-
###
|
|
3295
|
-
|
|
3296
|
-
|
|
3475
|
+
### Common Metrics
|
|
3476
|
+
activeUsers, sessions, screenPageViews, bounceRate,
|
|
3477
|
+
averageSessionDuration, conversions, totalRevenue
|
|
3297
3478
|
|
|
3298
|
-
###
|
|
3299
|
-
-
|
|
3300
|
-
-
|
|
3479
|
+
### Date Specification
|
|
3480
|
+
- Absolute: "2024-01-01"
|
|
3481
|
+
- Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
|
|
3301
3482
|
|
|
3302
|
-
##
|
|
3303
|
-
Non-SQL connectors like
|
|
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/
|
|
3487
|
+
import { connection } from "@squadbase/vite-server/connectors/google-analytics";
|
|
3307
3488
|
|
|
3308
|
-
const
|
|
3489
|
+
const ga = connection("<connectionId>");
|
|
3309
3490
|
|
|
3310
3491
|
// Authenticated fetch (returns standard Response)
|
|
3311
|
-
const res = await
|
|
3492
|
+
const res = await ga.request("properties/{propertyId}:runReport", {
|
|
3312
3493
|
method: "POST",
|
|
3313
|
-
|
|
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 {
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
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:
|
|
3511
|
+
tools: tools12
|
|
3327
3512
|
});
|
|
3328
3513
|
|
|
3329
|
-
// src/connectors/
|
|
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
|
-
|
|
3333
|
-
|
|
3334
|
-
if (
|
|
3335
|
-
|
|
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(
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
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
|
-
|
|
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
|
|
3354
|
-
name: "
|
|
3355
|
-
description:
|
|
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
|
|
3575
|
+
async execute({ connectionId }, connections, config) {
|
|
3361
3576
|
const connection = connections.find((c) => c.id === connectionId);
|
|
3362
3577
|
if (!connection) {
|
|
3363
|
-
return {
|
|
3578
|
+
return {
|
|
3579
|
+
success: false,
|
|
3580
|
+
error: `Connection ${connectionId} not found`
|
|
3581
|
+
};
|
|
3364
3582
|
}
|
|
3365
|
-
console.log(
|
|
3583
|
+
console.log(
|
|
3584
|
+
`[connector-request] google-analytics-oauth/${connection.name}: listAccounts`
|
|
3585
|
+
);
|
|
3366
3586
|
try {
|
|
3367
|
-
const
|
|
3368
|
-
const
|
|
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(
|
|
3592
|
+
const response = await fetch(proxyUrl, {
|
|
3378
3593
|
method: "POST",
|
|
3379
3594
|
headers: {
|
|
3380
|
-
|
|
3381
|
-
|
|
3595
|
+
"Content-Type": "application/json",
|
|
3596
|
+
Authorization: `Bearer ${token}`
|
|
3382
3597
|
},
|
|
3383
|
-
body: JSON.stringify({
|
|
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
|
-
|
|
3389
|
-
|
|
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
|
-
|
|
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/
|
|
3412
|
-
|
|
3413
|
-
var
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
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
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
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
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
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
|
|
3527
|
-
|
|
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
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
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
|
|
3542
|
-
name: "
|
|
3543
|
-
description:
|
|
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,
|
|
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-
|
|
3698
|
+
`[connector-request] google-analytics-oauth/${connection.name}: listProperties for ${accountName}`
|
|
3558
3699
|
);
|
|
3559
|
-
let connectionUrl;
|
|
3560
3700
|
try {
|
|
3561
|
-
const
|
|
3562
|
-
|
|
3563
|
-
const
|
|
3564
|
-
|
|
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
|
|
3571
|
-
const
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
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
|
-
|
|
3735
|
+
clearTimeout(timeout);
|
|
3581
3736
|
}
|
|
3582
3737
|
} catch (err) {
|
|
3583
|
-
|
|
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/
|
|
3593
|
-
var
|
|
3594
|
-
var
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
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
|
-
|
|
3626
|
-
|
|
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
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
await
|
|
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/
|
|
3644
|
-
var
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
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/
|
|
3658
|
-
import OpenAI from "openai";
|
|
3985
|
+
import { connection } from "@squadbase/vite-server/connectors/google-analytics-oauth";
|
|
3659
3986
|
|
|
3660
|
-
const
|
|
3661
|
-
const client = new OpenAI({ apiKey });
|
|
3987
|
+
const ga = connection("<connectionId>");
|
|
3662
3988
|
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
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:
|
|
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
|
|
3673
|
-
var
|
|
3674
|
-
var
|
|
3675
|
-
var
|
|
3676
|
-
async function
|
|
3677
|
-
if (
|
|
3678
|
-
return
|
|
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
|
-
|
|
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
|
|
3707
|
-
toolUseIntent:
|
|
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:
|
|
3711
|
-
method:
|
|
3712
|
-
path:
|
|
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:
|
|
4080
|
+
queryParams: z21.record(z21.string(), z21.string()).optional().describe("Query parameters to append to the URL")
|
|
3716
4081
|
});
|
|
3717
|
-
var
|
|
3718
|
-
|
|
3719
|
-
success:
|
|
3720
|
-
status:
|
|
3721
|
-
data:
|
|
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
|
-
|
|
3724
|
-
success:
|
|
3725
|
-
error:
|
|
4088
|
+
z21.object({
|
|
4089
|
+
success: z21.literal(false),
|
|
4090
|
+
error: z21.string()
|
|
3726
4091
|
})
|
|
3727
4092
|
]);
|
|
3728
|
-
var
|
|
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:
|
|
3734
|
-
outputSchema:
|
|
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 =
|
|
4112
|
+
const spreadsheetId = parameters5.spreadsheetId.tryGetValue(connection);
|
|
3748
4113
|
const resolvedPath = spreadsheetId ? path.replace(/\{spreadsheetId\}/g, spreadsheetId) : path;
|
|
3749
|
-
let url = `${
|
|
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
|
|
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(),
|
|
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_${
|
|
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
|
|
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/
|
|
3862
|
-
parameters:
|
|
3863
|
-
releaseFlag: { dev1: true, dev2:
|
|
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:
|
|
4278
|
+
tools: tools14,
|
|
3914
4279
|
async checkConnection(params, config) {
|
|
3915
4280
|
const { proxyFetch } = config;
|
|
3916
|
-
const spreadsheetId = params[
|
|
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
|
-
|
|
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
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
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
|
-
|
|
4044
|
-
|
|
4045
|
-
return {
|
|
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/
|
|
4305
|
+
// src/connectors/kintone/tools/request.ts
|
|
4051
4306
|
import { z as z22 } from "zod";
|
|
4052
|
-
var
|
|
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
|
-
|
|
4088
|
-
),
|
|
4089
|
-
|
|
4090
|
-
|
|
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
|
-
|
|
4098
|
-
|
|
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
|
|
4111
|
-
name: "
|
|
4112
|
-
description:
|
|
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,
|
|
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
|
|
4128
|
-
const
|
|
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(),
|
|
4345
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS12);
|
|
4131
4346
|
try {
|
|
4132
|
-
const
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
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
|
-
|
|
4155
|
-
|
|
4156
|
-
propertyId
|
|
4362
|
+
success: false,
|
|
4363
|
+
error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
4157
4364
|
};
|
|
4158
|
-
}
|
|
4159
|
-
return { success: true,
|
|
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/
|
|
4171
|
-
var
|
|
4172
|
-
var
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
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
|
-
|
|
4186
|
-
|
|
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
|
-
|
|
4193
|
-
|
|
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
|
-
###
|
|
4199
|
-
-
|
|
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
|
-
###
|
|
4202
|
-
-
|
|
4203
|
-
-
|
|
4204
|
-
en: `## Google Analytics Setup Instructions
|
|
4401
|
+
### Add Record
|
|
4402
|
+
- POST record.json
|
|
4403
|
+
- Body: { "app": 1, "record": { "fieldName": { "value": "value" } } }
|
|
4205
4404
|
|
|
4206
|
-
|
|
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
|
-
|
|
4412
|
+
## kintone SDK (TypeScript handler)
|
|
4413
|
+
Non-SQL connectors like kintone can also be used via the SDK in TypeScript handlers:
|
|
4209
4414
|
|
|
4210
|
-
|
|
4211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4223
|
-
1
|
|
4224
|
-
|
|
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
|
-
|
|
4229
|
-
|
|
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
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
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/
|
|
4441
|
+
// src/connectors/wix-store/tools/request.ts
|
|
4237
4442
|
import { z as z23 } from "zod";
|
|
4238
|
-
var
|
|
4239
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4296
|
-
Authentication is handled automatically
|
|
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
|
|
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
|
|
4313
|
-
const
|
|
4314
|
-
const url = `${
|
|
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(),
|
|
4480
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS13);
|
|
4319
4481
|
try {
|
|
4320
|
-
const response = await fetch(
|
|
4321
|
-
method
|
|
4482
|
+
const response = await fetch(url, {
|
|
4483
|
+
method,
|
|
4322
4484
|
headers: {
|
|
4323
|
-
|
|
4324
|
-
|
|
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
|
|
4339
|
-
return {
|
|
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/
|
|
4353
|
-
var
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
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
|
-
###
|
|
4384
|
-
-
|
|
4528
|
+
### List Products
|
|
4529
|
+
- POST stores/v1/products/query
|
|
4530
|
+
- Body: { "query": { "paging": { "limit": 50, "offset": 0 } } }
|
|
4385
4531
|
|
|
4386
|
-
###
|
|
4387
|
-
- POST
|
|
4388
|
-
- Body
|
|
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
|
-
###
|
|
4397
|
-
|
|
4398
|
-
|
|
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
|
-
###
|
|
4401
|
-
|
|
4402
|
-
|
|
4542
|
+
### List Collections (Categories)
|
|
4543
|
+
- POST stores/v1/collections/query
|
|
4544
|
+
- Body: { "query": { "paging": { "limit": 50 } } }
|
|
4403
4545
|
|
|
4404
|
-
###
|
|
4405
|
-
-
|
|
4406
|
-
-
|
|
4546
|
+
### List Inventory Items
|
|
4547
|
+
- POST stores/v2/inventoryItems/query
|
|
4548
|
+
- Body: { "query": { "paging": { "limit": 50 } } }
|
|
4407
4549
|
|
|
4408
|
-
##
|
|
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/
|
|
4554
|
+
import { connection } from "@squadbase/vite-server/connectors/wix-store";
|
|
4412
4555
|
|
|
4413
|
-
const
|
|
4556
|
+
const wix = connection("<connectionId>");
|
|
4414
4557
|
|
|
4415
4558
|
// Authenticated fetch (returns standard Response)
|
|
4416
|
-
const res = await
|
|
4559
|
+
const res = await wix.request("/stores/v1/products/query", {
|
|
4417
4560
|
method: "POST",
|
|
4418
|
-
|
|
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 {
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
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:
|
|
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/
|
|
4577
|
+
// src/connectors/dbt/tools/request.ts
|
|
4463
4578
|
import { z as z24 } from "zod";
|
|
4464
|
-
var
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
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
|
-
|
|
4500
|
-
),
|
|
4501
|
-
|
|
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
|
-
|
|
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
|
|
4519
|
-
name: "
|
|
4520
|
-
description:
|
|
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
|
|
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
|
|
4536
|
-
const token =
|
|
4537
|
-
const
|
|
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(),
|
|
4623
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS14);
|
|
4540
4624
|
try {
|
|
4541
|
-
const response = await fetch(
|
|
4625
|
+
const response = await fetch(endpoint, {
|
|
4542
4626
|
method: "POST",
|
|
4543
4627
|
headers: {
|
|
4544
|
-
|
|
4545
|
-
|
|
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
|
-
|
|
4559
|
-
|
|
4636
|
+
return {
|
|
4637
|
+
success: false,
|
|
4638
|
+
error: data?.message ?? `HTTP ${response.status} ${response.statusText}`
|
|
4639
|
+
};
|
|
4560
4640
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
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,
|
|
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/
|
|
4619
|
-
var
|
|
4620
|
-
var
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
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
|
-
###
|
|
4641
|
-
|
|
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
|
-
###
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
4717
|
+
### Lineage (Dependencies)
|
|
4718
|
+
- Traverse relationships using ancestors / children fields
|
|
4719
|
+
- Get ancestors { uniqueId name } or children { uniqueId name } within node
|
|
4651
4720
|
|
|
4652
|
-
|
|
4653
|
-
|
|
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
|
-
|
|
4660
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
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
|
-
//
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
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
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
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
|
|
4713
|
-
|
|
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
|
-
|
|
4723
|
-
|
|
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
|
|
4731
|
-
name: "
|
|
4732
|
-
description: `
|
|
4733
|
-
|
|
4734
|
-
|
|
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,
|
|
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-
|
|
4805
|
+
`[connector-query] squadbase-db/${connection.name}: ${sql}`
|
|
4747
4806
|
);
|
|
4807
|
+
let connectionUrl;
|
|
4748
4808
|
try {
|
|
4749
|
-
const
|
|
4750
|
-
|
|
4751
|
-
const
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
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
|
|
4759
|
-
const
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
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
|
-
|
|
4828
|
+
await pool.end();
|
|
4786
4829
|
}
|
|
4787
4830
|
} catch (err) {
|
|
4788
|
-
|
|
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/
|
|
4795
|
-
var
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
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
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
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
|
-
|
|
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/
|
|
4850
|
-
|
|
4851
|
-
const ads = connection("<connectionId>");
|
|
4905
|
+
import { connection } from "@squadbase/vite-server/connectors/openai";
|
|
4906
|
+
import OpenAI from "openai";
|
|
4852
4907
|
|
|
4853
|
-
|
|
4854
|
-
const
|
|
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
|
-
|
|
4860
|
-
|
|
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,
|