@squadbase/connectors 0.1.1 → 0.1.2-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,8 +5,11 @@ import {
5
5
  parameters3,
6
6
  parameters4,
7
7
  parameters5,
8
- parameters6
9
- } from "./chunk-4K4NERCT.js";
8
+ parameters6,
9
+ parameters7,
10
+ parameters8,
11
+ parameters9
12
+ } from "./chunk-Q5TIPE53.js";
10
13
 
11
14
  // src/connector-setup.ts
12
15
  var ConnectorSetup = class {
@@ -232,7 +235,7 @@ Follow these steps to set up the Snowflake connection.
232
235
  });
233
236
 
234
237
  // src/connectors/snowflake/parameters.ts
235
- var parameters7 = {
238
+ var parameters10 = {
236
239
  account: new ParameterDefinition({
237
240
  slug: "account",
238
241
  name: "Snowflake Account",
@@ -348,15 +351,15 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
348
351
  try {
349
352
  const snowflake = (await import("snowflake-sdk")).default;
350
353
  snowflake.configure({ logLevel: "ERROR" });
351
- const account = parameters7.account.getValue(connection);
352
- const user = parameters7.user.getValue(connection);
353
- const role = parameters7.role.getValue(connection);
354
- const warehouse = parameters7.warehouse.getValue(connection);
355
- const privateKeyBase64 = parameters7.privateKeyBase64.getValue(connection);
354
+ const account = parameters10.account.getValue(connection);
355
+ const user = parameters10.user.getValue(connection);
356
+ const role = parameters10.role.getValue(connection);
357
+ const warehouse = parameters10.warehouse.getValue(connection);
358
+ const privateKeyBase64 = parameters10.privateKeyBase64.getValue(connection);
356
359
  const privateKeyPem = Buffer.from(privateKeyBase64, "base64").toString(
357
360
  "utf-8"
358
361
  );
359
- const privateKeyPass = parameters7.privateKeyPassphrase.tryGetValue(connection);
362
+ const privateKeyPass = parameters10.privateKeyPassphrase.tryGetValue(connection);
360
363
  const privateKey = decryptPrivateKey(privateKeyPem, privateKeyPass ?? void 0);
361
364
  const conn = snowflake.createConnection({
362
365
  account,
@@ -421,7 +424,7 @@ var snowflakeConnector = new ConnectorPlugin({
421
424
  name: "Snowflake",
422
425
  description: "Connect to Snowflake for cloud data warehousing and analytics.",
423
426
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6oyVtAcP3pMlXaOrts9unk/b7a9dc25d15c388b66e983041b855447/snowflake.svg",
424
- parameters: parameters7,
427
+ parameters: parameters10,
425
428
  releaseFlag: { dev1: true, dev2: true, prod: true },
426
429
  setup: snowflakeSetup,
427
430
  systemPrompt: `## Snowflake SQL Notes
@@ -438,16 +441,16 @@ var snowflakeConnector = new ConnectorPlugin({
438
441
  const snowflake = (await import("snowflake-sdk")).default;
439
442
  snowflake.configure({ logLevel: "ERROR" });
440
443
  const privateKeyPem = Buffer.from(
441
- params[parameters7.privateKeyBase64.slug],
444
+ params[parameters10.privateKeyBase64.slug],
442
445
  "base64"
443
446
  ).toString("utf-8");
444
- const privateKeyPass = params[parameters7.privateKeyPassphrase.slug] || void 0;
447
+ const privateKeyPass = params[parameters10.privateKeyPassphrase.slug] || void 0;
445
448
  const privateKey = decryptPrivateKey(privateKeyPem, privateKeyPass);
446
449
  const conn = snowflake.createConnection({
447
- account: params[parameters7.account.slug],
448
- username: params[parameters7.user.slug],
449
- role: params[parameters7.role.slug],
450
- warehouse: params[parameters7.warehouse.slug],
450
+ account: params[parameters10.account.slug],
451
+ username: params[parameters10.user.slug],
452
+ role: params[parameters10.role.slug],
453
+ warehouse: params[parameters10.warehouse.slug],
451
454
  authenticator: "SNOWFLAKE_JWT",
452
455
  privateKey
453
456
  });
@@ -479,16 +482,16 @@ var snowflakeConnector = new ConnectorPlugin({
479
482
  const snowflake = (await import("snowflake-sdk")).default;
480
483
  snowflake.configure({ logLevel: "ERROR" });
481
484
  const privateKeyPem = Buffer.from(
482
- params[parameters7.privateKeyBase64.slug],
485
+ params[parameters10.privateKeyBase64.slug],
483
486
  "base64"
484
487
  ).toString("utf-8");
485
- const privateKeyPass = params[parameters7.privateKeyPassphrase.slug] || void 0;
488
+ const privateKeyPass = params[parameters10.privateKeyPassphrase.slug] || void 0;
486
489
  const privateKey = decryptPrivateKey(privateKeyPem, privateKeyPass);
487
490
  const conn = snowflake.createConnection({
488
- account: params[parameters7.account.slug],
489
- username: params[parameters7.user.slug],
490
- role: params[parameters7.role.slug],
491
- warehouse: params[parameters7.warehouse.slug],
491
+ account: params[parameters10.account.slug],
492
+ username: params[parameters10.user.slug],
493
+ role: params[parameters10.role.slug],
494
+ warehouse: params[parameters10.warehouse.slug],
492
495
  authenticator: "SNOWFLAKE_JWT",
493
496
  privateKey
494
497
  });
@@ -517,7 +520,7 @@ var snowflakeConnector = new ConnectorPlugin({
517
520
  });
518
521
 
519
522
  // src/connectors/snowflake-pat/parameters.ts
520
- var parameters8 = {
523
+ var parameters11 = {
521
524
  account: new ParameterDefinition({
522
525
  slug: "account",
523
526
  name: "Snowflake Account",
@@ -611,11 +614,11 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
611
614
  try {
612
615
  const snowflake = (await import("snowflake-sdk")).default;
613
616
  snowflake.configure({ logLevel: "ERROR" });
614
- const account = parameters8.account.getValue(connection);
615
- const user = parameters8.user.getValue(connection);
616
- const role = parameters8.role.getValue(connection);
617
- const warehouse = parameters8.warehouse.getValue(connection);
618
- const password = parameters8.pat.getValue(connection);
617
+ const account = parameters11.account.getValue(connection);
618
+ const user = parameters11.user.getValue(connection);
619
+ const role = parameters11.role.getValue(connection);
620
+ const warehouse = parameters11.warehouse.getValue(connection);
621
+ const password = parameters11.pat.getValue(connection);
619
622
  const conn = snowflake.createConnection({
620
623
  account,
621
624
  username: user,
@@ -678,7 +681,7 @@ var snowflakePatConnector = new ConnectorPlugin({
678
681
  name: "Snowflake (PAT)",
679
682
  description: "Connect to Snowflake using a Personal Access Token (PAT).",
680
683
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6oyVtAcP3pMlXaOrts9unk/b7a9dc25d15c388b66e983041b855447/snowflake.svg",
681
- parameters: parameters8,
684
+ parameters: parameters11,
682
685
  releaseFlag: { dev1: true, dev2: true, prod: true },
683
686
  setup: snowflakeSetup,
684
687
  systemPrompt: `## Snowflake SQL Notes
@@ -695,11 +698,11 @@ var snowflakePatConnector = new ConnectorPlugin({
695
698
  const snowflake = (await import("snowflake-sdk")).default;
696
699
  snowflake.configure({ logLevel: "ERROR" });
697
700
  const conn = snowflake.createConnection({
698
- account: params[parameters8.account.slug],
699
- username: params[parameters8.user.slug],
700
- role: params[parameters8.role.slug],
701
- warehouse: params[parameters8.warehouse.slug],
702
- password: params[parameters8.pat.slug]
701
+ account: params[parameters11.account.slug],
702
+ username: params[parameters11.user.slug],
703
+ role: params[parameters11.role.slug],
704
+ warehouse: params[parameters11.warehouse.slug],
705
+ password: params[parameters11.pat.slug]
703
706
  });
704
707
  await new Promise((resolve, reject) => {
705
708
  conn.connect((err) => {
@@ -734,11 +737,11 @@ var snowflakePatConnector = new ConnectorPlugin({
734
737
  const snowflake = (await import("snowflake-sdk")).default;
735
738
  snowflake.configure({ logLevel: "ERROR" });
736
739
  const conn = snowflake.createConnection({
737
- account: params[parameters8.account.slug],
738
- username: params[parameters8.user.slug],
739
- role: params[parameters8.role.slug],
740
- warehouse: params[parameters8.warehouse.slug],
741
- password: params[parameters8.pat.slug]
740
+ account: params[parameters11.account.slug],
741
+ username: params[parameters11.user.slug],
742
+ role: params[parameters11.role.slug],
743
+ warehouse: params[parameters11.warehouse.slug],
744
+ password: params[parameters11.pat.slug]
742
745
  });
743
746
  await new Promise((resolve, reject) => {
744
747
  conn.connect((err) => {
@@ -770,7 +773,7 @@ var snowflakePatConnector = new ConnectorPlugin({
770
773
  });
771
774
 
772
775
  // src/connectors/postgresql/parameters.ts
773
- var parameters9 = {
776
+ var parameters12 = {
774
777
  connectionUrl: new ParameterDefinition({
775
778
  slug: "connection-url",
776
779
  name: "PostgreSQL Connection URL",
@@ -827,7 +830,7 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
827
830
  let connectionUrl;
828
831
  try {
829
832
  const { Pool } = await import("pg");
830
- connectionUrl = parameters9.connectionUrl.getValue(connection);
833
+ connectionUrl = parameters12.connectionUrl.getValue(connection);
831
834
  const pool = new Pool({
832
835
  connectionString: connectionUrl,
833
836
  ssl: { rejectUnauthorized: false },
@@ -865,7 +868,7 @@ var postgresqlConnector = new ConnectorPlugin({
865
868
  name: "PostgreSQL",
866
869
  description: "Connect to PostgreSQL databases for relational data storage and querying.",
867
870
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/42AHi2uqteUn65MyqdN6V7/a0f68f12af6aac96bbcda5980f43de07/elephant.png",
868
- parameters: parameters9,
871
+ parameters: parameters12,
869
872
  releaseFlag: { dev1: true, dev2: true, prod: true },
870
873
  systemPrompt: `## PostgreSQL SQL Notes
871
874
  - Schema exploration:
@@ -876,7 +879,7 @@ var postgresqlConnector = new ConnectorPlugin({
876
879
  async checkConnection(params, _config) {
877
880
  const { Pool } = await import("pg");
878
881
  const pool = new Pool({
879
- connectionString: params[parameters9.connectionUrl.slug],
882
+ connectionString: params[parameters12.connectionUrl.slug],
880
883
  ssl: { rejectUnauthorized: false },
881
884
  connectionTimeoutMillis: 1e4
882
885
  });
@@ -893,7 +896,7 @@ var postgresqlConnector = new ConnectorPlugin({
893
896
  const { Pool } = await import("pg");
894
897
  const { text, values } = buildPositionalParams(sql, namedParams);
895
898
  const pool = new Pool({
896
- connectionString: params[parameters9.connectionUrl.slug],
899
+ connectionString: params[parameters12.connectionUrl.slug],
897
900
  ssl: { rejectUnauthorized: false },
898
901
  connectionTimeoutMillis: 1e4,
899
902
  statement_timeout: 6e4
@@ -908,7 +911,7 @@ var postgresqlConnector = new ConnectorPlugin({
908
911
  });
909
912
 
910
913
  // src/connectors/mysql/parameters.ts
911
- var parameters10 = {
914
+ var parameters13 = {
912
915
  connectionUrl: new ParameterDefinition({
913
916
  slug: "connection-url",
914
917
  name: "MySQL Connection URL",
@@ -965,7 +968,7 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
965
968
  let connectionUrl;
966
969
  try {
967
970
  const mysql = await import("mysql2/promise");
968
- connectionUrl = parameters10.connectionUrl.getValue(connection);
971
+ connectionUrl = parameters13.connectionUrl.getValue(connection);
969
972
  const pool = mysql.createPool({
970
973
  uri: connectionUrl,
971
974
  connectTimeout: CONNECT_TIMEOUT_MS2
@@ -1005,7 +1008,7 @@ var mysqlConnector = new ConnectorPlugin({
1005
1008
  name: "MySQL",
1006
1009
  description: "Connect to MySQL databases for relational data storage and querying.",
1007
1010
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6ghPFeGgl7uBs5NHH1a4L/512c9433beec5b595caa41f04921c1f9/logo-mysql-170x115.png",
1008
- parameters: parameters10,
1011
+ parameters: parameters13,
1009
1012
  releaseFlag: { dev1: true, dev2: true, prod: true },
1010
1013
  systemPrompt: `## MySQL SQL Notes
1011
1014
  - Schema exploration:
@@ -1016,7 +1019,7 @@ var mysqlConnector = new ConnectorPlugin({
1016
1019
  async checkConnection(params, _config) {
1017
1020
  const mysql = await import("mysql2/promise");
1018
1021
  const pool = mysql.createPool({
1019
- uri: params[parameters10.connectionUrl.slug],
1022
+ uri: params[parameters13.connectionUrl.slug],
1020
1023
  connectTimeout: 1e4
1021
1024
  });
1022
1025
  try {
@@ -1032,7 +1035,7 @@ var mysqlConnector = new ConnectorPlugin({
1032
1035
  const mysql = await import("mysql2/promise");
1033
1036
  const { text, values } = buildQuestionmarkParams(sql, namedParams);
1034
1037
  const pool = mysql.createPool({
1035
- uri: params[parameters10.connectionUrl.slug],
1038
+ uri: params[parameters13.connectionUrl.slug],
1036
1039
  connectTimeout: 1e4
1037
1040
  });
1038
1041
  try {
@@ -1053,7 +1056,7 @@ var mysqlConnector = new ConnectorPlugin({
1053
1056
  import { z as z5 } from "zod";
1054
1057
 
1055
1058
  // src/connectors/bigquery/parameters.ts
1056
- var parameters11 = {
1059
+ var parameters14 = {
1057
1060
  serviceAccountKeyJsonBase64: new ParameterDefinition({
1058
1061
  slug: "service-account-key-json-base64",
1059
1062
  name: "Google Cloud Service Account JSON",
@@ -1110,7 +1113,7 @@ var listProjectsTool = new ConnectorTool({
1110
1113
  };
1111
1114
  }
1112
1115
  try {
1113
- const serviceAccountJsonBase64 = parameters11.serviceAccountKeyJsonBase64.getValue(connection);
1116
+ const serviceAccountJsonBase64 = parameters14.serviceAccountKeyJsonBase64.getValue(connection);
1114
1117
  const credentials = JSON.parse(
1115
1118
  Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
1116
1119
  );
@@ -1174,8 +1177,8 @@ var listDatasetsTool = new ConnectorTool({
1174
1177
  }
1175
1178
  try {
1176
1179
  const { BigQuery } = await import("@google-cloud/bigquery");
1177
- const projectId = parameters11.projectId.getValue(connection);
1178
- const serviceAccountJsonBase64 = parameters11.serviceAccountKeyJsonBase64.getValue(connection);
1180
+ const projectId = parameters14.projectId.getValue(connection);
1181
+ const serviceAccountJsonBase64 = parameters14.serviceAccountKeyJsonBase64.getValue(connection);
1179
1182
  const credentials = JSON.parse(
1180
1183
  Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
1181
1184
  );
@@ -1336,8 +1339,8 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
1336
1339
  );
1337
1340
  try {
1338
1341
  const { BigQuery } = await import("@google-cloud/bigquery");
1339
- const projectId = parameters11.projectId.getValue(connection);
1340
- const serviceAccountJsonBase64 = parameters11.serviceAccountKeyJsonBase64.getValue(connection);
1342
+ const projectId = parameters14.projectId.getValue(connection);
1343
+ const serviceAccountJsonBase64 = parameters14.serviceAccountKeyJsonBase64.getValue(connection);
1341
1344
  const credentials = JSON.parse(
1342
1345
  Buffer.from(serviceAccountJsonBase64, "base64").toString("utf-8")
1343
1346
  );
@@ -1367,7 +1370,7 @@ var bigqueryConnector = new ConnectorPlugin({
1367
1370
  name: "BigQuery",
1368
1371
  description: "Connect to Google BigQuery for data warehouse and analytics.",
1369
1372
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6nlehQyOmdbktG5hOYkYMr/6ca559140d5ddc7dadc5eac88858a563/bigquery.svg",
1370
- parameters: parameters11,
1373
+ parameters: parameters14,
1371
1374
  releaseFlag: { dev1: true, dev2: true, prod: true },
1372
1375
  setup: bigquerySetup,
1373
1376
  systemPrompt: `## BigQuery SQL Notes
@@ -1381,10 +1384,10 @@ var bigqueryConnector = new ConnectorPlugin({
1381
1384
  async checkConnection(params, _config) {
1382
1385
  const { BigQuery } = await import("@google-cloud/bigquery");
1383
1386
  const credentials = JSON.parse(
1384
- Buffer.from(params[parameters11.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
1387
+ Buffer.from(params[parameters14.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
1385
1388
  );
1386
1389
  const bq = new BigQuery({
1387
- projectId: params[parameters11.projectId.slug],
1390
+ projectId: params[parameters14.projectId.slug],
1388
1391
  credentials
1389
1392
  });
1390
1393
  try {
@@ -1399,10 +1402,10 @@ var bigqueryConnector = new ConnectorPlugin({
1399
1402
  const { BigQuery } = await import("@google-cloud/bigquery");
1400
1403
  const resolvedSql = replaceLiteralParams(sql, namedParams);
1401
1404
  const credentials = JSON.parse(
1402
- Buffer.from(params[parameters11.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
1405
+ Buffer.from(params[parameters14.serviceAccountKeyJsonBase64.slug], "base64").toString("utf-8")
1403
1406
  );
1404
1407
  const bq = new BigQuery({
1405
- projectId: params[parameters11.projectId.slug],
1408
+ projectId: params[parameters14.projectId.slug],
1406
1409
  credentials
1407
1410
  });
1408
1411
  const [job] = await bq.createQueryJob({ query: resolvedSql });
@@ -1522,7 +1525,7 @@ var listProjectsTool2 = new ConnectorTool({
1522
1525
  import { z as z9 } from "zod";
1523
1526
 
1524
1527
  // src/connectors/bigquery-oauth/parameters.ts
1525
- var parameters12 = {
1528
+ var parameters15 = {
1526
1529
  projectId: new ParameterDefinition({
1527
1530
  slug: "project-id",
1528
1531
  name: "Google Cloud Project ID",
@@ -1600,7 +1603,7 @@ var listDatasetsTool2 = new ConnectorTool({
1600
1603
  error: `Connection ${connectionId} not found`
1601
1604
  };
1602
1605
  }
1603
- const gcpProjectId = parameters12.projectId.getValue(connection);
1606
+ const gcpProjectId = parameters15.projectId.getValue(connection);
1604
1607
  console.log(
1605
1608
  `[connector-query] bigquery-oauth/${connection.name}: listDatasets`
1606
1609
  );
@@ -1823,7 +1826,7 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
1823
1826
  error: `Connection ${connectionId} not found`
1824
1827
  };
1825
1828
  }
1826
- const gcpProjectId = parameters12.projectId.getValue(connection);
1829
+ const gcpProjectId = parameters15.projectId.getValue(connection);
1827
1830
  console.log(
1828
1831
  `[connector-query] bigquery-oauth/${connection.name}: ${sql}`
1829
1832
  );
@@ -1890,7 +1893,7 @@ var bigqueryOauthConnector = new ConnectorPlugin({
1890
1893
  name: "BigQuery (OAuth)",
1891
1894
  description: "Connect to Google BigQuery for data warehouse and analytics using OAuth.",
1892
1895
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6nlehQyOmdbktG5hOYkYMr/6ca559140d5ddc7dadc5eac88858a563/bigquery.svg",
1893
- parameters: parameters12,
1896
+ parameters: parameters15,
1894
1897
  releaseFlag: { dev1: true, dev2: true, prod: true },
1895
1898
  setup: bigquerySetup2,
1896
1899
  proxyPolicy: {
@@ -1909,7 +1912,7 @@ var bigqueryOauthConnector = new ConnectorPlugin({
1909
1912
  tools: tools6,
1910
1913
  async checkConnection(params, config) {
1911
1914
  const { proxyFetch } = config;
1912
- const projectId = params[parameters12.projectId.slug];
1915
+ const projectId = params[parameters15.projectId.slug];
1913
1916
  const url = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries`;
1914
1917
  try {
1915
1918
  const res = await proxyFetch(url, {
@@ -1928,7 +1931,7 @@ var bigqueryOauthConnector = new ConnectorPlugin({
1928
1931
  },
1929
1932
  async query(params, sql, namedParams, context) {
1930
1933
  const { proxyFetch } = context;
1931
- const projectId = params[parameters12.projectId.slug];
1934
+ const projectId = params[parameters15.projectId.slug];
1932
1935
  const resolvedSql = replaceLiteralParams(sql, namedParams);
1933
1936
  const url = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries`;
1934
1937
  const res = await proxyFetch(url, {
@@ -1946,7 +1949,7 @@ var bigqueryOauthConnector = new ConnectorPlugin({
1946
1949
  });
1947
1950
 
1948
1951
  // src/connectors/aws-athena/parameters.ts
1949
- var parameters13 = {
1952
+ var parameters16 = {
1950
1953
  awsAccessKeyId: new ParameterDefinition({
1951
1954
  slug: "aws-access-key-id",
1952
1955
  name: "AWS Access Key ID",
@@ -2043,8 +2046,8 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
2043
2046
  GetQueryExecutionCommand,
2044
2047
  GetQueryResultsCommand
2045
2048
  } = await import("@aws-sdk/client-athena");
2046
- const workgroup = parameters13.workgroup.tryGetValue(connection);
2047
- const outputLocation = parameters13.outputLocation.tryGetValue(connection);
2049
+ const workgroup = parameters16.workgroup.tryGetValue(connection);
2050
+ const outputLocation = parameters16.outputLocation.tryGetValue(connection);
2048
2051
  if (!workgroup && !outputLocation) {
2049
2052
  return {
2050
2053
  success: false,
@@ -2052,10 +2055,10 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
2052
2055
  };
2053
2056
  }
2054
2057
  const client = new AthenaClient({
2055
- region: parameters13.awsRegion.getValue(connection),
2058
+ region: parameters16.awsRegion.getValue(connection),
2056
2059
  credentials: {
2057
- accessKeyId: parameters13.awsAccessKeyId.getValue(connection),
2058
- secretAccessKey: parameters13.awsSecretAccessKey.getValue(connection)
2060
+ accessKeyId: parameters16.awsAccessKeyId.getValue(connection),
2061
+ secretAccessKey: parameters16.awsSecretAccessKey.getValue(connection)
2059
2062
  }
2060
2063
  });
2061
2064
  const startParams = { QueryString: sql };
@@ -2119,7 +2122,7 @@ var awsAthenaConnector = new ConnectorPlugin({
2119
2122
  name: "AWS Athena",
2120
2123
  description: "Connect to AWS Athena for serverless SQL queries on S3 data.",
2121
2124
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5x0vIHtUHfJJMZUv4RFOYZ/5059bac389f0169542f39cdb4b387d2c/Athena.svg",
2122
- parameters: parameters13,
2125
+ parameters: parameters16,
2123
2126
  releaseFlag: { dev1: true, dev2: true, prod: true },
2124
2127
  systemPrompt: `## AWS Athena SQL Notes
2125
2128
  - Uses Presto/Trino based SQL syntax
@@ -2137,8 +2140,8 @@ var awsAthenaConnector = new ConnectorPlugin({
2137
2140
  StartQueryExecutionCommand,
2138
2141
  GetQueryExecutionCommand
2139
2142
  } = await import("@aws-sdk/client-athena");
2140
- const workgroup = params[parameters13.workgroup.slug];
2141
- const outputLocation = params[parameters13.outputLocation.slug];
2143
+ const workgroup = params[parameters16.workgroup.slug];
2144
+ const outputLocation = params[parameters16.outputLocation.slug];
2142
2145
  if (!workgroup && !outputLocation) {
2143
2146
  return {
2144
2147
  success: false,
@@ -2146,10 +2149,10 @@ var awsAthenaConnector = new ConnectorPlugin({
2146
2149
  };
2147
2150
  }
2148
2151
  const client = new AthenaClient({
2149
- region: params[parameters13.awsRegion.slug],
2152
+ region: params[parameters16.awsRegion.slug],
2150
2153
  credentials: {
2151
- accessKeyId: params[parameters13.awsAccessKeyId.slug],
2152
- secretAccessKey: params[parameters13.awsSecretAccessKey.slug]
2154
+ accessKeyId: params[parameters16.awsAccessKeyId.slug],
2155
+ secretAccessKey: params[parameters16.awsSecretAccessKey.slug]
2153
2156
  }
2154
2157
  });
2155
2158
  const startParams = { QueryString: "SELECT 1" };
@@ -2194,16 +2197,16 @@ var awsAthenaConnector = new ConnectorPlugin({
2194
2197
  GetQueryResultsCommand
2195
2198
  } = await import("@aws-sdk/client-athena");
2196
2199
  const resolvedSql = replaceLiteralParams(sql, namedParams);
2197
- const workgroup = params[parameters13.workgroup.slug];
2198
- const outputLocation = params[parameters13.outputLocation.slug];
2200
+ const workgroup = params[parameters16.workgroup.slug];
2201
+ const outputLocation = params[parameters16.outputLocation.slug];
2199
2202
  if (!workgroup && !outputLocation) {
2200
2203
  throw new Error("Either workgroup or output-location is required");
2201
2204
  }
2202
2205
  const client = new AthenaClient({
2203
- region: params[parameters13.awsRegion.slug],
2206
+ region: params[parameters16.awsRegion.slug],
2204
2207
  credentials: {
2205
- accessKeyId: params[parameters13.awsAccessKeyId.slug],
2206
- secretAccessKey: params[parameters13.awsSecretAccessKey.slug]
2208
+ accessKeyId: params[parameters16.awsAccessKeyId.slug],
2209
+ secretAccessKey: params[parameters16.awsSecretAccessKey.slug]
2207
2210
  }
2208
2211
  });
2209
2212
  const startParams = { QueryString: resolvedSql };
@@ -2248,7 +2251,7 @@ var awsAthenaConnector = new ConnectorPlugin({
2248
2251
  });
2249
2252
 
2250
2253
  // src/connectors/redshift/parameters.ts
2251
- var parameters14 = {
2254
+ var parameters17 = {
2252
2255
  awsAccessKeyId: new ParameterDefinition({
2253
2256
  slug: "aws-access-key-id",
2254
2257
  name: "AWS Access Key ID",
@@ -2374,14 +2377,14 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
2374
2377
  DescribeStatementCommand,
2375
2378
  GetStatementResultCommand
2376
2379
  } = await import("@aws-sdk/client-redshift-data");
2377
- const awsAccessKeyId = parameters14.awsAccessKeyId.getValue(connection);
2378
- const awsSecretAccessKey = parameters14.awsSecretAccessKey.getValue(connection);
2379
- const awsRegion = parameters14.awsRegion.getValue(connection);
2380
- const database = parameters14.database.getValue(connection);
2381
- const clusterIdentifier = parameters14.clusterIdentifier.tryGetValue(connection);
2382
- const workgroupName = parameters14.workgroupName.tryGetValue(connection);
2383
- const secretArn = parameters14.secretArn.tryGetValue(connection);
2384
- const dbUser = parameters14.dbUser.tryGetValue(connection);
2380
+ const awsAccessKeyId = parameters17.awsAccessKeyId.getValue(connection);
2381
+ const awsSecretAccessKey = parameters17.awsSecretAccessKey.getValue(connection);
2382
+ const awsRegion = parameters17.awsRegion.getValue(connection);
2383
+ const database = parameters17.database.getValue(connection);
2384
+ const clusterIdentifier = parameters17.clusterIdentifier.tryGetValue(connection);
2385
+ const workgroupName = parameters17.workgroupName.tryGetValue(connection);
2386
+ const secretArn = parameters17.secretArn.tryGetValue(connection);
2387
+ const dbUser = parameters17.dbUser.tryGetValue(connection);
2385
2388
  if (!clusterIdentifier && !workgroupName) {
2386
2389
  return {
2387
2390
  success: false,
@@ -2452,7 +2455,7 @@ var redshiftConnector = new ConnectorPlugin({
2452
2455
  name: "Redshift",
2453
2456
  description: "Connect to Amazon Redshift for data warehouse analytics.",
2454
2457
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/AEwW2psmrnZ7htTVsgA9t/a637e31707c5d760be73ce1d8ec75580/aws-redshift-logo.svg",
2455
- parameters: parameters14,
2458
+ parameters: parameters17,
2456
2459
  releaseFlag: { dev1: true, dev2: true, prod: true },
2457
2460
  systemPrompt: `## Redshift SQL Notes
2458
2461
  - Uses PostgreSQL based SQL syntax
@@ -2470,10 +2473,10 @@ var redshiftConnector = new ConnectorPlugin({
2470
2473
  ExecuteStatementCommand,
2471
2474
  DescribeStatementCommand
2472
2475
  } = await import("@aws-sdk/client-redshift-data");
2473
- const clusterIdentifier = params[parameters14.clusterIdentifier.slug];
2474
- const workgroupName = params[parameters14.workgroupName.slug];
2475
- const secretArn = params[parameters14.secretArn.slug];
2476
- const dbUser = params[parameters14.dbUser.slug];
2476
+ const clusterIdentifier = params[parameters17.clusterIdentifier.slug];
2477
+ const workgroupName = params[parameters17.workgroupName.slug];
2478
+ const secretArn = params[parameters17.secretArn.slug];
2479
+ const dbUser = params[parameters17.dbUser.slug];
2477
2480
  if (!clusterIdentifier && !workgroupName) {
2478
2481
  return {
2479
2482
  success: false,
@@ -2481,15 +2484,15 @@ var redshiftConnector = new ConnectorPlugin({
2481
2484
  };
2482
2485
  }
2483
2486
  const client = new RedshiftDataClient({
2484
- region: params[parameters14.awsRegion.slug],
2487
+ region: params[parameters17.awsRegion.slug],
2485
2488
  credentials: {
2486
- accessKeyId: params[parameters14.awsAccessKeyId.slug],
2487
- secretAccessKey: params[parameters14.awsSecretAccessKey.slug]
2489
+ accessKeyId: params[parameters17.awsAccessKeyId.slug],
2490
+ secretAccessKey: params[parameters17.awsSecretAccessKey.slug]
2488
2491
  }
2489
2492
  });
2490
2493
  const { Id: statementId } = await client.send(
2491
2494
  new ExecuteStatementCommand({
2492
- Database: params[parameters14.database.slug],
2495
+ Database: params[parameters17.database.slug],
2493
2496
  Sql: "SELECT 1",
2494
2497
  ...clusterIdentifier && { ClusterIdentifier: clusterIdentifier },
2495
2498
  ...workgroupName && { WorkgroupName: workgroupName },
@@ -2530,23 +2533,23 @@ var redshiftConnector = new ConnectorPlugin({
2530
2533
  GetStatementResultCommand
2531
2534
  } = await import("@aws-sdk/client-redshift-data");
2532
2535
  const resolvedSql = replaceLiteralParams(sql, namedParams);
2533
- const clusterIdentifier = params[parameters14.clusterIdentifier.slug];
2534
- const workgroupName = params[parameters14.workgroupName.slug];
2535
- const secretArn = params[parameters14.secretArn.slug];
2536
- const dbUser = params[parameters14.dbUser.slug];
2536
+ const clusterIdentifier = params[parameters17.clusterIdentifier.slug];
2537
+ const workgroupName = params[parameters17.workgroupName.slug];
2538
+ const secretArn = params[parameters17.secretArn.slug];
2539
+ const dbUser = params[parameters17.dbUser.slug];
2537
2540
  if (!clusterIdentifier && !workgroupName) {
2538
2541
  throw new Error("Either cluster-identifier or workgroup-name is required");
2539
2542
  }
2540
2543
  const client = new RedshiftDataClient({
2541
- region: params[parameters14.awsRegion.slug],
2544
+ region: params[parameters17.awsRegion.slug],
2542
2545
  credentials: {
2543
- accessKeyId: params[parameters14.awsAccessKeyId.slug],
2544
- secretAccessKey: params[parameters14.awsSecretAccessKey.slug]
2546
+ accessKeyId: params[parameters17.awsAccessKeyId.slug],
2547
+ secretAccessKey: params[parameters17.awsSecretAccessKey.slug]
2545
2548
  }
2546
2549
  });
2547
2550
  const { Id: statementId } = await client.send(
2548
2551
  new ExecuteStatementCommand({
2549
- Database: params[parameters14.database.slug],
2552
+ Database: params[parameters17.database.slug],
2550
2553
  Sql: resolvedSql,
2551
2554
  ...clusterIdentifier && { ClusterIdentifier: clusterIdentifier },
2552
2555
  ...workgroupName && { WorkgroupName: workgroupName },
@@ -2584,7 +2587,7 @@ var redshiftConnector = new ConnectorPlugin({
2584
2587
  });
2585
2588
 
2586
2589
  // src/connectors/databricks/parameters.ts
2587
- var parameters15 = {
2590
+ var parameters18 = {
2588
2591
  host: new ParameterDefinition({
2589
2592
  slug: "host",
2590
2593
  name: "Databricks Workspace Host",
@@ -2656,9 +2659,9 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
2656
2659
  );
2657
2660
  try {
2658
2661
  const { DBSQLClient } = await import("@databricks/sql");
2659
- const host = parameters15.host.getValue(connection);
2660
- const token = parameters15.token.getValue(connection);
2661
- const httpPath = parameters15.httpPath.getValue(connection);
2662
+ const host = parameters18.host.getValue(connection);
2663
+ const token = parameters18.token.getValue(connection);
2664
+ const httpPath = parameters18.httpPath.getValue(connection);
2662
2665
  const client = new DBSQLClient();
2663
2666
  await client.connect({ host, path: httpPath, token });
2664
2667
  let session;
@@ -2699,7 +2702,7 @@ var databricksConnector = new ConnectorPlugin({
2699
2702
  name: "Databricks",
2700
2703
  description: "Connect to Databricks for data lakehouse and SQL analytics.",
2701
2704
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6QgcrfpQOKg18P7DdgKerd/af55bf0d871339049824dd167b97a29f/databricks-icon.svg",
2702
- parameters: parameters15,
2705
+ parameters: parameters18,
2703
2706
  releaseFlag: { dev1: true, dev2: true, prod: true },
2704
2707
  systemPrompt: `## Databricks SQL Notes
2705
2708
  - Uses Spark SQL / Databricks SQL syntax
@@ -2714,9 +2717,9 @@ var databricksConnector = new ConnectorPlugin({
2714
2717
  const { DBSQLClient } = await import("@databricks/sql");
2715
2718
  const client = new DBSQLClient();
2716
2719
  await client.connect({
2717
- host: params[parameters15.host.slug],
2718
- path: params[parameters15.httpPath.slug],
2719
- token: params[parameters15.token.slug]
2720
+ host: params[parameters18.host.slug],
2721
+ path: params[parameters18.httpPath.slug],
2722
+ token: params[parameters18.token.slug]
2720
2723
  });
2721
2724
  let session;
2722
2725
  let operation;
@@ -2743,9 +2746,9 @@ var databricksConnector = new ConnectorPlugin({
2743
2746
  const resolvedSql = replaceLiteralParams(sql, namedParams);
2744
2747
  const client = new DBSQLClient();
2745
2748
  await client.connect({
2746
- host: params[parameters15.host.slug],
2747
- path: params[parameters15.httpPath.slug],
2748
- token: params[parameters15.token.slug]
2749
+ host: params[parameters18.host.slug],
2750
+ path: params[parameters18.httpPath.slug],
2751
+ token: params[parameters18.token.slug]
2749
2752
  });
2750
2753
  let session;
2751
2754
  let operation;
@@ -3499,7 +3502,7 @@ const metrics = await dbt.getMetrics({ limit: 100 });
3499
3502
  });
3500
3503
 
3501
3504
  // src/connectors/squadbase-db/parameters.ts
3502
- var parameters16 = {
3505
+ var parameters19 = {
3503
3506
  connectionUrl: new ParameterDefinition({
3504
3507
  slug: "connection-url",
3505
3508
  name: "Connection URL",
@@ -3556,7 +3559,7 @@ Avoid loading large amounts of data; always include LIMIT in queries.`,
3556
3559
  let connectionUrl;
3557
3560
  try {
3558
3561
  const { Pool } = await import("pg");
3559
- connectionUrl = parameters16.connectionUrl.getValue(connection);
3562
+ connectionUrl = parameters19.connectionUrl.getValue(connection);
3560
3563
  const pool = new Pool({
3561
3564
  connectionString: connectionUrl,
3562
3565
  ssl: { rejectUnauthorized: false },
@@ -3594,7 +3597,7 @@ var squadbaseDbConnector = new ConnectorPlugin({
3594
3597
  name: "Squadbase DB",
3595
3598
  description: "Connect to Squadbase DB (PostgreSQL).",
3596
3599
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/25y0XqMxIufeD3egWH3bEl/659b4ade405890654cfaf91c03a4b458/icon.svg",
3597
- parameters: parameters16,
3600
+ parameters: parameters19,
3598
3601
  releaseFlag: { dev1: true, dev2: true, prod: true },
3599
3602
  systemPrompt: `## Squadbase DB SQL Notes
3600
3603
  - Uses PostgreSQL based SQL syntax
@@ -3606,7 +3609,7 @@ var squadbaseDbConnector = new ConnectorPlugin({
3606
3609
  async checkConnection(params, _config) {
3607
3610
  const { Pool } = await import("pg");
3608
3611
  const pool = new Pool({
3609
- connectionString: params[parameters16.connectionUrl.slug],
3612
+ connectionString: params[parameters19.connectionUrl.slug],
3610
3613
  ssl: { rejectUnauthorized: false },
3611
3614
  connectionTimeoutMillis: 1e4
3612
3615
  });
@@ -3623,7 +3626,7 @@ var squadbaseDbConnector = new ConnectorPlugin({
3623
3626
  const { Pool } = await import("pg");
3624
3627
  const { text, values } = buildPositionalParams(sql, namedParams);
3625
3628
  const pool = new Pool({
3626
- connectionString: params[parameters16.connectionUrl.slug],
3629
+ connectionString: params[parameters19.connectionUrl.slug],
3627
3630
  ssl: { rejectUnauthorized: false },
3628
3631
  connectionTimeoutMillis: 1e4,
3629
3632
  statement_timeout: 6e4
@@ -3665,6 +3668,1242 @@ const response = await client.chat.completions.create({
3665
3668
  tools: tools16
3666
3669
  });
3667
3670
 
3671
+ // src/connectors/google-sheets-oauth/tools/request.ts
3672
+ import { z as z20 } from "zod";
3673
+ var BASE_URL4 = "https://sheets.googleapis.com/v4/spreadsheets";
3674
+ var REQUEST_TIMEOUT_MS9 = 6e4;
3675
+ var cachedToken4 = null;
3676
+ async function getProxyToken4(config) {
3677
+ if (cachedToken4 && cachedToken4.expiresAt > Date.now() + 6e4) {
3678
+ return cachedToken4.token;
3679
+ }
3680
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
3681
+ const res = await fetch(url, {
3682
+ method: "POST",
3683
+ headers: {
3684
+ "Content-Type": "application/json",
3685
+ "x-api-key": config.appApiKey,
3686
+ "project-id": config.projectId
3687
+ },
3688
+ body: JSON.stringify({
3689
+ sandboxId: config.sandboxId,
3690
+ issuedBy: "coding-agent"
3691
+ })
3692
+ });
3693
+ if (!res.ok) {
3694
+ const errorText = await res.text().catch(() => res.statusText);
3695
+ throw new Error(
3696
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
3697
+ );
3698
+ }
3699
+ const data = await res.json();
3700
+ cachedToken4 = {
3701
+ token: data.token,
3702
+ expiresAt: new Date(data.expiresAt).getTime()
3703
+ };
3704
+ return data.token;
3705
+ }
3706
+ var inputSchema20 = z20.object({
3707
+ toolUseIntent: z20.string().optional().describe(
3708
+ "Brief description of what you intend to accomplish with this tool call"
3709
+ ),
3710
+ connectionId: z20.string().describe("ID of the Google Sheets OAuth connection to use"),
3711
+ method: z20.enum(["GET"]).describe("HTTP method (read-only, GET only)"),
3712
+ path: z20.string().describe(
3713
+ "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
+ ),
3715
+ queryParams: z20.record(z20.string(), z20.string()).optional().describe("Query parameters to append to the URL")
3716
+ });
3717
+ var outputSchema20 = z20.discriminatedUnion("success", [
3718
+ z20.object({
3719
+ success: z20.literal(true),
3720
+ status: z20.number(),
3721
+ data: z20.record(z20.string(), z20.unknown())
3722
+ }),
3723
+ z20.object({
3724
+ success: z20.literal(false),
3725
+ error: z20.string()
3726
+ })
3727
+ ]);
3728
+ var requestTool6 = new ConnectorTool({
3729
+ name: "request",
3730
+ description: `Send authenticated GET requests to the Google Sheets API v4.
3731
+ Authentication is handled automatically via OAuth proxy.
3732
+ {spreadsheetId} in the path is automatically replaced with the connection's default spreadsheet ID if configured.`,
3733
+ inputSchema: inputSchema20,
3734
+ outputSchema: outputSchema20,
3735
+ async execute({ connectionId, method, path, queryParams }, connections, config) {
3736
+ const connection = connections.find((c) => c.id === connectionId);
3737
+ if (!connection) {
3738
+ return {
3739
+ success: false,
3740
+ error: `Connection ${connectionId} not found`
3741
+ };
3742
+ }
3743
+ console.log(
3744
+ `[connector-request] google-sheets-oauth/${connection.name}: ${method} ${path}`
3745
+ );
3746
+ try {
3747
+ const spreadsheetId = parameters7.spreadsheetId.tryGetValue(connection);
3748
+ const resolvedPath = spreadsheetId ? path.replace(/\{spreadsheetId\}/g, spreadsheetId) : path;
3749
+ let url = `${BASE_URL4}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
3750
+ if (queryParams) {
3751
+ const searchParams = new URLSearchParams(queryParams);
3752
+ url += `?${searchParams.toString()}`;
3753
+ }
3754
+ const token = await getProxyToken4(config.oauthProxy);
3755
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
3756
+ const controller = new AbortController();
3757
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS9);
3758
+ try {
3759
+ const response = await fetch(proxyUrl, {
3760
+ method: "POST",
3761
+ headers: {
3762
+ "Content-Type": "application/json",
3763
+ Authorization: `Bearer ${token}`
3764
+ },
3765
+ body: JSON.stringify({
3766
+ url,
3767
+ method
3768
+ }),
3769
+ signal: controller.signal
3770
+ });
3771
+ const data = await response.json();
3772
+ if (!response.ok) {
3773
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
3774
+ return { success: false, error: errorMessage };
3775
+ }
3776
+ return { success: true, status: response.status, data };
3777
+ } finally {
3778
+ clearTimeout(timeout);
3779
+ }
3780
+ } catch (err) {
3781
+ const msg = err instanceof Error ? err.message : String(err);
3782
+ return { success: false, error: msg };
3783
+ }
3784
+ }
3785
+ });
3786
+
3787
+ // src/connectors/google-sheets-oauth/setup.ts
3788
+ var requestToolName = `google-sheets-oauth_${requestTool6.name}`;
3789
+ var googleSheetsSetup = new ConnectorSetup({
3790
+ ja: `## Google Sheets \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
3791
+
3792
+ \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Sheets\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
3793
+
3794
+ ### \u624B\u9806
3795
+
3796
+ #### \u30B9\u30C6\u30C3\u30D71: \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u9078\u629E
3797
+ 1. \u30E6\u30FC\u30B6\u30FC\u306B\u300C\u4F7F\u7528\u3059\u308BGoogle Sheets\u306EURL\u3092\u8CBC\u308A\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: https://docs.google.com/spreadsheets/d/xxxxx/edit\uFF09\u300D\u3068\u4F1D\u3048\u308B
3798
+ 2. \u30E6\u30FC\u30B6\u30FC\u304CURL\u307E\u305F\u306F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u3092\u63D0\u4F9B\u3057\u305F\u3089\u3001URL\u304B\u3089ID\u3092\u62BD\u51FA\u3059\u308B\uFF08\`/d/\` \u3068 \`/edit\` \u306E\u9593\u306E\u6587\u5B57\u5217\uFF09
3799
+ 3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
3800
+ - \`parameterSlug\`: \`"spreadsheet-id"\`
3801
+ - \`value\`: \u62BD\u51FA\u3057\u305F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID
3802
+
3803
+ #### \u30B9\u30C6\u30C3\u30D72: \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u691C\u8A3C
3804
+ 1. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3059\u308B:
3805
+ - \`method\`: \`"GET"\`
3806
+ - \`path\`: \`"/{spreadsheetId}?fields=spreadsheetId,properties.title,sheets.properties.title"\`
3807
+ 2. \u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u3001\u30E6\u30FC\u30B6\u30FC\u306B\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u5171\u6709\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3059\u308B\u3088\u3046\u4F1D\u3048\u308B
3808
+
3809
+ #### \u30B9\u30C6\u30C3\u30D73: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
3810
+ 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
3811
+ - \`spreadsheet\`: \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30BF\u30A4\u30C8\u30EB
3812
+ - \`sheets\`: \u30B7\u30FC\u30C8\u540D\u4E00\u89A7\uFF08\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09
3813
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
3814
+
3815
+ ### \u91CD\u8981\u306A\u5236\u7D04
3816
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30B7\u30FC\u30C8\u306E\u30C7\u30FC\u30BF\u3092\u8AAD\u307F\u53D6\u3089\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\u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u307F
3817
+
3818
+ ### \u5B9F\u884C\u65B9\u91DD
3819
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
3820
+ - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
3821
+ en: `## Google Sheets Setup Instructions
3822
+
3823
+ Follow these steps to set up the Google Sheets connection.
3824
+
3825
+ ### Steps
3826
+
3827
+ #### Step 1: Spreadsheet Selection
3828
+ 1. Ask the user to paste the Google Sheets URL (e.g., https://docs.google.com/spreadsheets/d/xxxxx/edit)
3829
+ 2. When the user provides a URL or spreadsheet ID, extract the ID from the URL (the string between \`/d/\` and \`/edit\`)
3830
+ 3. Call \`updateConnectionParameters\`:
3831
+ - \`parameterSlug\`: \`"spreadsheet-id"\`
3832
+ - \`value\`: The extracted spreadsheet ID
3833
+
3834
+ #### Step 2: Spreadsheet Validation
3835
+ 1. Call \`${requestToolName}\` to fetch spreadsheet metadata:
3836
+ - \`method\`: \`"GET"\`
3837
+ - \`path\`: \`"/{spreadsheetId}?fields=spreadsheetId,properties.title,sheets.properties.title"\`
3838
+ 2. If an error is returned, ask the user to check the spreadsheet sharing settings
3839
+
3840
+ #### Step 3: Complete Setup
3841
+ 1. Call \`updateConnectionContext\`:
3842
+ - \`spreadsheet\`: The spreadsheet title
3843
+ - \`sheets\`: Sheet names (comma-separated)
3844
+ - \`note\`: Brief description of the setup
3845
+
3846
+ ### Important Constraints
3847
+ - **Do NOT read sheet data during setup**. Only the metadata request specified in the steps above is allowed
3848
+
3849
+ ### Execution Policy
3850
+ - Write only 1 sentence between tool calls, then immediately call the next tool
3851
+ - Skip unnecessary explanations and proceed efficiently`
3852
+ });
3853
+
3854
+ // src/connectors/google-sheets-oauth/index.ts
3855
+ var tools17 = { request: requestTool6 };
3856
+ var googleSheetsOauthConnector = new ConnectorPlugin({
3857
+ slug: "google-sheets",
3858
+ authType: AUTH_TYPES.OAUTH,
3859
+ name: "Google Sheets (OAuth)",
3860
+ description: "Connect to Google Sheets for spreadsheet data access using OAuth.",
3861
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/5lJMKlzv5lYbsBQbXGqacn/f56205b34e55c37d1dc69b34dcbbc379/google-sheets.svg",
3862
+ parameters: parameters7,
3863
+ releaseFlag: { dev1: true, dev2: true, prod: false },
3864
+ setup: googleSheetsSetup,
3865
+ proxyPolicy: {
3866
+ allowlist: [
3867
+ {
3868
+ host: "sheets.googleapis.com",
3869
+ methods: ["GET"]
3870
+ }
3871
+ ]
3872
+ },
3873
+ systemPrompt: `## Google Sheets API (OAuth, Read-Only)
3874
+
3875
+ ### Available Endpoints
3876
+ - GET \`/{spreadsheetId}\` \u2014 Get spreadsheet metadata (title, sheets, properties)
3877
+ - GET \`/{spreadsheetId}/values/{range}\` \u2014 Get cell values for a range
3878
+ - GET \`/{spreadsheetId}/values:batchGet?ranges={range1}&ranges={range2}\` \u2014 Get values for multiple ranges
3879
+
3880
+ ### Range Notation (A1 notation)
3881
+ - \`Sheet1!A1:D10\` \u2014 Specific range on Sheet1
3882
+ - \`Sheet1!A:A\` \u2014 Entire column A on Sheet1
3883
+ - \`Sheet1!1:3\` \u2014 Rows 1 to 3 on Sheet1
3884
+ - \`Sheet1\` \u2014 All data on Sheet1
3885
+ - \`A1:D10\` \u2014 Range on the first sheet (when only one sheet exists)
3886
+
3887
+ ### Tips
3888
+ - Use \`{spreadsheetId}\` placeholder in paths \u2014 it is automatically replaced with the configured default spreadsheet ID
3889
+ - To explore a spreadsheet, first get metadata to see available sheet names
3890
+ - Use \`valueRenderOption=FORMATTED_VALUE\` query param to get display values
3891
+ - Use \`valueRenderOption=UNFORMATTED_VALUE\` for raw numeric values
3892
+ - Use \`majorDimension=COLUMNS\` to get data organized by columns instead of rows
3893
+
3894
+ ## Google Sheets SDK (TypeScript handler)
3895
+
3896
+ \`\`\`ts
3897
+ import { connection } from "@squadbase/vite-server/connectors/google-sheets-oauth";
3898
+
3899
+ const sheets = connection("<connectionId>");
3900
+
3901
+ // Get spreadsheet metadata
3902
+ const metadata = await sheets.getSpreadsheet();
3903
+ console.log(metadata.properties.title, metadata.sheets.map(s => s.properties.title));
3904
+
3905
+ // Get cell values
3906
+ const values = await sheets.getValues("Sheet1!A1:D10");
3907
+ console.log(values.values); // 2D array
3908
+
3909
+ // Get multiple ranges at once
3910
+ const batch = await sheets.batchGetValues(["Sheet1!A1:B5", "Sheet2!A1:C3"]);
3911
+ batch.valueRanges.forEach(vr => console.log(vr.range, vr.values));
3912
+ \`\`\``,
3913
+ tools: tools17,
3914
+ async checkConnection(params, config) {
3915
+ const { proxyFetch } = config;
3916
+ const spreadsheetId = params[parameters7.spreadsheetId.slug];
3917
+ 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
+ };
4008
+ }
4009
+ console.log(
4010
+ `[connector-request] google-analytics-oauth/${connection.name}: listAccounts`
4011
+ );
4012
+ try {
4013
+ const token = await getProxyToken5(config.oauthProxy);
4014
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4015
+ const controller = new AbortController();
4016
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS10);
4017
+ try {
4018
+ const response = await fetch(proxyUrl, {
4019
+ method: "POST",
4020
+ headers: {
4021
+ "Content-Type": "application/json",
4022
+ Authorization: `Bearer ${token}`
4023
+ },
4024
+ body: JSON.stringify({
4025
+ url: `${ADMIN_BASE_URL}accounts`,
4026
+ method: "GET"
4027
+ }),
4028
+ signal: controller.signal
4029
+ });
4030
+ const data = await response.json();
4031
+ if (!response.ok) {
4032
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4033
+ return { success: false, error: errorMessage };
4034
+ }
4035
+ const accounts = (data.accounts ?? []).map((a) => ({
4036
+ name: a.name ?? "",
4037
+ displayName: a.displayName ?? ""
4038
+ }));
4039
+ return { success: true, accounts };
4040
+ } finally {
4041
+ clearTimeout(timeout);
4042
+ }
4043
+ } catch (err) {
4044
+ const msg = err instanceof Error ? err.message : String(err);
4045
+ return { success: false, error: msg };
4046
+ }
4047
+ }
4048
+ });
4049
+
4050
+ // src/connectors/google-analytics-oauth/tools/list-properties.ts
4051
+ import { z as z22 } from "zod";
4052
+ var ADMIN_BASE_URL2 = "https://analyticsadmin.googleapis.com/v1beta/";
4053
+ var REQUEST_TIMEOUT_MS11 = 6e4;
4054
+ var cachedToken6 = null;
4055
+ async function getProxyToken6(config) {
4056
+ if (cachedToken6 && cachedToken6.expiresAt > Date.now() + 6e4) {
4057
+ return cachedToken6.token;
4058
+ }
4059
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4060
+ const res = await fetch(url, {
4061
+ method: "POST",
4062
+ headers: {
4063
+ "Content-Type": "application/json",
4064
+ "x-api-key": config.appApiKey,
4065
+ "project-id": config.projectId
4066
+ },
4067
+ body: JSON.stringify({
4068
+ sandboxId: config.sandboxId,
4069
+ issuedBy: "coding-agent"
4070
+ })
4071
+ });
4072
+ if (!res.ok) {
4073
+ const errorText = await res.text().catch(() => res.statusText);
4074
+ throw new Error(
4075
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4076
+ );
4077
+ }
4078
+ const data = await res.json();
4079
+ cachedToken6 = {
4080
+ token: data.token,
4081
+ expiresAt: new Date(data.expiresAt).getTime()
4082
+ };
4083
+ return data.token;
4084
+ }
4085
+ var inputSchema22 = z22.object({
4086
+ toolUseIntent: z22.string().optional().describe(
4087
+ "Brief description of what you intend to accomplish with this tool call"
4088
+ ),
4089
+ connectionId: z22.string().describe("ID of the Google Analytics OAuth connection to use"),
4090
+ accountName: z22.string().describe(
4091
+ "The account resource name (e.g., 'accounts/123456'). Obtained from the listAccounts tool."
4092
+ )
4093
+ });
4094
+ var outputSchema22 = z22.discriminatedUnion("success", [
4095
+ z22.object({
4096
+ success: z22.literal(true),
4097
+ properties: z22.array(
4098
+ z22.object({
4099
+ name: z22.string(),
4100
+ displayName: z22.string(),
4101
+ propertyId: z22.string()
4102
+ })
4103
+ )
4104
+ }),
4105
+ z22.object({
4106
+ success: z22.literal(false),
4107
+ error: z22.string()
4108
+ })
4109
+ ]);
4110
+ var listPropertiesTool = new ConnectorTool({
4111
+ name: "listProperties",
4112
+ description: "List GA4 properties for a given Google Analytics account. Returns property names, display names, and property IDs.",
4113
+ inputSchema: inputSchema22,
4114
+ outputSchema: outputSchema22,
4115
+ async execute({ connectionId, accountName }, connections, config) {
4116
+ const connection = connections.find((c) => c.id === connectionId);
4117
+ if (!connection) {
4118
+ return {
4119
+ success: false,
4120
+ error: `Connection ${connectionId} not found`
4121
+ };
4122
+ }
4123
+ console.log(
4124
+ `[connector-request] google-analytics-oauth/${connection.name}: listProperties for ${accountName}`
4125
+ );
4126
+ try {
4127
+ const token = await getProxyToken6(config.oauthProxy);
4128
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4129
+ const controller = new AbortController();
4130
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS11);
4131
+ try {
4132
+ const filter = encodeURIComponent(`parent:${accountName}`);
4133
+ const response = await fetch(proxyUrl, {
4134
+ method: "POST",
4135
+ headers: {
4136
+ "Content-Type": "application/json",
4137
+ Authorization: `Bearer ${token}`
4138
+ },
4139
+ body: JSON.stringify({
4140
+ url: `${ADMIN_BASE_URL2}properties?filter=${filter}`,
4141
+ method: "GET"
4142
+ }),
4143
+ signal: controller.signal
4144
+ });
4145
+ const data = await response.json();
4146
+ 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
+ return {
4154
+ name,
4155
+ displayName: p.displayName ?? "",
4156
+ propertyId
4157
+ };
4158
+ });
4159
+ return { success: true, properties };
4160
+ } finally {
4161
+ clearTimeout(timeout);
4162
+ }
4163
+ } catch (err) {
4164
+ const msg = err instanceof Error ? err.message : String(err);
4165
+ return { success: false, error: msg };
4166
+ }
4167
+ }
4168
+ });
4169
+
4170
+ // src/connectors/google-analytics-oauth/setup.ts
4171
+ var listAccountsToolName = `google-analytics-oauth_${listAccountsTool.name}`;
4172
+ var listPropertiesToolName = `google-analytics-oauth_${listPropertiesTool.name}`;
4173
+ var googleAnalyticsOauthSetup = new ConnectorSetup({
4174
+ ja: `## Google Analytics \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
4175
+
4176
+ \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Analytics (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
4177
+
4178
+ ### \u624B\u9806
4179
+
4180
+ #### \u30B9\u30C6\u30C3\u30D71: \u30A2\u30AB\u30A6\u30F3\u30C8\u9078\u629E
4181
+ 1. \`${listAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Analytics\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4182
+ 2. \u300C\u4F7F\u7528\u3059\u308B\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D\u3068\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u305F\u4E0A\u3067\u3001\`askUserQuestion\` \u3092\u547C\u3073\u51FA\u3059:
4183
+ - \`options\`: \u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (name)\` \u306E\u5F62\u5F0F
4184
+
4185
+ #### \u30B9\u30C6\u30C3\u30D72: \u30D7\u30ED\u30D1\u30C6\u30A3\u9078\u629E
4186
+ 1. \`${listPropertiesToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4187
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
4188
+ - \`parameterSlug\`: \`"property-id"\`
4189
+ - \`options\`: \u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (id: \u30D7\u30ED\u30D1\u30C6\u30A3ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30D1\u30C6\u30A3ID
4190
+ 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D73\u306B\u9032\u3080
4191
+
4192
+ #### \u30B9\u30C6\u30C3\u30D73: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
4193
+ 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
4194
+ - \`property\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E\u8868\u793A\u540D
4195
+ - \`propertyId\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3ID
4196
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
4197
+
4198
+ ### \u91CD\u8981\u306A\u5236\u7D04
4199
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30EC\u30DD\u30FC\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u306E\u307F
4200
+
4201
+ ### \u5B9F\u884C\u65B9\u91DD
4202
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
4203
+ - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
4204
+ en: `## Google Analytics Setup Instructions
4205
+
4206
+ Follow these steps to set up the Google Analytics (OAuth) connection.
4207
+
4208
+ ### Steps
4209
+
4210
+ #### Step 1: Account Selection
4211
+ 1. Call \`${listAccountsToolName}\` to get the list of Google Analytics accounts accessible with the OAuth credentials
4212
+ 2. Tell the user "Please select an account.", then call \`askUserQuestion\`:
4213
+ - \`options\`: The account list. Each option's \`label\` should be \`Display Name (name)\`
4214
+
4215
+ #### Step 2: Property Selection
4216
+ 1. Call \`${listPropertiesToolName}\` to get the list of properties for the selected account
4217
+ 2. Call \`updateConnectionParameters\`:
4218
+ - \`parameterSlug\`: \`"property-id"\`
4219
+ - \`options\`: The property list. Each option's \`label\` should be \`Display Name (id: propertyId)\`, \`value\` should be the property ID
4220
+ 3. The \`label\` of the user's selected property will arrive as a message. Proceed to Step 3
4221
+
4222
+ #### Step 3: Complete Setup
4223
+ 1. Call \`updateConnectionContext\`:
4224
+ - \`property\`: The selected property's display name
4225
+ - \`propertyId\`: The selected property ID
4226
+ - \`note\`: Brief description of the setup
4227
+
4228
+ ### Important Constraints
4229
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
4230
+
4231
+ ### Execution Policy
4232
+ - Write only 1 sentence between tool calls, then immediately call the next tool
4233
+ - Skip unnecessary explanations and proceed efficiently`
4234
+ });
4235
+
4236
+ // src/connectors/google-analytics-oauth/tools/request.ts
4237
+ import { z as z23 } from "zod";
4238
+ var BASE_URL5 = "https://analyticsdata.googleapis.com/v1beta/";
4239
+ var REQUEST_TIMEOUT_MS12 = 6e4;
4240
+ var cachedToken7 = null;
4241
+ async function getProxyToken7(config) {
4242
+ if (cachedToken7 && cachedToken7.expiresAt > Date.now() + 6e4) {
4243
+ return cachedToken7.token;
4244
+ }
4245
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4246
+ const res = await fetch(url, {
4247
+ method: "POST",
4248
+ headers: {
4249
+ "Content-Type": "application/json",
4250
+ "x-api-key": config.appApiKey,
4251
+ "project-id": config.projectId
4252
+ },
4253
+ body: JSON.stringify({
4254
+ sandboxId: config.sandboxId,
4255
+ issuedBy: "coding-agent"
4256
+ })
4257
+ });
4258
+ if (!res.ok) {
4259
+ const errorText = await res.text().catch(() => res.statusText);
4260
+ throw new Error(
4261
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4262
+ );
4263
+ }
4264
+ const data = await res.json();
4265
+ cachedToken7 = {
4266
+ token: data.token,
4267
+ expiresAt: new Date(data.expiresAt).getTime()
4268
+ };
4269
+ return data.token;
4270
+ }
4271
+ var inputSchema23 = z23.object({
4272
+ toolUseIntent: z23.string().optional().describe(
4273
+ "Brief description of what you intend to accomplish with this tool call"
4274
+ ),
4275
+ connectionId: z23.string().describe("ID of the Google Analytics OAuth connection to use"),
4276
+ method: z23.enum(["GET", "POST"]).describe("HTTP method"),
4277
+ path: z23.string().describe(
4278
+ "API path appended to https://analyticsdata.googleapis.com/v1beta/ (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."
4279
+ ),
4280
+ body: z23.record(z23.string(), z23.unknown()).optional().describe("POST request body (JSON)")
4281
+ });
4282
+ var outputSchema23 = z23.discriminatedUnion("success", [
4283
+ z23.object({
4284
+ success: z23.literal(true),
4285
+ status: z23.number(),
4286
+ data: z23.record(z23.string(), z23.unknown())
4287
+ }),
4288
+ z23.object({
4289
+ success: z23.literal(false),
4290
+ error: z23.string()
4291
+ })
4292
+ ]);
4293
+ var requestTool7 = new ConnectorTool({
4294
+ name: "request",
4295
+ description: `Send authenticated requests to the Google Analytics Data API v1beta.
4296
+ Authentication is handled automatically via OAuth proxy.
4297
+ {propertyId} in the path is automatically replaced with the connection's property ID.`,
4298
+ inputSchema: inputSchema23,
4299
+ outputSchema: outputSchema23,
4300
+ async execute({ connectionId, method, path, body }, connections, config) {
4301
+ const connection = connections.find((c) => c.id === connectionId);
4302
+ if (!connection) {
4303
+ return {
4304
+ success: false,
4305
+ error: `Connection ${connectionId} not found`
4306
+ };
4307
+ }
4308
+ console.log(
4309
+ `[connector-request] google-analytics-oauth/${connection.name}: ${method} ${path}`
4310
+ );
4311
+ try {
4312
+ const propertyId = parameters8.propertyId.tryGetValue(connection);
4313
+ const resolvedPath = propertyId ? path.replace(/\{propertyId\}/g, propertyId) : path;
4314
+ const url = `${BASE_URL5}${resolvedPath}`;
4315
+ const token = await getProxyToken7(config.oauthProxy);
4316
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4317
+ const controller = new AbortController();
4318
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS12);
4319
+ try {
4320
+ const response = await fetch(proxyUrl, {
4321
+ method: "POST",
4322
+ headers: {
4323
+ "Content-Type": "application/json",
4324
+ Authorization: `Bearer ${token}`
4325
+ },
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
+ }),
4334
+ signal: controller.signal
4335
+ });
4336
+ const data = await response.json();
4337
+ if (!response.ok) {
4338
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4339
+ return { success: false, error: errorMessage };
4340
+ }
4341
+ return { success: true, status: response.status, data };
4342
+ } finally {
4343
+ clearTimeout(timeout);
4344
+ }
4345
+ } catch (err) {
4346
+ const msg = err instanceof Error ? err.message : String(err);
4347
+ return { success: false, error: msg };
4348
+ }
4349
+ }
4350
+ });
4351
+
4352
+ // src/connectors/google-analytics-oauth/index.ts
4353
+ var tools18 = {
4354
+ request: requestTool7,
4355
+ listAccounts: listAccountsTool,
4356
+ listProperties: listPropertiesTool
4357
+ };
4358
+ var googleAnalyticsOauthConnector = new ConnectorPlugin({
4359
+ slug: "google-analytics",
4360
+ authType: AUTH_TYPES.OAUTH,
4361
+ name: "Google Analytics (OAuth)",
4362
+ description: "Connect to Google Analytics for web analytics and reporting using OAuth.",
4363
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/7fs0ipzxuD9mACDzBATtxX/3c53ed90d15c96483e4f78cb29dab5e9/google-analytics.svg",
4364
+ parameters: parameters8,
4365
+ releaseFlag: { dev1: true, dev2: true, prod: false },
4366
+ setup: googleAnalyticsOauthSetup,
4367
+ proxyPolicy: {
4368
+ allowlist: [
4369
+ {
4370
+ host: "analyticsdata.googleapis.com",
4371
+ methods: ["GET", "POST"]
4372
+ },
4373
+ {
4374
+ host: "analyticsadmin.googleapis.com",
4375
+ methods: ["GET"]
4376
+ }
4377
+ ]
4378
+ },
4379
+ systemPrompt: `## Google Analytics Data API (OAuth, Read-Only)
4380
+ - Call the GA4 Data API using the authenticated request tool
4381
+ - {propertyId} in the path is automatically replaced
4382
+
4383
+ ### Get Metadata (Check available dimensions and metrics)
4384
+ - GET properties/{propertyId}/metadata
4385
+
4386
+ ### Get Report
4387
+ - POST properties/{propertyId}:runReport
4388
+ - Body example:
4389
+ {
4390
+ "dateRanges": [{"startDate": "7daysAgo", "endDate": "today"}],
4391
+ "dimensions": [{"name": "date"}],
4392
+ "metrics": [{"name": "activeUsers"}],
4393
+ "limit": 100
4394
+ }
4395
+
4396
+ ### Common Dimensions
4397
+ date, country, city, deviceCategory, browser, pagePath, pageTitle,
4398
+ sessionSource, sessionMedium, eventName
4399
+
4400
+ ### Common Metrics
4401
+ activeUsers, sessions, screenPageViews, bounceRate,
4402
+ averageSessionDuration, conversions, totalRevenue
4403
+
4404
+ ### Date Specification
4405
+ - Absolute: "2024-01-01"
4406
+ - Relative: "today", "yesterday", "7daysAgo", "30daysAgo"
4407
+
4408
+ ## Google Analytics SDK (TypeScript handler)
4409
+
4410
+ \`\`\`ts
4411
+ import { connection } from "@squadbase/vite-server/connectors/google-analytics-oauth";
4412
+
4413
+ const ga = connection("<connectionId>");
4414
+
4415
+ // Authenticated fetch (returns standard Response)
4416
+ const res = await ga.request("properties/{propertyId}:runReport", {
4417
+ method: "POST",
4418
+ body: JSON.stringify({ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], metrics: [{ name: "activeUsers" }] }),
4419
+ });
4420
+ const data = await res.json();
4421
+
4422
+ // Convenience methods
4423
+ const { rows, rowCount } = await ga.runReport({
4424
+ dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
4425
+ dimensions: [{ name: "date" }],
4426
+ metrics: [{ name: "activeUsers" }, { name: "sessions" }],
4427
+ limit: 100,
4428
+ });
4429
+ const metadata = await ga.getMetadata();
4430
+ const realtime = await ga.runRealtimeReport({
4431
+ metrics: [{ name: "activeUsers" }],
4432
+ dimensions: [{ name: "country" }],
4433
+ });
4434
+ \`\`\``,
4435
+ tools: tools18,
4436
+ async checkConnection(params, config) {
4437
+ const { proxyFetch } = config;
4438
+ const propertyId = params[parameters8.propertyId.slug];
4439
+ if (!propertyId) {
4440
+ return { success: true };
4441
+ }
4442
+ const url = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}/metadata`;
4443
+ try {
4444
+ const res = await proxyFetch(url, { method: "GET" });
4445
+ if (!res.ok) {
4446
+ const errorText = await res.text().catch(() => res.statusText);
4447
+ return {
4448
+ success: false,
4449
+ error: `Google Analytics API failed: HTTP ${res.status} ${errorText}`
4450
+ };
4451
+ }
4452
+ return { success: true };
4453
+ } catch (error) {
4454
+ return {
4455
+ success: false,
4456
+ error: error instanceof Error ? error.message : String(error)
4457
+ };
4458
+ }
4459
+ }
4460
+ });
4461
+
4462
+ // src/connectors/google-ads-oauth/tools/list-customers.ts
4463
+ import { z as z24 } from "zod";
4464
+ var BASE_URL6 = "https://googleads.googleapis.com/v18/";
4465
+ var REQUEST_TIMEOUT_MS13 = 6e4;
4466
+ var cachedToken8 = null;
4467
+ async function getProxyToken8(config) {
4468
+ if (cachedToken8 && cachedToken8.expiresAt > Date.now() + 6e4) {
4469
+ return cachedToken8.token;
4470
+ }
4471
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4472
+ const res = await fetch(url, {
4473
+ method: "POST",
4474
+ headers: {
4475
+ "Content-Type": "application/json",
4476
+ "x-api-key": config.appApiKey,
4477
+ "project-id": config.projectId
4478
+ },
4479
+ body: JSON.stringify({
4480
+ sandboxId: config.sandboxId,
4481
+ issuedBy: "coding-agent"
4482
+ })
4483
+ });
4484
+ if (!res.ok) {
4485
+ const errorText = await res.text().catch(() => res.statusText);
4486
+ throw new Error(
4487
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4488
+ );
4489
+ }
4490
+ const data = await res.json();
4491
+ cachedToken8 = {
4492
+ token: data.token,
4493
+ expiresAt: new Date(data.expiresAt).getTime()
4494
+ };
4495
+ return data.token;
4496
+ }
4497
+ var inputSchema24 = z24.object({
4498
+ toolUseIntent: z24.string().optional().describe(
4499
+ "Brief description of what you intend to accomplish with this tool call"
4500
+ ),
4501
+ connectionId: z24.string().describe("ID of the Google Ads OAuth connection to use")
4502
+ });
4503
+ var outputSchema24 = z24.discriminatedUnion("success", [
4504
+ z24.object({
4505
+ success: z24.literal(true),
4506
+ customers: z24.array(
4507
+ z24.object({
4508
+ customerId: z24.string(),
4509
+ descriptiveName: z24.string()
4510
+ })
4511
+ )
4512
+ }),
4513
+ z24.object({
4514
+ success: z24.literal(false),
4515
+ error: z24.string()
4516
+ })
4517
+ ]);
4518
+ var listCustomersTool = new ConnectorTool({
4519
+ name: "listCustomers",
4520
+ description: "List Google Ads customer accounts accessible with the current OAuth credentials.",
4521
+ inputSchema: inputSchema24,
4522
+ outputSchema: outputSchema24,
4523
+ async execute({ connectionId }, connections, config) {
4524
+ const connection = connections.find((c) => c.id === connectionId);
4525
+ if (!connection) {
4526
+ return {
4527
+ success: false,
4528
+ error: `Connection ${connectionId} not found`
4529
+ };
4530
+ }
4531
+ console.log(
4532
+ `[connector-request] google-ads-oauth/${connection.name}: listCustomers`
4533
+ );
4534
+ try {
4535
+ const developerToken = parameters9.developerToken.getValue(connection);
4536
+ const token = await getProxyToken8(config.oauthProxy);
4537
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4538
+ const controller = new AbortController();
4539
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS13);
4540
+ try {
4541
+ const response = await fetch(proxyUrl, {
4542
+ method: "POST",
4543
+ headers: {
4544
+ "Content-Type": "application/json",
4545
+ Authorization: `Bearer ${token}`
4546
+ },
4547
+ body: JSON.stringify({
4548
+ url: `${BASE_URL6}customers:listAccessibleCustomers`,
4549
+ method: "GET",
4550
+ headers: {
4551
+ "developer-token": developerToken
4552
+ }
4553
+ }),
4554
+ signal: controller.signal
4555
+ });
4556
+ const data = await response.json();
4557
+ if (!response.ok) {
4558
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
4559
+ return { success: false, error: errorMessage };
4560
+ }
4561
+ const customerIds = (data.resourceNames ?? []).map(
4562
+ (rn) => rn.replace(/^customers\//, "")
4563
+ );
4564
+ const customers = [];
4565
+ for (const cid of customerIds) {
4566
+ try {
4567
+ const detailResponse = await fetch(proxyUrl, {
4568
+ method: "POST",
4569
+ headers: {
4570
+ "Content-Type": "application/json",
4571
+ Authorization: `Bearer ${token}`
4572
+ },
4573
+ body: JSON.stringify({
4574
+ url: `${BASE_URL6}customers/${cid}/googleAds:searchStream`,
4575
+ method: "POST",
4576
+ headers: {
4577
+ "Content-Type": "application/json",
4578
+ "developer-token": developerToken,
4579
+ "login-customer-id": cid
4580
+ },
4581
+ body: JSON.stringify({
4582
+ query: "SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1"
4583
+ })
4584
+ }),
4585
+ signal: controller.signal
4586
+ });
4587
+ if (detailResponse.ok) {
4588
+ const detailData = await detailResponse.json();
4589
+ const customer = detailData?.[0]?.results?.[0]?.customer;
4590
+ customers.push({
4591
+ customerId: cid,
4592
+ descriptiveName: customer?.descriptiveName ?? cid
4593
+ });
4594
+ } else {
4595
+ customers.push({
4596
+ customerId: cid,
4597
+ descriptiveName: cid
4598
+ });
4599
+ }
4600
+ } catch {
4601
+ customers.push({
4602
+ customerId: cid,
4603
+ descriptiveName: cid
4604
+ });
4605
+ }
4606
+ }
4607
+ return { success: true, customers };
4608
+ } finally {
4609
+ clearTimeout(timeout);
4610
+ }
4611
+ } catch (err) {
4612
+ const msg = err instanceof Error ? err.message : String(err);
4613
+ return { success: false, error: msg };
4614
+ }
4615
+ }
4616
+ });
4617
+
4618
+ // src/connectors/google-ads-oauth/setup.ts
4619
+ var listCustomersToolName = `google-ads-oauth_${listCustomersTool.name}`;
4620
+ var googleAdsSetup = new ConnectorSetup({
4621
+ ja: `## Google Ads \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u624B\u9806
4622
+
4623
+ \u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Ads (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
4624
+
4625
+ ### \u624B\u9806
4626
+
4627
+ #### \u30B9\u30C6\u30C3\u30D71: \u30AB\u30B9\u30BF\u30DE\u30FC\u9078\u629E
4628
+ 1. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
4629
+ 2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
4630
+ - \`parameterSlug\`: \`"customer-id"\`
4631
+ - \`options\`: \u30AB\u30B9\u30BF\u30DE\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30AB\u30B9\u30BF\u30DE\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30B9\u30BF\u30DE\u30FCID
4632
+ 3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u30B9\u30C6\u30C3\u30D72\u306B\u9032\u3080
4633
+
4634
+ #### \u30B9\u30C6\u30C3\u30D72: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86
4635
+ 1. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
4636
+ - \`customer\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E\u8868\u793A\u540D
4637
+ - \`customerId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30B9\u30BF\u30DE\u30FCID
4638
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
4639
+
4640
+ ### \u91CD\u8981\u306A\u5236\u7D04
4641
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30EC\u30DD\u30FC\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u306E\u307F
4642
+
4643
+ ### \u5B9F\u884C\u65B9\u91DD
4644
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057
4645
+ - \u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
4646
+ en: `## Google Ads Setup Instructions
4647
+
4648
+ Follow these steps to set up the Google Ads (OAuth) connection.
4649
+
4650
+ ### Steps
4651
+
4652
+ #### Step 1: Customer Selection
4653
+ 1. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
4654
+ 2. Call \`updateConnectionParameters\`:
4655
+ - \`parameterSlug\`: \`"customer-id"\`
4656
+ - \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
4657
+ 3. The \`label\` of the user's selected customer will arrive as a message. Proceed to Step 2
4658
+
4659
+ #### Step 2: Complete Setup
4660
+ 1. Call \`updateConnectionContext\`:
4661
+ - \`customer\`: The selected customer's display name
4662
+ - \`customerId\`: The selected customer ID
4663
+ - \`note\`: Brief description of the setup
4664
+
4665
+ ### Important Constraints
4666
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
4667
+
4668
+ ### Execution Policy
4669
+ - Write only 1 sentence between tool calls, then immediately call the next tool
4670
+ - Skip unnecessary explanations and proceed efficiently`
4671
+ });
4672
+
4673
+ // src/connectors/google-ads-oauth/tools/request.ts
4674
+ import { z as z25 } from "zod";
4675
+ var BASE_URL7 = "https://googleads.googleapis.com/v18/";
4676
+ var REQUEST_TIMEOUT_MS14 = 6e4;
4677
+ var cachedToken9 = null;
4678
+ async function getProxyToken9(config) {
4679
+ if (cachedToken9 && cachedToken9.expiresAt > Date.now() + 6e4) {
4680
+ return cachedToken9.token;
4681
+ }
4682
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
4683
+ const res = await fetch(url, {
4684
+ method: "POST",
4685
+ headers: {
4686
+ "Content-Type": "application/json",
4687
+ "x-api-key": config.appApiKey,
4688
+ "project-id": config.projectId
4689
+ },
4690
+ body: JSON.stringify({
4691
+ sandboxId: config.sandboxId,
4692
+ issuedBy: "coding-agent"
4693
+ })
4694
+ });
4695
+ if (!res.ok) {
4696
+ const errorText = await res.text().catch(() => res.statusText);
4697
+ throw new Error(
4698
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
4699
+ );
4700
+ }
4701
+ const data = await res.json();
4702
+ cachedToken9 = {
4703
+ token: data.token,
4704
+ expiresAt: new Date(data.expiresAt).getTime()
4705
+ };
4706
+ return data.token;
4707
+ }
4708
+ var inputSchema25 = z25.object({
4709
+ toolUseIntent: z25.string().optional().describe(
4710
+ "Brief description of what you intend to accomplish with this tool call"
4711
+ ),
4712
+ connectionId: z25.string().describe("ID of the Google Ads OAuth connection to use"),
4713
+ method: z25.enum(["GET", "POST"]).describe("HTTP method"),
4714
+ path: z25.string().describe(
4715
+ "API path appended to https://googleads.googleapis.com/v18/ (e.g., 'customers/{customerId}/googleAds:searchStream'). {customerId} is automatically replaced."
4716
+ ),
4717
+ body: z25.record(z25.string(), z25.unknown()).optional().describe("POST request body (JSON)")
4718
+ });
4719
+ var outputSchema25 = z25.discriminatedUnion("success", [
4720
+ z25.object({
4721
+ success: z25.literal(true),
4722
+ status: z25.number(),
4723
+ data: z25.unknown()
4724
+ }),
4725
+ z25.object({
4726
+ success: z25.literal(false),
4727
+ error: z25.string()
4728
+ })
4729
+ ]);
4730
+ var requestTool8 = new ConnectorTool({
4731
+ name: "request",
4732
+ description: `Send authenticated requests to the Google Ads API v18.
4733
+ Authentication is handled automatically via OAuth proxy.
4734
+ {customerId} in the path is automatically replaced with the connection's customer ID (hyphens removed).`,
4735
+ inputSchema: inputSchema25,
4736
+ outputSchema: outputSchema25,
4737
+ async execute({ connectionId, method, path, body }, connections, config) {
4738
+ const connection = connections.find((c) => c.id === connectionId);
4739
+ if (!connection) {
4740
+ return {
4741
+ success: false,
4742
+ error: `Connection ${connectionId} not found`
4743
+ };
4744
+ }
4745
+ console.log(
4746
+ `[connector-request] google-ads-oauth/${connection.name}: ${method} ${path}`
4747
+ );
4748
+ try {
4749
+ const rawCustomerId = parameters9.customerId.tryGetValue(connection);
4750
+ const customerId = rawCustomerId?.replace(/-/g, "") ?? "";
4751
+ const resolvedPath = customerId ? path.replace(/\{customerId\}/g, customerId) : path;
4752
+ const url = `${BASE_URL7}${resolvedPath}`;
4753
+ const token = await getProxyToken9(config.oauthProxy);
4754
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
4755
+ const controller = new AbortController();
4756
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS14);
4757
+ try {
4758
+ const developerToken = parameters9.developerToken.getValue(connection);
4759
+ const response = await fetch(proxyUrl, {
4760
+ method: "POST",
4761
+ headers: {
4762
+ "Content-Type": "application/json",
4763
+ Authorization: `Bearer ${token}`
4764
+ },
4765
+ body: JSON.stringify({
4766
+ url,
4767
+ method,
4768
+ headers: {
4769
+ "Content-Type": "application/json",
4770
+ "developer-token": developerToken,
4771
+ ...customerId ? { "login-customer-id": customerId } : {}
4772
+ },
4773
+ ...method === "POST" && body ? { body: JSON.stringify(body) } : {}
4774
+ }),
4775
+ signal: controller.signal
4776
+ });
4777
+ const data = await response.json();
4778
+ if (!response.ok) {
4779
+ const dataObj = data;
4780
+ const errorMessage = typeof dataObj?.error === "string" ? dataObj.error : typeof dataObj?.message === "string" ? dataObj.message : `HTTP ${response.status} ${response.statusText}`;
4781
+ return { success: false, error: errorMessage };
4782
+ }
4783
+ return { success: true, status: response.status, data };
4784
+ } finally {
4785
+ clearTimeout(timeout);
4786
+ }
4787
+ } catch (err) {
4788
+ const msg = err instanceof Error ? err.message : String(err);
4789
+ return { success: false, error: msg };
4790
+ }
4791
+ }
4792
+ });
4793
+
4794
+ // src/connectors/google-ads-oauth/index.ts
4795
+ var tools19 = {
4796
+ request: requestTool8,
4797
+ listCustomers: listCustomersTool
4798
+ };
4799
+ var googleAdsOauthConnector = new ConnectorPlugin({
4800
+ slug: "google-ads",
4801
+ authType: AUTH_TYPES.OAUTH,
4802
+ name: "Google Ads (OAuth)",
4803
+ description: "Connect to Google Ads for advertising campaign data and reporting using OAuth.",
4804
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6lQFddkMPLCzLJBPn1TiSe/4b22b78cd2a9cc91e9ee9283e5c80a51/google-ads.svg",
4805
+ parameters: parameters9,
4806
+ releaseFlag: { dev1: true, dev2: true, prod: false },
4807
+ setup: googleAdsSetup,
4808
+ proxyPolicy: {
4809
+ allowlist: [
4810
+ {
4811
+ host: "googleads.googleapis.com",
4812
+ methods: ["GET", "POST"]
4813
+ }
4814
+ ]
4815
+ },
4816
+ systemPrompt: `## Google Ads API (OAuth, Read-Only)
4817
+ - Use GAQL (Google Ads Query Language) to query campaign data
4818
+ - {customerId} in the path is automatically replaced (hyphens removed)
4819
+
4820
+ ### Query Data (searchStream)
4821
+ - POST customers/{customerId}/googleAds:searchStream
4822
+ - Body: { "query": "SELECT campaign.id, campaign.name, metrics.impressions FROM campaign WHERE segments.date DURING LAST_30_DAYS" }
4823
+
4824
+ ### Common GAQL Resources
4825
+ - \`campaign\`: Campaign data (campaign.id, campaign.name, campaign.status)
4826
+ - \`ad_group\`: Ad group data (ad_group.id, ad_group.name, ad_group.status)
4827
+ - \`ad_group_ad\`: Ad data (ad_group_ad.ad.id, ad_group_ad.status)
4828
+ - \`keyword_view\`: Keyword performance data
4829
+
4830
+ ### Common Metrics
4831
+ metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions,
4832
+ metrics.ctr, metrics.average_cpc, metrics.conversions_value
4833
+
4834
+ ### Common Segments
4835
+ segments.date, segments.device, segments.ad_network_type
4836
+
4837
+ ### Date Filters
4838
+ - \`DURING LAST_7_DAYS\`, \`DURING LAST_30_DAYS\`, \`DURING THIS_MONTH\`
4839
+ - \`WHERE segments.date BETWEEN '2024-01-01' AND '2024-01-31'\`
4840
+
4841
+ ### Tips
4842
+ - cost_micros is in micros (divide by 1,000,000 for actual currency)
4843
+ - Use LIMIT to restrict result count
4844
+ - Always include relevant WHERE clauses to filter data
4845
+
4846
+ ## Google Ads SDK (TypeScript handler)
4847
+
4848
+ \`\`\`ts
4849
+ import { connection } from "@squadbase/vite-server/connectors/google-ads-oauth";
4850
+
4851
+ const ads = connection("<connectionId>");
4852
+
4853
+ // Execute a GAQL query
4854
+ const rows = await ads.search(
4855
+ "SELECT campaign.name, metrics.impressions, metrics.clicks FROM campaign WHERE segments.date DURING LAST_7_DAYS"
4856
+ );
4857
+ rows.forEach(row => console.log(row));
4858
+
4859
+ // List accessible customer accounts
4860
+ const customerIds = await ads.listAccessibleCustomers();
4861
+ \`\`\``,
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
+ }
4905
+ });
4906
+
3668
4907
  // src/connectors/registry.ts
3669
4908
  var plugins = {
3670
4909
  snowflake: snowflakeConnector,
@@ -3682,7 +4921,10 @@ var plugins = {
3682
4921
  squadbaseDb: squadbaseDbConnector,
3683
4922
  kintone: kintoneConnector,
3684
4923
  wixStore: wixStoreConnector,
3685
- openai: openaiConnector
4924
+ openai: openaiConnector,
4925
+ googleSheetsOauth: googleSheetsOauthConnector,
4926
+ googleAnalyticsOauth: googleAnalyticsOauthConnector,
4927
+ googleAdsOauth: googleAdsOauthConnector
3686
4928
  };
3687
4929
  var connectors = {
3688
4930
  ...plugins,
@@ -3718,7 +4960,10 @@ export {
3718
4960
  connectors,
3719
4961
  databricksConnector,
3720
4962
  dbtConnector,
4963
+ googleAdsOauthConnector,
3721
4964
  googleAnalyticsConnector,
4965
+ googleAnalyticsOauthConnector,
4966
+ googleSheetsOauthConnector,
3722
4967
  kintoneConnector,
3723
4968
  mysqlConnector,
3724
4969
  openaiConnector,