@squadbase/connectors 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -52,6 +52,12 @@ interface ProxyPolicyRule {
52
52
  interface ProxyPolicy {
53
53
  allowlist: ProxyPolicyRule[];
54
54
  }
55
+ type ConnectionCheckResult = {
56
+ success: true;
57
+ } | {
58
+ success: false;
59
+ error: string;
60
+ };
55
61
  interface ConnectorToolsConfig {
56
62
  oauthProxy: {
57
63
  appApiKey: string;
@@ -97,6 +103,9 @@ declare class ConnectorPlugin<P extends Record<string, ParameterDefinition> = Re
97
103
  }) => Promise<{
98
104
  rows: Record<string, unknown>[];
99
105
  }>;
106
+ readonly checkConnection?: (params: Record<string, string>, config: {
107
+ proxyFetch: typeof fetch;
108
+ }) => Promise<ConnectionCheckResult>;
100
109
  constructor(config: {
101
110
  slug: string;
102
111
  authType: string | null;
@@ -115,6 +124,9 @@ declare class ConnectorPlugin<P extends Record<string, ParameterDefinition> = Re
115
124
  }) => Promise<{
116
125
  rows: Record<string, unknown>[];
117
126
  }>;
127
+ checkConnection?: (params: Record<string, string>, config: {
128
+ proxyFetch: typeof fetch;
129
+ }) => Promise<ConnectionCheckResult>;
118
130
  });
119
131
  get connectorKey(): string;
120
132
  /**
@@ -790,4 +802,4 @@ declare const squadbaseDbConnector: ConnectorPlugin<{
790
802
  }>;
791
803
  }>;
792
804
 
793
- export { AUTH_TYPES, ConnectorPlugin, ConnectorSetup, ConnectorTool, type ConnectorToolsConfig, ParameterDefinition, type ProxyPolicy, type ProxyPolicyRule, type ReleaseFlag, type SetupLanguage, airtableConnector, awsAthenaConnector, bigqueryConnector, bigqueryOauthConnector, connectors, databricksConnector, dbtConnector, googleAnalyticsConnector, kintoneConnector, mysqlConnector, postgresqlConnector, redshiftConnector, snowflakeConnector, snowflakePatConnector, squadbaseDbConnector, wixStoreConnector };
805
+ export { AUTH_TYPES, type ConnectionCheckResult, ConnectorPlugin, ConnectorSetup, ConnectorTool, type ConnectorToolsConfig, ParameterDefinition, type ProxyPolicy, type ProxyPolicyRule, type ReleaseFlag, type SetupLanguage, airtableConnector, awsAthenaConnector, bigqueryConnector, bigqueryOauthConnector, connectors, databricksConnector, dbtConnector, googleAnalyticsConnector, kintoneConnector, mysqlConnector, postgresqlConnector, redshiftConnector, snowflakeConnector, snowflakePatConnector, squadbaseDbConnector, wixStoreConnector };
package/dist/index.js CHANGED
@@ -92,6 +92,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
92
92
  systemPrompt;
93
93
  tools;
94
94
  query;
95
+ checkConnection;
95
96
  constructor(config) {
96
97
  this.slug = config.slug;
97
98
  this.authType = config.authType;
@@ -106,6 +107,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
106
107
  this.systemPrompt = config.systemPrompt;
107
108
  this.tools = config.tools;
108
109
  this.query = config.query;
110
+ this.checkConnection = config.checkConnection;
109
111
  }
110
112
  get connectorKey() {
111
113
  return _ConnectorPlugin.deriveKey(this.slug, this.authType);
@@ -439,6 +441,45 @@ var snowflakeConnector = new ConnectorPlugin({
439
441
  - List columns: \`DESCRIBE TABLE db_name.schema_name.table_name\`
440
442
  - INFORMATION_SCHEMA is also available: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``,
441
443
  tools,
444
+ async checkConnection(params, _config) {
445
+ try {
446
+ const snowflake = (await import("snowflake-sdk")).default;
447
+ snowflake.configure({ logLevel: "ERROR" });
448
+ const privateKey = Buffer.from(
449
+ params[parameters.privateKeyBase64.slug],
450
+ "base64"
451
+ ).toString("utf-8");
452
+ const conn = snowflake.createConnection({
453
+ account: params[parameters.account.slug],
454
+ username: params[parameters.user.slug],
455
+ role: params[parameters.role.slug],
456
+ warehouse: params[parameters.warehouse.slug],
457
+ authenticator: "SNOWFLAKE_JWT",
458
+ privateKey
459
+ });
460
+ await new Promise((resolve, reject) => {
461
+ conn.connect((err) => {
462
+ if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
463
+ else resolve();
464
+ });
465
+ });
466
+ await new Promise((resolve, reject) => {
467
+ conn.execute({
468
+ sqlText: "SELECT 1",
469
+ complete: (err) => {
470
+ if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
471
+ else resolve();
472
+ }
473
+ });
474
+ });
475
+ conn.destroy((err) => {
476
+ if (err) console.warn(`[connector-client] Snowflake destroy error: ${err.message}`);
477
+ });
478
+ return { success: true };
479
+ } catch (e) {
480
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
481
+ }
482
+ },
442
483
  async query(params, sql, namedParams) {
443
484
  const resolvedSql = replaceLiteralParams(sql, namedParams);
444
485
  const snowflake = (await import("snowflake-sdk")).default;
@@ -653,6 +694,45 @@ var snowflakePatConnector = new ConnectorPlugin({
653
694
  - List columns: \`DESCRIBE TABLE db_name.schema_name.table_name\`
654
695
  - INFORMATION_SCHEMA is also available: \`SELECT * FROM db_name.INFORMATION_SCHEMA.TABLES\``,
655
696
  tools: tools2,
697
+ async checkConnection(params, _config) {
698
+ try {
699
+ const snowflake = (await import("snowflake-sdk")).default;
700
+ snowflake.configure({ logLevel: "ERROR" });
701
+ const conn = snowflake.createConnection({
702
+ account: params[parameters2.account.slug],
703
+ username: params[parameters2.user.slug],
704
+ role: params[parameters2.role.slug],
705
+ warehouse: params[parameters2.warehouse.slug],
706
+ password: params[parameters2.pat.slug]
707
+ });
708
+ await new Promise((resolve, reject) => {
709
+ conn.connect((err) => {
710
+ if (err)
711
+ reject(new Error(`Snowflake connect failed: ${err.message}`));
712
+ else resolve();
713
+ });
714
+ });
715
+ await new Promise((resolve, reject) => {
716
+ conn.execute({
717
+ sqlText: "SELECT 1",
718
+ complete: (err) => {
719
+ if (err)
720
+ reject(new Error(`Snowflake query failed: ${err.message}`));
721
+ else resolve();
722
+ }
723
+ });
724
+ });
725
+ conn.destroy((err) => {
726
+ if (err)
727
+ console.warn(
728
+ `[connector-client] Snowflake destroy error: ${err.message}`
729
+ );
730
+ });
731
+ return { success: true };
732
+ } catch (e) {
733
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
734
+ }
735
+ },
656
736
  async query(params, sql, namedParams) {
657
737
  const resolvedSql = replaceLiteralParams(sql, namedParams);
658
738
  const snowflake = (await import("snowflake-sdk")).default;
@@ -797,6 +877,22 @@ var postgresqlConnector = new ConnectorPlugin({
797
877
  - List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
798
878
  - Always include LIMIT in queries`,
799
879
  tools: tools3,
880
+ async checkConnection(params, _config) {
881
+ const { Pool } = await import("pg");
882
+ const pool = new Pool({
883
+ connectionString: params[parameters3.connectionUrl.slug],
884
+ ssl: { rejectUnauthorized: false },
885
+ connectionTimeoutMillis: 1e4
886
+ });
887
+ try {
888
+ await pool.query("SELECT 1");
889
+ return { success: true };
890
+ } catch (error) {
891
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
892
+ } finally {
893
+ await pool.end();
894
+ }
895
+ },
800
896
  async query(params, sql, namedParams) {
801
897
  const { Pool } = await import("pg");
802
898
  const { text, values } = buildPositionalParams(sql, namedParams);
@@ -921,6 +1017,21 @@ var mysqlConnector = new ConnectorPlugin({
921
1017
  - List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xxx'\`
922
1018
  - Always include LIMIT in queries`,
923
1019
  tools: tools4,
1020
+ async checkConnection(params, _config) {
1021
+ const mysql = await import("mysql2/promise");
1022
+ const pool = mysql.createPool({
1023
+ uri: params[parameters4.connectionUrl.slug],
1024
+ connectTimeout: 1e4
1025
+ });
1026
+ try {
1027
+ await pool.query("SELECT 1");
1028
+ return { success: true };
1029
+ } catch (e) {
1030
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
1031
+ } finally {
1032
+ await pool.end();
1033
+ }
1034
+ },
924
1035
  async query(params, sql, namedParams) {
925
1036
  const mysql = await import("mysql2/promise");
926
1037
  const { text, values } = buildQuestionmarkParams(sql, namedParams);
@@ -942,7 +1053,94 @@ var mysqlConnector = new ConnectorPlugin({
942
1053
  }
943
1054
  });
944
1055
 
1056
+ // src/connectors/bigquery/tools/list-projects.ts
1057
+ import { z as z5 } from "zod";
1058
+
1059
+ // src/connectors/bigquery/parameters.ts
1060
+ var parameters5 = {
1061
+ serviceAccountKeyJsonBase64: new ParameterDefinition({
1062
+ slug: "service-account-key-json-base64",
1063
+ name: "Google Cloud Service Account JSON",
1064
+ description: "The service account JSON key used to authenticate with Google Cloud Platform. Ensure that the service account has the necessary permissions to access the required resources.",
1065
+ envVarBaseKey: "BIGQUERY_SERVICE_ACCOUNT_JSON_BASE64",
1066
+ type: "base64EncodedJson",
1067
+ secret: true,
1068
+ required: true
1069
+ }),
1070
+ projectId: new ParameterDefinition({
1071
+ slug: "project-id",
1072
+ name: "Google Cloud Project ID",
1073
+ description: "The ID of the Google Cloud project where resources will be managed.",
1074
+ envVarBaseKey: "BIGQUERY_PROJECT_ID",
1075
+ type: "text",
1076
+ secret: false,
1077
+ required: false
1078
+ })
1079
+ };
1080
+
1081
+ // src/connectors/bigquery/tools/list-projects.ts
1082
+ var inputSchema5 = z5.object({
1083
+ toolUseIntent: z5.string().optional().describe(
1084
+ "Brief description of what you intend to accomplish with this tool call"
1085
+ ),
1086
+ connectionId: z5.string().describe("ID of the BigQuery connection to use")
1087
+ });
1088
+ var outputSchema5 = z5.discriminatedUnion("success", [
1089
+ z5.object({
1090
+ success: z5.literal(true),
1091
+ projects: z5.array(
1092
+ z5.object({
1093
+ projectId: z5.string(),
1094
+ friendlyName: z5.string()
1095
+ })
1096
+ )
1097
+ }),
1098
+ z5.object({
1099
+ success: z5.literal(false),
1100
+ error: z5.string()
1101
+ })
1102
+ ]);
1103
+ var listProjectsTool = new ConnectorTool({
1104
+ name: "listProjects",
1105
+ description: `List GCP projects accessible with the service account credentials. Returns project IDs and friendly names.`,
1106
+ inputSchema: inputSchema5,
1107
+ outputSchema: outputSchema5,
1108
+ async execute({ connectionId }, connections) {
1109
+ const connection = connections.find((c) => c.id === connectionId);
1110
+ if (!connection) {
1111
+ return {
1112
+ success: false,
1113
+ error: `Connection ${connectionId} not found`
1114
+ };
1115
+ }
1116
+ try {
1117
+ const serviceAccountJsonBase64 = parameters5.serviceAccountKeyJsonBase64.getValue(connection);
1118
+ const credentials = JSON.parse(
1119
+ Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
1120
+ );
1121
+ const { GoogleAuth } = await import("google-auth-library");
1122
+ const auth = new GoogleAuth({
1123
+ credentials,
1124
+ scopes: ["https://www.googleapis.com/auth/bigquery"]
1125
+ });
1126
+ const client = await auth.getClient();
1127
+ const res = await client.request({
1128
+ url: "https://bigquery.googleapis.com/bigquery/v2/projects"
1129
+ });
1130
+ const projects = (res.data.projects ?? []).map((p) => ({
1131
+ projectId: p.projectReference?.projectId ?? "",
1132
+ friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
1133
+ })).filter((p) => p.projectId !== "");
1134
+ return { success: true, projects };
1135
+ } catch (err) {
1136
+ const msg = err instanceof Error ? err.message : String(err);
1137
+ return { success: false, error: msg };
1138
+ }
1139
+ }
1140
+ });
1141
+
945
1142
  // src/connectors/bigquery/setup.ts
1143
+ var listProjectsToolName = `bigquery_${listProjectsTool.name}`;
946
1144
  var bigquerySetup = new ConnectorSetup({
947
1145
  ja: `## BigQuery \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
948
1146
 
@@ -950,32 +1148,27 @@ var bigquerySetup = new ConnectorSetup({
950
1148
 
951
1149
  ### \u624B\u9806
952
1150
 
953
- #### \u30B9\u30C6\u30C3\u30D70: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
954
- 1. \`listProjects\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
955
- 2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
956
- - **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: false\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u629E\u3055\u305B\u308B\u3002\u9078\u629E\u80A2\u306E label \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u306B\u3059\u308B
957
- - **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
958
- 3. \`updateConnectionParameters\` \u3067\u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
1151
+ #### \u30B9\u30C6\u30C3\u30D71: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
1152
+ 1. \`${listProjectsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
1153
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3057\u3066\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
959
1154
  - \`parameterSlug\`: \`"project-id"\`
960
- - \`value\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
961
- - \`displayValue\`: \`"\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)"\`
962
- 4. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D71\u306B\u9032\u3080
1155
+ - \`options\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
1156
+ 3. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
963
1157
 
964
- #### \u30B9\u30C6\u30C3\u30D71: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
1158
+ #### \u30B9\u30C6\u30C3\u30D72: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
965
1159
  1. \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` \u3092\u5B9F\u884C\u3057\u3066\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
966
1160
  2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
967
1161
  - **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u629E\u3055\u305B\u308B
968
1162
  - **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
969
1163
 
970
- #### \u30B9\u30C6\u30C3\u30D72: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
1164
+ #### \u30B9\u30C6\u30C3\u30D73: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
971
1165
  3. \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306B\u5BFE\u3057\u3066 \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` \u3092\u5B9F\u884C\u3057\u3066\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\uFF08\u8907\u6570\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306E\u5834\u5408\u306F UNION ALL \u3067\u4E00\u62EC\u53D6\u5F97\uFF09
972
1166
  4. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
973
1167
  - **\u30C6\u30FC\u30D6\u30EB\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u3055\u305B\u308B\u3002description \u306B\u306F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\u3092\u8A18\u8F09\u3059\u308B
974
1168
  - **\u30C6\u30FC\u30D6\u30EB\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528
975
1169
 
976
- #### \u30B9\u30C6\u30C3\u30D73: \u4FDD\u5B58
1170
+ #### \u30B9\u30C6\u30C3\u30D74: \u4FDD\u5B58
977
1171
  5. \`updateConnectionContext\` \u3067\u4EE5\u4E0B\u3092\u4FDD\u5B58\u3059\u308B:
978
- - \`project\`: BigQuery\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
979
1172
  - \`dataset\`: \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
980
1173
  - \`tables\`: \u9078\u629E\u3055\u308C\u305F\u30C6\u30FC\u30D6\u30EB\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
981
1174
  - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
@@ -993,32 +1186,27 @@ Follow these steps to set up the BigQuery connection.
993
1186
 
994
1187
  ### Steps
995
1188
 
996
- #### Step 0: Project Selection
997
- 1. Call \`listProjects\` to get the list of GCP projects accessible with the service account credentials
998
- 2. Branch based on results:
999
- - **2 or more projects**: Present them to the user via \`askUserQuestion\` (multiSelect: false) and let them select a project. Format option labels as \`Project Name (id: project-id)\`
1000
- - **Exactly 1 project**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected project X" in one sentence
1001
- 3. Call \`updateConnectionParameters\` to save the selected project:
1189
+ #### Step 1: Project Selection
1190
+ 1. Call \`${listProjectsToolName}\` to get the list of GCP projects accessible with the service account credentials
1191
+ 2. Call \`updateConnectionParameters\` to save the project:
1002
1192
  - \`parameterSlug\`: \`"project-id"\`
1003
- - \`value\`: the project ID
1004
- - \`displayValue\`: \`"Project Name (id: project-id)"\`
1005
- 4. After receiving the continuation message, proceed to Step 1
1193
+ - \`options\`: The project list. Each option's \`label\` should be \`Project Name (id: project-id)\`, \`value\` should be the project ID
1194
+ 3. After receiving the continuation message, proceed to Step 2
1006
1195
 
1007
- #### Step 1: Dataset Selection
1196
+ #### Step 2: Dataset Selection
1008
1197
  1. Run \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` to get the list of datasets
1009
1198
  2. Branch based on results:
1010
1199
  - **2 or more datasets**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which datasets to use
1011
1200
  - **Exactly 1 dataset**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected dataset X" in one sentence
1012
1201
 
1013
- #### Step 2: Table Selection
1202
+ #### Step 3: Table Selection
1014
1203
  3. For the selected dataset(s), run \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` to get the table list (use UNION ALL for multiple datasets)
1015
1204
  4. Branch based on results:
1016
1205
  - **2 or more tables**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which tables to use. Include the dataset name in the description
1017
1206
  - **Exactly 1 table**: Do NOT call askUserQuestion. Auto-select it
1018
1207
 
1019
- #### Step 3: Save
1208
+ #### Step 4: Save
1020
1209
  5. Call \`updateConnectionContext\` to save:
1021
- - \`project\`: BigQuery project ID
1022
1210
  - \`dataset\`: Selected dataset name(s) (comma-separated if multiple)
1023
1211
  - \`tables\`: Selected table name(s) (comma-separated if multiple)
1024
1212
  - \`note\`: Brief description of the setup
@@ -1032,50 +1220,28 @@ Follow these steps to set up the BigQuery connection.
1032
1220
  - Skip unnecessary explanations and proceed efficiently`
1033
1221
  });
1034
1222
 
1035
- // src/connectors/bigquery/parameters.ts
1036
- var parameters5 = {
1037
- serviceAccountKeyJsonBase64: new ParameterDefinition({
1038
- slug: "service-account-key-json-base64",
1039
- name: "Google Cloud Service Account JSON",
1040
- description: "The service account JSON key used to authenticate with Google Cloud Platform. Ensure that the service account has the necessary permissions to access the required resources.",
1041
- envVarBaseKey: "BIGQUERY_SERVICE_ACCOUNT_JSON_BASE64",
1042
- type: "base64EncodedJson",
1043
- secret: true,
1044
- required: true
1045
- }),
1046
- projectId: new ParameterDefinition({
1047
- slug: "project-id",
1048
- name: "Google Cloud Project ID",
1049
- description: "The ID of the Google Cloud project where resources will be managed.",
1050
- envVarBaseKey: "BIGQUERY_PROJECT_ID",
1051
- type: "text",
1052
- secret: false,
1053
- required: false
1054
- })
1055
- };
1056
-
1057
1223
  // src/connectors/bigquery/tools/execute-query.ts
1058
- import { z as z5 } from "zod";
1224
+ import { z as z6 } from "zod";
1059
1225
  var MAX_ROWS5 = 500;
1060
- var inputSchema5 = z5.object({
1061
- toolUseIntent: z5.string().optional().describe(
1226
+ var inputSchema6 = z6.object({
1227
+ toolUseIntent: z6.string().optional().describe(
1062
1228
  "Brief description of what you intend to accomplish with this tool call"
1063
1229
  ),
1064
- connectionId: z5.string().describe("ID of the BigQuery connection to use"),
1065
- sql: z5.string().describe(
1230
+ connectionId: z6.string().describe("ID of the BigQuery connection to use"),
1231
+ sql: z6.string().describe(
1066
1232
  "BigQuery SQL (GoogleSQL) query. Use backtick-quoted fully qualified names `project.dataset.table` for table references."
1067
1233
  )
1068
1234
  });
1069
- var outputSchema5 = z5.discriminatedUnion("success", [
1070
- z5.object({
1071
- success: z5.literal(true),
1072
- rowCount: z5.number(),
1073
- truncated: z5.boolean(),
1074
- rows: z5.array(z5.record(z5.string(), z5.unknown()))
1235
+ var outputSchema6 = z6.discriminatedUnion("success", [
1236
+ z6.object({
1237
+ success: z6.literal(true),
1238
+ rowCount: z6.number(),
1239
+ truncated: z6.boolean(),
1240
+ rows: z6.array(z6.record(z6.string(), z6.unknown()))
1075
1241
  }),
1076
- z5.object({
1077
- success: z5.literal(false),
1078
- error: z5.string()
1242
+ z6.object({
1243
+ success: z6.literal(false),
1244
+ error: z6.string()
1079
1245
  })
1080
1246
  ]);
1081
1247
  var executeQueryTool5 = new ConnectorTool({
@@ -1083,8 +1249,8 @@ var executeQueryTool5 = new ConnectorTool({
1083
1249
  description: `Execute SQL against BigQuery. Returns up to ${MAX_ROWS5} rows.
1084
1250
  Use for: schema exploration (INFORMATION_SCHEMA), data sampling, analytical queries.
1085
1251
  Avoid loading large amounts of data; always include LIMIT in queries.`,
1086
- inputSchema: inputSchema5,
1087
- outputSchema: outputSchema5,
1252
+ inputSchema: inputSchema6,
1253
+ outputSchema: outputSchema6,
1088
1254
  async execute({ connectionId, sql }, connections) {
1089
1255
  const connection = connections.find((c) => c.id === connectionId);
1090
1256
  if (!connection) {
@@ -1121,68 +1287,6 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
1121
1287
  }
1122
1288
  });
1123
1289
 
1124
- // src/connectors/bigquery/tools/list-projects.ts
1125
- import { z as z6 } from "zod";
1126
- var inputSchema6 = z6.object({
1127
- toolUseIntent: z6.string().optional().describe(
1128
- "Brief description of what you intend to accomplish with this tool call"
1129
- ),
1130
- connectionId: z6.string().describe("ID of the BigQuery connection to use")
1131
- });
1132
- var outputSchema6 = z6.discriminatedUnion("success", [
1133
- z6.object({
1134
- success: z6.literal(true),
1135
- projects: z6.array(
1136
- z6.object({
1137
- projectId: z6.string(),
1138
- friendlyName: z6.string()
1139
- })
1140
- )
1141
- }),
1142
- z6.object({
1143
- success: z6.literal(false),
1144
- error: z6.string()
1145
- })
1146
- ]);
1147
- var listProjectsTool = new ConnectorTool({
1148
- name: "listProjects",
1149
- description: `List GCP projects accessible with the service account credentials. Returns project IDs and friendly names.`,
1150
- inputSchema: inputSchema6,
1151
- outputSchema: outputSchema6,
1152
- async execute({ connectionId }, connections) {
1153
- const connection = connections.find((c) => c.id === connectionId);
1154
- if (!connection) {
1155
- return {
1156
- success: false,
1157
- error: `Connection ${connectionId} not found`
1158
- };
1159
- }
1160
- try {
1161
- const serviceAccountJsonBase64 = parameters5.serviceAccountKeyJsonBase64.getValue(connection);
1162
- const credentials = JSON.parse(
1163
- Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
1164
- );
1165
- const { GoogleAuth } = await import("google-auth-library");
1166
- const auth = new GoogleAuth({
1167
- credentials,
1168
- scopes: ["https://www.googleapis.com/auth/bigquery"]
1169
- });
1170
- const client = await auth.getClient();
1171
- const res = await client.request({
1172
- url: "https://bigquery.googleapis.com/bigquery/v2/projects"
1173
- });
1174
- const projects = (res.data.projects ?? []).map((p) => ({
1175
- projectId: p.projectReference?.projectId ?? "",
1176
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
1177
- })).filter((p) => p.projectId !== "");
1178
- return { success: true, projects };
1179
- } catch (err) {
1180
- const msg = err instanceof Error ? err.message : String(err);
1181
- return { success: false, error: msg };
1182
- }
1183
- }
1184
- });
1185
-
1186
1290
  // src/connectors/bigquery/index.ts
1187
1291
  var tools5 = { executeQuery: executeQueryTool5, listProjects: listProjectsTool };
1188
1292
  var bigqueryConnector = new ConnectorPlugin({
@@ -1202,6 +1306,23 @@ var bigqueryConnector = new ConnectorPlugin({
1202
1306
  - List columns: \`SELECT column_name, data_type FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'xxx'\`
1203
1307
  - Always specify project_id explicitly in queries`,
1204
1308
  tools: tools5,
1309
+ async checkConnection(params, _config) {
1310
+ const { BigQuery } = await import("@google-cloud/bigquery");
1311
+ const credentials = JSON.parse(
1312
+ Buffer.from(params[parameters5.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
1313
+ );
1314
+ const bq = new BigQuery({
1315
+ projectId: params[parameters5.projectId.slug],
1316
+ credentials
1317
+ });
1318
+ try {
1319
+ const [job] = await bq.createQueryJob({ query: "SELECT 1" });
1320
+ await job.getQueryResults();
1321
+ return { success: true };
1322
+ } catch (error) {
1323
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
1324
+ }
1325
+ },
1205
1326
  async query(params, sql, namedParams) {
1206
1327
  const { BigQuery } = await import("@google-cloud/bigquery");
1207
1328
  const resolvedSql = replaceLiteralParams(sql, namedParams);
@@ -1218,7 +1339,115 @@ var bigqueryConnector = new ConnectorPlugin({
1218
1339
  }
1219
1340
  });
1220
1341
 
1342
+ // src/connectors/bigquery-oauth/tools/list-projects.ts
1343
+ import { z as z7 } from "zod";
1344
+ var REQUEST_TIMEOUT_MS = 6e4;
1345
+ var cachedToken = null;
1346
+ async function getProxyToken(config) {
1347
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
1348
+ return cachedToken.token;
1349
+ }
1350
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
1351
+ const res = await fetch(url, {
1352
+ method: "POST",
1353
+ headers: {
1354
+ "Content-Type": "application/json",
1355
+ "x-api-key": config.appApiKey,
1356
+ "project-id": config.projectId
1357
+ },
1358
+ body: JSON.stringify({
1359
+ sandboxId: config.sandboxId,
1360
+ issuedBy: "coding-agent"
1361
+ })
1362
+ });
1363
+ if (!res.ok) {
1364
+ const errorText = await res.text().catch(() => res.statusText);
1365
+ throw new Error(`Failed to get proxy token: HTTP ${res.status} ${errorText}`);
1366
+ }
1367
+ const data = await res.json();
1368
+ cachedToken = {
1369
+ token: data.token,
1370
+ expiresAt: new Date(data.expiresAt).getTime()
1371
+ };
1372
+ return data.token;
1373
+ }
1374
+ var inputSchema7 = z7.object({
1375
+ toolUseIntent: z7.string().optional().describe(
1376
+ "Brief description of what you intend to accomplish with this tool call"
1377
+ ),
1378
+ connectionId: z7.string().describe("ID of the BigQuery OAuth connection to use")
1379
+ });
1380
+ var outputSchema7 = z7.discriminatedUnion("success", [
1381
+ z7.object({
1382
+ success: z7.literal(true),
1383
+ projects: z7.array(
1384
+ z7.object({
1385
+ projectId: z7.string(),
1386
+ friendlyName: z7.string()
1387
+ })
1388
+ )
1389
+ }),
1390
+ z7.object({
1391
+ success: z7.literal(false),
1392
+ error: z7.string()
1393
+ })
1394
+ ]);
1395
+ var listProjectsTool2 = new ConnectorTool({
1396
+ name: "listProjects",
1397
+ description: `List GCP projects accessible with the current OAuth credentials. Returns project IDs and friendly names.`,
1398
+ inputSchema: inputSchema7,
1399
+ outputSchema: outputSchema7,
1400
+ async execute({ connectionId }, connections, config) {
1401
+ const connection = connections.find((c) => c.id === connectionId);
1402
+ if (!connection) {
1403
+ return {
1404
+ success: false,
1405
+ error: `Connection ${connectionId} not found`
1406
+ };
1407
+ }
1408
+ console.log(
1409
+ `[connector-query] bigquery-oauth/${connection.name}: listProjects`
1410
+ );
1411
+ try {
1412
+ const token = await getProxyToken(config.oauthProxy);
1413
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
1414
+ const controller = new AbortController();
1415
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
1416
+ try {
1417
+ const response = await fetch(proxyUrl, {
1418
+ method: "POST",
1419
+ headers: {
1420
+ "Content-Type": "application/json",
1421
+ Authorization: `Bearer ${token}`
1422
+ },
1423
+ body: JSON.stringify({
1424
+ url: "https://bigquery.googleapis.com/bigquery/v2/projects",
1425
+ method: "GET"
1426
+ }),
1427
+ signal: controller.signal
1428
+ });
1429
+ const data = await response.json();
1430
+ if (!response.ok) {
1431
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
1432
+ return { success: false, error: errorMessage };
1433
+ }
1434
+ const projects = (data.projects ?? []).map((p) => ({
1435
+ projectId: p.projectReference?.projectId ?? "",
1436
+ friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
1437
+ })).filter((p) => p.projectId !== "");
1438
+ return { success: true, projects };
1439
+ } finally {
1440
+ clearTimeout(timeout);
1441
+ }
1442
+ } catch (err) {
1443
+ const msg = err instanceof Error ? err.message : String(err);
1444
+ return { success: false, error: msg };
1445
+ }
1446
+ }
1447
+ });
1448
+
1221
1449
  // src/connectors/bigquery-oauth/setup.ts
1450
+ var listProjectsToolName2 = `bigquery-oauth_${listProjectsTool2.name}`;
1222
1451
  var bigquerySetup2 = new ConnectorSetup({
1223
1452
  ja: `## BigQuery \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
1224
1453
 
@@ -1226,32 +1455,27 @@ var bigquerySetup2 = new ConnectorSetup({
1226
1455
 
1227
1456
  ### \u624B\u9806
1228
1457
 
1229
- #### \u30B9\u30C6\u30C3\u30D70: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
1230
- 1. \`listProjects\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
1231
- 2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
1232
- - **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: false\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9078\u629E\u3055\u305B\u308B\u3002\u9078\u629E\u80A2\u306E label \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u306B\u3059\u308B
1233
- - **\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
1234
- 3. \`updateConnectionParameters\` \u3067\u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
1458
+ #### \u30B9\u30C6\u30C3\u30D71: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u9078\u629E
1459
+ 1. \`${listProjectsToolName2}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGCP\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
1460
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3057\u3066\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3059\u308B:
1235
1461
  - \`parameterSlug\`: \`"project-id"\`
1236
- - \`value\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
1237
- - \`displayValue\`: \`"\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)"\`
1238
- 4. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D71\u306B\u9032\u3080
1462
+ - \`options\`: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D (id: \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
1463
+ 3. \u7D9A\u884C\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u3051\u53D6\u3063\u305F\u3089\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
1239
1464
 
1240
- #### \u30B9\u30C6\u30C3\u30D71: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
1465
+ #### \u30B9\u30C6\u30C3\u30D72: \u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u9078\u629E
1241
1466
  1. \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` \u3092\u5B9F\u884C\u3057\u3066\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
1242
1467
  2. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
1243
1468
  - **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u3092\u9078\u629E\u3055\u305B\u308B
1244
1469
  - **\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528\u3002\u300C\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8 X \u3092\u81EA\u52D5\u9078\u629E\u3057\u307E\u3057\u305F\u300D\u30681\u6587\u3060\u3051\u66F8\u304F
1245
1470
 
1246
- #### \u30B9\u30C6\u30C3\u30D72: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
1471
+ #### \u30B9\u30C6\u30C3\u30D73: \u30C6\u30FC\u30D6\u30EB\u9078\u629E
1247
1472
  3. \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306B\u5BFE\u3057\u3066 \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` \u3092\u5B9F\u884C\u3057\u3066\u30C6\u30FC\u30D6\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\uFF08\u8907\u6570\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u306E\u5834\u5408\u306F UNION ALL \u3067\u4E00\u62EC\u53D6\u5F97\uFF09
1248
1473
  4. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
1249
1474
  - **\u30C6\u30FC\u30D6\u30EB\u304C2\u3064\u4EE5\u4E0A**: \`askUserQuestion\`\uFF08multiSelect: true\uFF09\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u63D0\u793A\u3057\u3001\u4F7F\u7528\u3059\u308B\u30C6\u30FC\u30D6\u30EB\u3092\u9078\u629E\u3055\u305B\u308B\u3002description \u306B\u306F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\u3092\u8A18\u8F09\u3059\u308B
1250
1475
  - **\u30C6\u30FC\u30D6\u30EB\u304C1\u3064\u3060\u3051**: askUserQuestion \u306F\u4F7F\u308F\u305A\u81EA\u52D5\u63A1\u7528
1251
1476
 
1252
- #### \u30B9\u30C6\u30C3\u30D73: \u4FDD\u5B58
1477
+ #### \u30B9\u30C6\u30C3\u30D74: \u4FDD\u5B58
1253
1478
  5. \`updateConnectionContext\` \u3067\u4EE5\u4E0B\u3092\u4FDD\u5B58\u3059\u308B:
1254
- - \`project\`: BigQuery\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8ID
1255
1479
  - \`dataset\`: \u9078\u629E\u3055\u308C\u305F\u30C7\u30FC\u30BF\u30BB\u30C3\u30C8\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
1256
1480
  - \`tables\`: \u9078\u629E\u3055\u308C\u305F\u30C6\u30FC\u30D6\u30EB\u540D\uFF08\u8907\u6570\u306E\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
1257
1481
  - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
@@ -1269,32 +1493,27 @@ Follow these steps to set up the BigQuery connection.
1269
1493
 
1270
1494
  ### Steps
1271
1495
 
1272
- #### Step 0: Project Selection
1273
- 1. Call \`listProjects\` to get the list of GCP projects accessible with the OAuth credentials
1274
- 2. Branch based on results:
1275
- - **2 or more projects**: Present them to the user via \`askUserQuestion\` (multiSelect: false) and let them select a project. Format option labels as \`Project Name (id: project-id)\`
1276
- - **Exactly 1 project**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected project X" in one sentence
1277
- 3. Call \`updateConnectionParameters\` to save the selected project:
1496
+ #### Step 1: Project Selection
1497
+ 1. Call \`${listProjectsToolName2}\` to get the list of GCP projects accessible with the OAuth credentials
1498
+ 2. Call \`updateConnectionParameters\` to save the project:
1278
1499
  - \`parameterSlug\`: \`"project-id"\`
1279
- - \`value\`: the project ID
1280
- - \`displayValue\`: \`"Project Name (id: project-id)"\`
1281
- 4. After receiving the continuation message, proceed to Step 1
1500
+ - \`options\`: The project list. Each option's \`label\` should be \`Project Name (id: project-id)\`, \`value\` should be the project ID
1501
+ 3. After receiving the continuation message, proceed to Step 2
1282
1502
 
1283
- #### Step 1: Dataset Selection
1503
+ #### Step 2: Dataset Selection
1284
1504
  1. Run \`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA\` to get the list of datasets
1285
1505
  2. Branch based on results:
1286
1506
  - **2 or more datasets**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which datasets to use
1287
1507
  - **Exactly 1 dataset**: Do NOT call askUserQuestion. Auto-select it. Just write "Auto-selected dataset X" in one sentence
1288
1508
 
1289
- #### Step 2: Table Selection
1509
+ #### Step 3: Table Selection
1290
1510
  3. For the selected dataset(s), run \`SELECT table_name FROM \\\`{project}.{dataset}.INFORMATION_SCHEMA.TABLES\\\`\` to get the table list (use UNION ALL for multiple datasets)
1291
1511
  4. Branch based on results:
1292
1512
  - **2 or more tables**: Present them to the user via \`askUserQuestion\` (multiSelect: true) and let them select which tables to use. Include the dataset name in the description
1293
1513
  - **Exactly 1 table**: Do NOT call askUserQuestion. Auto-select it
1294
1514
 
1295
- #### Step 3: Save
1515
+ #### Step 4: Save
1296
1516
  5. Call \`updateConnectionContext\` to save:
1297
- - \`project\`: BigQuery project ID
1298
1517
  - \`dataset\`: Selected dataset name(s) (comma-separated if multiple)
1299
1518
  - \`tables\`: Selected table name(s) (comma-separated if multiple)
1300
1519
  - \`note\`: Brief description of the setup
@@ -1321,14 +1540,14 @@ var parameters6 = {
1321
1540
  })
1322
1541
  };
1323
1542
 
1324
- // src/connectors/bigquery-oauth/tools/execute-query.ts
1325
- import { z as z7 } from "zod";
1326
- var MAX_ROWS6 = 500;
1327
- var REQUEST_TIMEOUT_MS = 6e4;
1328
- var cachedToken = null;
1329
- async function getProxyToken(config) {
1330
- if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
1331
- return cachedToken.token;
1543
+ // src/connectors/bigquery-oauth/tools/execute-query.ts
1544
+ import { z as z8 } from "zod";
1545
+ var MAX_ROWS6 = 500;
1546
+ var REQUEST_TIMEOUT_MS2 = 6e4;
1547
+ var cachedToken2 = null;
1548
+ async function getProxyToken2(config) {
1549
+ if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
1550
+ return cachedToken2.token;
1332
1551
  }
1333
1552
  const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
1334
1553
  const res = await fetch(url, {
@@ -1348,31 +1567,31 @@ async function getProxyToken(config) {
1348
1567
  throw new Error(`Failed to get proxy token: HTTP ${res.status} ${errorText}`);
1349
1568
  }
1350
1569
  const data = await res.json();
1351
- cachedToken = {
1570
+ cachedToken2 = {
1352
1571
  token: data.token,
1353
1572
  expiresAt: new Date(data.expiresAt).getTime()
1354
1573
  };
1355
1574
  return data.token;
1356
1575
  }
1357
- var inputSchema7 = z7.object({
1358
- toolUseIntent: z7.string().optional().describe(
1576
+ var inputSchema8 = z8.object({
1577
+ toolUseIntent: z8.string().optional().describe(
1359
1578
  "Brief description of what you intend to accomplish with this tool call"
1360
1579
  ),
1361
- connectionId: z7.string().describe("ID of the BigQuery OAuth connection to use"),
1362
- sql: z7.string().describe(
1580
+ connectionId: z8.string().describe("ID of the BigQuery OAuth connection to use"),
1581
+ sql: z8.string().describe(
1363
1582
  "BigQuery SQL (GoogleSQL) query. Use backtick-quoted fully qualified names `project.dataset.table` for table references."
1364
1583
  )
1365
1584
  });
1366
- var outputSchema7 = z7.discriminatedUnion("success", [
1367
- z7.object({
1368
- success: z7.literal(true),
1369
- rowCount: z7.number(),
1370
- truncated: z7.boolean(),
1371
- rows: z7.array(z7.record(z7.string(), z7.unknown()))
1585
+ var outputSchema8 = z8.discriminatedUnion("success", [
1586
+ z8.object({
1587
+ success: z8.literal(true),
1588
+ rowCount: z8.number(),
1589
+ truncated: z8.boolean(),
1590
+ rows: z8.array(z8.record(z8.string(), z8.unknown()))
1372
1591
  }),
1373
- z7.object({
1374
- success: z7.literal(false),
1375
- error: z7.string()
1592
+ z8.object({
1593
+ success: z8.literal(false),
1594
+ error: z8.string()
1376
1595
  })
1377
1596
  ]);
1378
1597
  function parseQueryResponse(data) {
@@ -1393,8 +1612,8 @@ var executeQueryTool6 = new ConnectorTool({
1393
1612
  description: `Execute SQL against BigQuery via OAuth. Returns up to ${MAX_ROWS6} rows.
1394
1613
  Use for: schema exploration (INFORMATION_SCHEMA), data sampling, analytical queries.
1395
1614
  Avoid loading large amounts of data; always include LIMIT in queries.`,
1396
- inputSchema: inputSchema7,
1397
- outputSchema: outputSchema7,
1615
+ inputSchema: inputSchema8,
1616
+ outputSchema: outputSchema8,
1398
1617
  async execute({ connectionId, sql }, connections, config) {
1399
1618
  const connection = connections.find((c) => c.id === connectionId);
1400
1619
  if (!connection) {
@@ -1408,11 +1627,11 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
1408
1627
  `[connector-query] bigquery-oauth/${connection.name}: ${sql}`
1409
1628
  );
1410
1629
  try {
1411
- const token = await getProxyToken(config.oauthProxy);
1630
+ const token = await getProxyToken2(config.oauthProxy);
1412
1631
  const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
1413
1632
  const queryUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${gcpProjectId}/queries`;
1414
1633
  const controller = new AbortController();
1415
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
1634
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
1416
1635
  try {
1417
1636
  const response = await fetch(proxyUrl, {
1418
1637
  method: "POST",
@@ -1450,113 +1669,6 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
1450
1669
  }
1451
1670
  });
1452
1671
 
1453
- // src/connectors/bigquery-oauth/tools/list-projects.ts
1454
- import { z as z8 } from "zod";
1455
- var REQUEST_TIMEOUT_MS2 = 6e4;
1456
- var cachedToken2 = null;
1457
- async function getProxyToken2(config) {
1458
- if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
1459
- return cachedToken2.token;
1460
- }
1461
- const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
1462
- const res = await fetch(url, {
1463
- method: "POST",
1464
- headers: {
1465
- "Content-Type": "application/json",
1466
- "x-api-key": config.appApiKey,
1467
- "project-id": config.projectId
1468
- },
1469
- body: JSON.stringify({
1470
- sandboxId: config.sandboxId,
1471
- issuedBy: "coding-agent"
1472
- })
1473
- });
1474
- if (!res.ok) {
1475
- const errorText = await res.text().catch(() => res.statusText);
1476
- throw new Error(`Failed to get proxy token: HTTP ${res.status} ${errorText}`);
1477
- }
1478
- const data = await res.json();
1479
- cachedToken2 = {
1480
- token: data.token,
1481
- expiresAt: new Date(data.expiresAt).getTime()
1482
- };
1483
- return data.token;
1484
- }
1485
- var inputSchema8 = z8.object({
1486
- toolUseIntent: z8.string().optional().describe(
1487
- "Brief description of what you intend to accomplish with this tool call"
1488
- ),
1489
- connectionId: z8.string().describe("ID of the BigQuery OAuth connection to use")
1490
- });
1491
- var outputSchema8 = z8.discriminatedUnion("success", [
1492
- z8.object({
1493
- success: z8.literal(true),
1494
- projects: z8.array(
1495
- z8.object({
1496
- projectId: z8.string(),
1497
- friendlyName: z8.string()
1498
- })
1499
- )
1500
- }),
1501
- z8.object({
1502
- success: z8.literal(false),
1503
- error: z8.string()
1504
- })
1505
- ]);
1506
- var listProjectsTool2 = new ConnectorTool({
1507
- name: "listProjects",
1508
- description: `List GCP projects accessible with the current OAuth credentials. Returns project IDs and friendly names.`,
1509
- inputSchema: inputSchema8,
1510
- outputSchema: outputSchema8,
1511
- async execute({ connectionId }, connections, config) {
1512
- const connection = connections.find((c) => c.id === connectionId);
1513
- if (!connection) {
1514
- return {
1515
- success: false,
1516
- error: `Connection ${connectionId} not found`
1517
- };
1518
- }
1519
- console.log(
1520
- `[connector-query] bigquery-oauth/${connection.name}: listProjects`
1521
- );
1522
- try {
1523
- const token = await getProxyToken2(config.oauthProxy);
1524
- const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
1525
- const controller = new AbortController();
1526
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
1527
- try {
1528
- const response = await fetch(proxyUrl, {
1529
- method: "POST",
1530
- headers: {
1531
- "Content-Type": "application/json",
1532
- Authorization: `Bearer ${token}`
1533
- },
1534
- body: JSON.stringify({
1535
- url: "https://bigquery.googleapis.com/bigquery/v2/projects",
1536
- method: "GET"
1537
- }),
1538
- signal: controller.signal
1539
- });
1540
- const data = await response.json();
1541
- if (!response.ok) {
1542
- const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
1543
- return { success: false, error: errorMessage };
1544
- }
1545
- const projects = (data.projects ?? []).map((p) => ({
1546
- projectId: p.projectReference?.projectId ?? "",
1547
- friendlyName: p.friendlyName ?? p.projectReference?.projectId ?? ""
1548
- })).filter((p) => p.projectId !== "");
1549
- return { success: true, projects };
1550
- } finally {
1551
- clearTimeout(timeout);
1552
- }
1553
- } catch (err) {
1554
- const msg = err instanceof Error ? err.message : String(err);
1555
- return { success: false, error: msg };
1556
- }
1557
- }
1558
- });
1559
-
1560
1672
  // src/connectors/bigquery-oauth/index.ts
1561
1673
  var tools6 = { executeQuery: executeQueryTool6, listProjects: listProjectsTool2 };
1562
1674
  function parseQueryResponse2(data) {
@@ -1594,6 +1706,25 @@ var bigqueryOauthConnector = new ConnectorPlugin({
1594
1706
  - List columns: \`SELECT column_name, data_type FROM \\\`project_id.dataset\\\`.INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'xxx'\`
1595
1707
  - Always specify project_id explicitly in queries`,
1596
1708
  tools: tools6,
1709
+ async checkConnection(params, config) {
1710
+ const { proxyFetch } = config;
1711
+ const projectId = params[parameters6.projectId.slug];
1712
+ const url = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries`;
1713
+ try {
1714
+ const res = await proxyFetch(url, {
1715
+ method: "POST",
1716
+ headers: { "Content-Type": "application/json" },
1717
+ body: JSON.stringify({ query: "SELECT 1", useLegacySql: false })
1718
+ });
1719
+ if (!res.ok) {
1720
+ const errorText = await res.text().catch(() => res.statusText);
1721
+ return { success: false, error: `BigQuery query failed: HTTP ${res.status} ${errorText}` };
1722
+ }
1723
+ return { success: true };
1724
+ } catch (error) {
1725
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
1726
+ }
1727
+ },
1597
1728
  async query(params, sql, namedParams, context) {
1598
1729
  const { proxyFetch } = context;
1599
1730
  const projectId = params[parameters6.projectId.slug];
@@ -1798,6 +1929,62 @@ var awsAthenaConnector = new ConnectorPlugin({
1798
1929
  - Always include LIMIT in queries
1799
1930
  - Query execution is asynchronous and results may take time to retrieve`,
1800
1931
  tools: tools7,
1932
+ async checkConnection(params, _config) {
1933
+ try {
1934
+ const {
1935
+ AthenaClient,
1936
+ StartQueryExecutionCommand,
1937
+ GetQueryExecutionCommand
1938
+ } = await import("@aws-sdk/client-athena");
1939
+ const workgroup = params[parameters7.workgroup.slug];
1940
+ const outputLocation = params[parameters7.outputLocation.slug];
1941
+ if (!workgroup && !outputLocation) {
1942
+ return {
1943
+ success: false,
1944
+ error: "Either workgroup or output-location is required"
1945
+ };
1946
+ }
1947
+ const client = new AthenaClient({
1948
+ region: params[parameters7.awsRegion.slug],
1949
+ credentials: {
1950
+ accessKeyId: params[parameters7.awsAccessKeyId.slug],
1951
+ secretAccessKey: params[parameters7.awsSecretAccessKey.slug]
1952
+ }
1953
+ });
1954
+ const startParams = { QueryString: "SELECT 1" };
1955
+ if (workgroup) startParams.WorkGroup = workgroup;
1956
+ if (outputLocation) {
1957
+ startParams.ResultConfiguration = { OutputLocation: outputLocation };
1958
+ }
1959
+ const { QueryExecutionId } = await client.send(
1960
+ new StartQueryExecutionCommand(startParams)
1961
+ );
1962
+ const startTime = Date.now();
1963
+ while (true) {
1964
+ const exec = await client.send(
1965
+ new GetQueryExecutionCommand({ QueryExecutionId })
1966
+ );
1967
+ const state = exec.QueryExecution?.Status?.State;
1968
+ if (state === "SUCCEEDED") break;
1969
+ if (state === "FAILED" || state === "CANCELLED") {
1970
+ return {
1971
+ success: false,
1972
+ error: exec.QueryExecution?.Status?.StateChangeReason || `Query ${state}`
1973
+ };
1974
+ }
1975
+ if (Date.now() - startTime > 12e4) {
1976
+ return { success: false, error: "Query timed out after 120 seconds" };
1977
+ }
1978
+ await new Promise((r) => setTimeout(r, 1e3));
1979
+ }
1980
+ return { success: true };
1981
+ } catch (error) {
1982
+ return {
1983
+ success: false,
1984
+ error: error instanceof Error ? error.message : String(error)
1985
+ };
1986
+ }
1987
+ },
1801
1988
  async query(params, sql, namedParams) {
1802
1989
  const {
1803
1990
  AthenaClient,
@@ -2075,6 +2262,65 @@ var redshiftConnector = new ConnectorPlugin({
2075
2262
  - Always include LIMIT in queries
2076
2263
  - Query execution is asynchronous and results may take time to retrieve`,
2077
2264
  tools: tools8,
2265
+ async checkConnection(params, _config) {
2266
+ try {
2267
+ const {
2268
+ RedshiftDataClient,
2269
+ ExecuteStatementCommand,
2270
+ DescribeStatementCommand
2271
+ } = await import("@aws-sdk/client-redshift-data");
2272
+ const clusterIdentifier = params[parameters8.clusterIdentifier.slug];
2273
+ const workgroupName = params[parameters8.workgroupName.slug];
2274
+ const secretArn = params[parameters8.secretArn.slug];
2275
+ const dbUser = params[parameters8.dbUser.slug];
2276
+ if (!clusterIdentifier && !workgroupName) {
2277
+ return {
2278
+ success: false,
2279
+ error: "Either cluster-identifier or workgroup-name is required"
2280
+ };
2281
+ }
2282
+ const client = new RedshiftDataClient({
2283
+ region: params[parameters8.awsRegion.slug],
2284
+ credentials: {
2285
+ accessKeyId: params[parameters8.awsAccessKeyId.slug],
2286
+ secretAccessKey: params[parameters8.awsSecretAccessKey.slug]
2287
+ }
2288
+ });
2289
+ const { Id: statementId } = await client.send(
2290
+ new ExecuteStatementCommand({
2291
+ Database: params[parameters8.database.slug],
2292
+ Sql: "SELECT 1",
2293
+ ...clusterIdentifier && { ClusterIdentifier: clusterIdentifier },
2294
+ ...workgroupName && { WorkgroupName: workgroupName },
2295
+ ...secretArn && { SecretArn: secretArn },
2296
+ ...dbUser && { DbUser: dbUser }
2297
+ })
2298
+ );
2299
+ const startTime = Date.now();
2300
+ while (true) {
2301
+ const desc = await client.send(
2302
+ new DescribeStatementCommand({ Id: statementId })
2303
+ );
2304
+ if (desc.Status === "FINISHED") break;
2305
+ if (desc.Status === "FAILED" || desc.Status === "ABORTED") {
2306
+ return {
2307
+ success: false,
2308
+ error: desc.Error || `Statement ${desc.Status}`
2309
+ };
2310
+ }
2311
+ if (Date.now() - startTime > 12e4) {
2312
+ return { success: false, error: "Query timed out after 120 seconds" };
2313
+ }
2314
+ await new Promise((r) => setTimeout(r, 1e3));
2315
+ }
2316
+ return { success: true };
2317
+ } catch (error) {
2318
+ return {
2319
+ success: false,
2320
+ error: error instanceof Error ? error.message : String(error)
2321
+ };
2322
+ }
2323
+ },
2078
2324
  async query(params, sql, namedParams) {
2079
2325
  const {
2080
2326
  RedshiftDataClient,
@@ -2263,6 +2509,34 @@ var databricksConnector = new ConnectorPlugin({
2263
2509
  - List columns: \`DESCRIBE TABLE table_name\`
2264
2510
  - Always include LIMIT in queries`,
2265
2511
  tools: tools9,
2512
+ async checkConnection(params, _config) {
2513
+ const { DBSQLClient } = await import("@databricks/sql");
2514
+ const client = new DBSQLClient();
2515
+ await client.connect({
2516
+ host: params[parameters9.host.slug],
2517
+ path: params[parameters9.httpPath.slug],
2518
+ token: params[parameters9.token.slug]
2519
+ });
2520
+ let session;
2521
+ let operation;
2522
+ try {
2523
+ session = await client.openSession();
2524
+ operation = await session.executeStatement("SELECT 1", {
2525
+ runAsync: true
2526
+ });
2527
+ await operation.fetchAll();
2528
+ return { success: true };
2529
+ } catch (e) {
2530
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
2531
+ } finally {
2532
+ if (operation) await operation.close().catch(() => {
2533
+ });
2534
+ if (session) await session.close().catch(() => {
2535
+ });
2536
+ await client.close().catch(() => {
2537
+ });
2538
+ }
2539
+ },
2266
2540
  async query(params, sql, namedParams) {
2267
2541
  const { DBSQLClient } = await import("@databricks/sql");
2268
2542
  const resolvedSql = replaceLiteralParams(sql, namedParams);
@@ -3124,6 +3398,22 @@ var squadbaseDbConnector = new ConnectorPlugin({
3124
3398
  - List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
3125
3399
  - Always include LIMIT in queries`,
3126
3400
  tools: tools15,
3401
+ async checkConnection(params, _config) {
3402
+ const { Pool } = await import("pg");
3403
+ const pool = new Pool({
3404
+ connectionString: params[parameters15.connectionUrl.slug],
3405
+ ssl: { rejectUnauthorized: false },
3406
+ connectionTimeoutMillis: 1e4
3407
+ });
3408
+ try {
3409
+ await pool.query("SELECT 1");
3410
+ return { success: true };
3411
+ } catch (error) {
3412
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
3413
+ } finally {
3414
+ await pool.end();
3415
+ }
3416
+ },
3127
3417
  async query(params, sql, namedParams) {
3128
3418
  const { Pool } = await import("pg");
3129
3419
  const { text, values } = buildPositionalParams(sql, namedParams);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/connectors",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Squadbase Connectors",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",