@tangle-network/agent-integrations 0.53.30 → 0.53.32

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.
@@ -182,6 +182,9 @@ __export(adapters_exports, {
182
182
  copyAiConnector: () => copyAiConnector,
183
183
  couchbaseConnector: () => couchbaseConnector,
184
184
  createKafkaConnector: () => createKafkaConnector,
185
+ createMySqlConnector: () => createMySqlConnector,
186
+ createRabbitMqConnector: () => createRabbitMqConnector,
187
+ createRedisConnector: () => createRedisConnector,
185
188
  createSftpConnector: () => createSftpConnector,
186
189
  createTangleIdentityClient: () => createTangleIdentityClient,
187
190
  crispConnector: () => crispConnector,
@@ -218,6 +221,7 @@ __export(adapters_exports, {
218
221
  dripConnector: () => dripConnector,
219
222
  dropboxConnector: () => dropboxConnector,
220
223
  dubConnector: () => dubConnector,
224
+ duckdbConnector: () => duckdbConnector,
221
225
  dumplingAiConnector: () => dumplingAiConnector,
222
226
  dustConnector: () => dustConnector,
223
227
  easyPeasyAiConnector: () => easyPeasyAiConnector,
@@ -438,6 +442,7 @@ __export(adapters_exports, {
438
442
  mondayConnector: () => mondayConnector,
439
443
  mongodbConnector: () => mongodbConnector,
440
444
  mycaseConnector: () => mycaseConnector,
445
+ mysqlConnector: () => mysqlConnector,
441
446
  n8nConnector: () => n8nConnector,
442
447
  netlifyConnector: () => netlifyConnector,
443
448
  netsuiteConnector: () => netsuiteConnector,
@@ -508,6 +513,7 @@ __export(adapters_exports, {
508
513
  quadernoConnector: () => quadernoConnector,
509
514
  quickbooksConnector: () => quickbooksConnector,
510
515
  quickzuConnector: () => quickzuConnector,
516
+ rabbitMqConnector: () => rabbitMqConnector,
511
517
  raiaAiConnector: () => raiaAiConnector,
512
518
  rampConnector: () => rampConnector,
513
519
  rapidtextAiConnector: () => rapidtextAiConnector,
@@ -515,6 +521,7 @@ __export(adapters_exports, {
515
521
  recallAiConnector: () => recallAiConnector,
516
522
  recurlyConnector: () => recurlyConnector,
517
523
  redditConnector: () => redditConnector,
524
+ redisConnector: () => redisConnector,
518
525
  reoonVerifierConnector: () => reoonVerifierConnector,
519
526
  replyIoConnector: () => replyIoConnector,
520
527
  resolveConnectorAdapterFactoryOptions: () => resolveConnectorAdapterFactoryOptions,
@@ -1123,6 +1130,10 @@ function sameUrl(left, right) {
1123
1130
  function applyCredentials(headers, url, placement, credentials) {
1124
1131
  const token = credentialToken(credentials);
1125
1132
  if (placement.kind === "bearer") headers.authorization = `Bearer ${token}`;
1133
+ if (placement.kind === "authorization-by-auth") {
1134
+ const prefix = credentials.kind === "oauth2" ? placement.oauth2Prefix ?? "Bearer " : placement.apiKeyPrefix ?? "";
1135
+ headers.authorization = `${prefix}${token}`;
1136
+ }
1126
1137
  if (placement.kind === "basic-api-key") {
1127
1138
  headers.authorization = `Basic ${Buffer.from(`${token}:`).toString("base64")}`;
1128
1139
  }
@@ -6500,6 +6511,294 @@ var digitalPilotConnector = declarativeRestConnector({
6500
6511
  ]
6501
6512
  });
6502
6513
 
6514
+ // src/connectors/adapters/duckdb.ts
6515
+ import {
6516
+ DuckDBInstance
6517
+ } from "@duckdb/node-api";
6518
+ var MAX_ARGUMENTS = 100;
6519
+ var MAX_COLUMNS2 = 256;
6520
+ var MAX_EXECUTION_MS = 5e3;
6521
+ var MAX_INPUT_BYTES = 10 * 1024 * 1024;
6522
+ var MAX_QUERY_BYTES = 2e4;
6523
+ var MAX_RESULT_BYTES = 10 * 1024 * 1024;
6524
+ var MAX_ROWS = 1e4;
6525
+ var MAX_SCHEMA_DEPTH = 16;
6526
+ var MAX_TABLES = 16;
6527
+ var TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/;
6528
+ var duckdbConnector = {
6529
+ manifest: {
6530
+ kind: "duckdb",
6531
+ displayName: "DuckDB",
6532
+ description: "Load bounded JSON tables into a throwaway in-memory DuckDB database and run a parameterized query.",
6533
+ auth: { kind: "none" },
6534
+ defaultConsistencyModel: "advisory",
6535
+ category: "database",
6536
+ capabilities: [
6537
+ {
6538
+ name: "create.and.query.db",
6539
+ class: "read",
6540
+ description: "Create in-memory tables from JSON arrays and return a bounded parameterized SQL query result.",
6541
+ parameters: {
6542
+ type: "object",
6543
+ properties: {
6544
+ tables: {
6545
+ type: "array",
6546
+ minItems: 1,
6547
+ maxItems: MAX_TABLES,
6548
+ items: {
6549
+ type: "object",
6550
+ properties: {
6551
+ name: { type: "string", pattern: TABLE_NAME.source, maxLength: 63 },
6552
+ data: { type: "array", maxItems: MAX_ROWS, items: { type: "object" } },
6553
+ schema: { type: "object", maxProperties: MAX_COLUMNS2 }
6554
+ },
6555
+ required: ["name", "data"],
6556
+ additionalProperties: false
6557
+ }
6558
+ },
6559
+ query: {
6560
+ type: "string",
6561
+ minLength: 1,
6562
+ maxLength: MAX_QUERY_BYTES,
6563
+ description: "A single relation-returning SQL query. Use $1, $2, and args for dynamic values."
6564
+ },
6565
+ args: {
6566
+ type: "array",
6567
+ maxItems: MAX_ARGUMENTS,
6568
+ items: { type: ["string", "number", "boolean", "null"] }
6569
+ },
6570
+ maxRows: { type: "integer", minimum: 1, maximum: MAX_ROWS, default: 1e3 }
6571
+ },
6572
+ required: ["tables", "query"],
6573
+ additionalProperties: false
6574
+ }
6575
+ }
6576
+ ]
6577
+ },
6578
+ async executeRead({ capabilityName, args }) {
6579
+ if (capabilityName !== "create.and.query.db") {
6580
+ throw new Error(`Unknown DuckDB capability: ${capabilityName}`);
6581
+ }
6582
+ const tables = readTables(args.tables);
6583
+ const query2 = readQuery(args.query);
6584
+ const queryArgs = readArguments(args.args);
6585
+ const maxRows = readBoundedInteger(args.maxRows, 1e3, 1, MAX_ROWS, "maxRows");
6586
+ const instance = await DuckDBInstance.create(":memory:");
6587
+ let connection;
6588
+ let timedOut = false;
6589
+ let timeout;
6590
+ try {
6591
+ const activeConnection = await instance.connect();
6592
+ connection = activeConnection;
6593
+ timeout = setTimeout(() => {
6594
+ timedOut = true;
6595
+ activeConnection.interrupt();
6596
+ }, MAX_EXECUTION_MS);
6597
+ await configureConnection(activeConnection);
6598
+ throwIfTimedOut(timedOut);
6599
+ for (const table of tables) {
6600
+ await loadTable(activeConnection, table);
6601
+ throwIfTimedOut(timedOut);
6602
+ }
6603
+ const reader = await activeConnection.runAndReadAll(
6604
+ `SELECT * FROM (${query2}) AS "_tangle_result" LIMIT ${maxRows + 1}`,
6605
+ queryArgs
6606
+ );
6607
+ throwIfTimedOut(timedOut);
6608
+ const allRows = reader.getRowObjectsJson();
6609
+ const truncated = allRows.length > maxRows;
6610
+ const rows = truncated ? allRows.slice(0, maxRows) : allRows;
6611
+ assertResultSize(rows);
6612
+ return {
6613
+ data: {
6614
+ rows,
6615
+ rowCount: rows.length,
6616
+ truncated,
6617
+ columns: reader.columnNameAndTypeObjectsJson()
6618
+ },
6619
+ fetchedAt: Date.now()
6620
+ };
6621
+ } catch (error) {
6622
+ if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
6623
+ throw error;
6624
+ } finally {
6625
+ if (timeout) clearTimeout(timeout);
6626
+ try {
6627
+ connection?.closeSync();
6628
+ } finally {
6629
+ instance.closeSync();
6630
+ }
6631
+ }
6632
+ },
6633
+ async test() {
6634
+ const instance = await DuckDBInstance.create(":memory:");
6635
+ let connection;
6636
+ try {
6637
+ const activeConnection = await instance.connect();
6638
+ connection = activeConnection;
6639
+ await configureConnection(activeConnection);
6640
+ const reader = await activeConnection.runAndReadAll("SELECT 1 AS ok");
6641
+ if (reader.getRowObjectsJson()[0]?.ok !== 1) return { ok: false, reason: "DuckDB SELECT 1 returned an unexpected result" };
6642
+ return { ok: true };
6643
+ } catch (error) {
6644
+ return { ok: false, reason: error instanceof Error ? error.message : String(error) };
6645
+ } finally {
6646
+ try {
6647
+ connection?.closeSync();
6648
+ } finally {
6649
+ instance.closeSync();
6650
+ }
6651
+ }
6652
+ }
6653
+ };
6654
+ async function configureConnection(connection) {
6655
+ await connection.run(`
6656
+ SET memory_limit = '256MB';
6657
+ SET max_temp_directory_size = '0B';
6658
+ SET threads = 2;
6659
+ SET enable_logging = false;
6660
+ SET enable_external_access = false;
6661
+ SET lock_configuration = true;
6662
+ `);
6663
+ }
6664
+ async function loadTable(connection, table) {
6665
+ const sourceData = stringifyJson(table.data, `${table.name}.data`);
6666
+ let sourceSchema;
6667
+ if (table.schema) {
6668
+ assertDuckDbSchema(table.schema, `${table.name}.schema`);
6669
+ sourceSchema = stringifyJson([table.schema], `${table.name}.schema`);
6670
+ } else {
6671
+ if (table.data.length === 0) {
6672
+ throw new Error(`${table.name}.schema is required when data is empty`);
6673
+ }
6674
+ const detected = await connection.runAndReadAll(
6675
+ "SELECT json_structure($sourceData) AS schema",
6676
+ { sourceData }
6677
+ );
6678
+ const value = detected.getRowsJson()[0]?.[0];
6679
+ if (typeof value !== "string") throw new Error(`Could not infer a schema for ${table.name}`);
6680
+ let detectedSchema;
6681
+ try {
6682
+ detectedSchema = JSON.parse(value);
6683
+ } catch {
6684
+ throw new Error(`Could not parse the inferred schema for ${table.name}`);
6685
+ }
6686
+ assertDuckDbSchema(detectedSchema, `${table.name}.inferredSchema`);
6687
+ sourceSchema = value;
6688
+ }
6689
+ await connection.run(
6690
+ `CREATE TABLE ${quoteIdentifier(table.name)} AS
6691
+ SELECT UNNEST(JSON_TRANSFORM($sourceData, $sourceSchema), recursive := true)`,
6692
+ { sourceData, sourceSchema }
6693
+ );
6694
+ }
6695
+ function readTables(value) {
6696
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_TABLES) {
6697
+ throw new Error(`tables must contain 1 through ${MAX_TABLES} entries`);
6698
+ }
6699
+ assertInputSize(value);
6700
+ let totalRows = 0;
6701
+ const tables = value.map((entry, tableIndex) => {
6702
+ if (!isPlainRecord(entry)) throw new Error(`tables[${tableIndex}] must be an object`);
6703
+ const { name, data: data4, schema } = entry;
6704
+ if (typeof name !== "string" || !TABLE_NAME.test(name)) {
6705
+ throw new Error(`tables[${tableIndex}].name must be a safe SQL identifier`);
6706
+ }
6707
+ if (!Array.isArray(data4) || data4.length > MAX_ROWS) {
6708
+ throw new Error(`tables[${tableIndex}].data must contain at most ${MAX_ROWS} rows`);
6709
+ }
6710
+ const records = data4.map((record, rowIndex) => {
6711
+ if (!isPlainRecord(record)) throw new Error(`tables[${tableIndex}].data[${rowIndex}] must be an object`);
6712
+ if (Object.keys(record).length > MAX_COLUMNS2) {
6713
+ throw new Error(`tables[${tableIndex}].data[${rowIndex}] exceeds ${MAX_COLUMNS2} columns`);
6714
+ }
6715
+ return record;
6716
+ });
6717
+ const columns = new Set(records.flatMap((record) => Object.keys(record)));
6718
+ if (columns.size > MAX_COLUMNS2) {
6719
+ throw new Error(`tables[${tableIndex}].data exceeds ${MAX_COLUMNS2} distinct columns`);
6720
+ }
6721
+ totalRows += records.length;
6722
+ if (totalRows > MAX_ROWS) throw new Error(`tables exceed the ${MAX_ROWS}-row total limit`);
6723
+ if (schema !== void 0 && (!isPlainRecord(schema) || Object.keys(schema).length > MAX_COLUMNS2)) {
6724
+ throw new Error(`tables[${tableIndex}].schema must contain at most ${MAX_COLUMNS2} fields`);
6725
+ }
6726
+ return { name, data: records, schema };
6727
+ });
6728
+ if (new Set(tables.map((table) => table.name.toLowerCase())).size !== tables.length) {
6729
+ throw new Error("table names must be unique ignoring case");
6730
+ }
6731
+ return tables;
6732
+ }
6733
+ function readQuery(value) {
6734
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error("query is required");
6735
+ if (Buffer.byteLength(value, "utf8") > MAX_QUERY_BYTES) {
6736
+ throw new Error(`query exceeds the ${MAX_QUERY_BYTES}-byte limit`);
6737
+ }
6738
+ return value.trim().replace(/;\s*$/, "");
6739
+ }
6740
+ function readArguments(value) {
6741
+ if (value === void 0 || value === null) return {};
6742
+ if (!Array.isArray(value) || value.length > MAX_ARGUMENTS) {
6743
+ throw new Error(`args must contain at most ${MAX_ARGUMENTS} scalar values`);
6744
+ }
6745
+ return Object.fromEntries(value.map((entry, index) => {
6746
+ if (entry !== null && typeof entry !== "string" && typeof entry !== "number" && typeof entry !== "boolean") {
6747
+ throw new Error(`args[${index}] must be a string, number, boolean, or null`);
6748
+ }
6749
+ if (typeof entry === "number" && !Number.isFinite(entry)) throw new Error(`args[${index}] must be finite`);
6750
+ return [String(index + 1), entry];
6751
+ }));
6752
+ }
6753
+ function assertInputSize(value) {
6754
+ const serialized = stringifyJson(value, "tables");
6755
+ if (Buffer.byteLength(serialized, "utf8") > MAX_INPUT_BYTES) {
6756
+ throw new Error(`tables exceed the ${MAX_INPUT_BYTES}-byte input limit`);
6757
+ }
6758
+ }
6759
+ function assertResultSize(rows) {
6760
+ const serialized = stringifyJson(rows, "DuckDB result");
6761
+ if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES) {
6762
+ throw new Error(`DuckDB result exceeds the ${MAX_RESULT_BYTES}-byte output limit`);
6763
+ }
6764
+ }
6765
+ function assertDuckDbSchema(value, label) {
6766
+ let leaves = 0;
6767
+ const visit = (entry, depth) => {
6768
+ if (depth > MAX_SCHEMA_DEPTH) throw new Error(`${label} exceeds ${MAX_SCHEMA_DEPTH} nested levels`);
6769
+ if (typeof entry === "string") {
6770
+ leaves += 1;
6771
+ if (leaves > MAX_COLUMNS2) throw new Error(`${label} exceeds ${MAX_COLUMNS2} leaf columns`);
6772
+ return;
6773
+ }
6774
+ if (Array.isArray(entry)) {
6775
+ if (entry.length !== 1) throw new Error(`${label} array schemas must contain exactly one element type`);
6776
+ visit(entry[0], depth + 1);
6777
+ return;
6778
+ }
6779
+ if (!isPlainRecord(entry) || Object.keys(entry).length === 0) {
6780
+ throw new Error(`${label} must describe at least one DuckDB column type`);
6781
+ }
6782
+ for (const child of Object.values(entry)) visit(child, depth + 1);
6783
+ };
6784
+ visit(value, 0);
6785
+ }
6786
+ function stringifyJson(value, label) {
6787
+ try {
6788
+ const serialized = JSON.stringify(value);
6789
+ if (serialized === void 0) throw new Error("undefined");
6790
+ return serialized;
6791
+ } catch {
6792
+ throw new Error(`${label} must be JSON-serializable`);
6793
+ }
6794
+ }
6795
+ function quoteIdentifier(value) {
6796
+ return `"${value.replaceAll('"', '""')}"`;
6797
+ }
6798
+ function throwIfTimedOut(timedOut) {
6799
+ if (timedOut) throw new Error(`DuckDB execution exceeded ${MAX_EXECUTION_MS}ms`);
6800
+ }
6801
+
6503
6802
  // src/connectors/adapters/docsbot.ts
6504
6803
  var docsbotConnector = declarativeRestConnector({
6505
6804
  kind: "docsbot",
@@ -6917,7 +7216,7 @@ var elevenlabsConnector = declarativeRestConnector({
6917
7216
  // src/connectors/adapters/excel-files.ts
6918
7217
  import readXlsxFile from "read-excel-file/node";
6919
7218
  import writeXlsxFile from "write-excel-file/node";
6920
- var MAX_COLUMNS2 = 256;
7219
+ var MAX_COLUMNS3 = 256;
6921
7220
  var MAX_SHEETS = 50;
6922
7221
  var MAX_UNCOMPRESSED_WORKBOOK_BYTES = 50 * 1024 * 1024;
6923
7222
  var MAX_ZIP_ENTRIES = 1e3;
@@ -6958,7 +7257,7 @@ var excelFilesConnector = {
6958
7257
  type: "array",
6959
7258
  minItems: 1,
6960
7259
  maxItems: MAX_RECORDS,
6961
- items: { type: "array", maxItems: MAX_COLUMNS2 }
7260
+ items: { type: "array", maxItems: MAX_COLUMNS3 }
6962
7261
  },
6963
7262
  sheetName: { type: "string", minLength: 1, maxLength: 31, default: "Sheet1" }
6964
7263
  },
@@ -6988,8 +7287,8 @@ var excelFilesConnector = {
6988
7287
  totalRows += sheet.data.length;
6989
7288
  if (totalRows > MAX_RECORDS) throw new Error(`workbook exceeds the ${MAX_RECORDS}-row limit`);
6990
7289
  for (const [rowIndex, row] of sheet.data.entries()) {
6991
- if (row.length > MAX_COLUMNS2) {
6992
- throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${MAX_COLUMNS2}-column limit`);
7290
+ if (row.length > MAX_COLUMNS3) {
7291
+ throw new Error(`sheet ${sheet.sheet} row ${rowIndex + 1} exceeds the ${MAX_COLUMNS3}-column limit`);
6993
7292
  }
6994
7293
  }
6995
7294
  return { name: sheet.sheet, rows: jsonSafe(sheet.data) };
@@ -7003,7 +7302,7 @@ var excelFilesConnector = {
7003
7302
  if (capabilityName !== "excel.create") throw new Error(`Unknown Excel capability: ${capabilityName}`);
7004
7303
  const rows = readRows(args.rows);
7005
7304
  for (const [index, row] of rows.entries()) {
7006
- if (row.length > MAX_COLUMNS2) throw new Error(`rows[${index}] exceeds the ${MAX_COLUMNS2}-column limit`);
7305
+ if (row.length > MAX_COLUMNS3) throw new Error(`rows[${index}] exceeds the ${MAX_COLUMNS3}-column limit`);
7007
7306
  }
7008
7307
  const sheetName = readOptionalString(args.sheetName, "sheetName") ?? "Sheet1";
7009
7308
  validateSheetName(sheetName);
@@ -16301,16 +16600,27 @@ var clickupConnector = declarativeRestConnector({
16301
16600
  displayName: "ClickUp",
16302
16601
  description: "Read and mutate ClickUp workspaces, spaces, folders, lists, tasks, comments, and time entries via the ClickUp REST v2 API.",
16303
16602
  auth: {
16304
- kind: "oauth2",
16305
- authorizationUrl: "https://app.clickup.com/api",
16306
- tokenUrl: "https://api.clickup.com/api/v2/oauth/token",
16307
- scopes: [],
16308
- clientIdEnv: "CLICKUP_OAUTH_CLIENT_ID",
16309
- clientSecretEnv: "CLICKUP_OAUTH_CLIENT_SECRET"
16603
+ kind: "one_of",
16604
+ preferred: "api-key",
16605
+ options: [
16606
+ {
16607
+ kind: "api-key",
16608
+ hint: "ClickUp personal API token (starts with pk_)."
16609
+ },
16610
+ {
16611
+ kind: "oauth2",
16612
+ authorizationUrl: "https://app.clickup.com/api",
16613
+ tokenUrl: "https://api.clickup.com/api/v2/oauth/token",
16614
+ scopes: [],
16615
+ clientIdEnv: "CLICKUP_OAUTH_CLIENT_ID",
16616
+ clientSecretEnv: "CLICKUP_OAUTH_CLIENT_SECRET"
16617
+ }
16618
+ ]
16310
16619
  },
16311
16620
  category: "other",
16312
16621
  defaultConsistencyModel: "authoritative",
16313
16622
  baseUrl: "https://api.clickup.com/api/v2",
16623
+ credentialPlacement: { kind: "authorization-by-auth" },
16314
16624
  // GET /user — returns the authorized user; used as a cheap liveness probe.
16315
16625
  test: { method: "GET", path: "/user" },
16316
16626
  capabilities: [
@@ -43883,7 +44193,7 @@ var pipedriveConnector = declarativeRestConnector({
43883
44193
  // src/connectors/adapters/parquet-files.ts
43884
44194
  import { parquetReadObjects } from "hyparquet";
43885
44195
  import { parquetWriteBuffer } from "hyparquet-writer";
43886
- var MAX_COLUMNS3 = 256;
44196
+ var MAX_COLUMNS4 = 256;
43887
44197
  var parquetFilesConnector = {
43888
44198
  manifest: {
43889
44199
  kind: "parquet-files",
@@ -43901,7 +44211,7 @@ var parquetFilesConnector = {
43901
44211
  type: "object",
43902
44212
  properties: {
43903
44213
  fileBase64: { type: "string", description: "Parquet file content encoded as canonical base64." },
43904
- columns: { type: "array", maxItems: MAX_COLUMNS3, items: { type: "string", minLength: 1 } },
44214
+ columns: { type: "array", maxItems: MAX_COLUMNS4, items: { type: "string", minLength: 1 } },
43905
44215
  rowStart: { type: "integer", minimum: 0, default: 0 },
43906
44216
  rowLimit: { type: "integer", minimum: 1, maximum: MAX_RECORDS, default: MAX_RECORDS }
43907
44217
  },
@@ -43947,8 +44257,8 @@ var parquetFilesConnector = {
43947
44257
  if (capabilityName !== "parquet.create") throw new Error(`Unknown Parquet capability: ${capabilityName}`);
43948
44258
  const records = readRecords(args.records);
43949
44259
  const columnNames = [...new Set(records.flatMap((record) => Object.keys(record)))];
43950
- if (columnNames.length === 0 || columnNames.length > MAX_COLUMNS3) {
43951
- throw new Error(`records must contain 1 through ${MAX_COLUMNS3} columns`);
44260
+ if (columnNames.length === 0 || columnNames.length > MAX_COLUMNS4) {
44261
+ throw new Error(`records must contain 1 through ${MAX_COLUMNS4} columns`);
43952
44262
  }
43953
44263
  if (columnNames.some((name) => name.length === 0)) throw new Error("column names must not be empty");
43954
44264
  const columnData = columnNames.map((name) => {
@@ -43978,8 +44288,8 @@ function asyncBuffer(file) {
43978
44288
  }
43979
44289
  function readColumns2(value) {
43980
44290
  if (value === void 0 || value === null) return void 0;
43981
- if (!Array.isArray(value) || value.length === 0 || value.length > MAX_COLUMNS3) {
43982
- throw new Error(`columns must contain 1 through ${MAX_COLUMNS3} names`);
44291
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_COLUMNS4) {
44292
+ throw new Error(`columns must contain 1 through ${MAX_COLUMNS4} names`);
43983
44293
  }
43984
44294
  const columns = value.map((entry, index) => {
43985
44295
  if (typeof entry !== "string" || entry.length === 0) throw new Error(`columns[${index}] must be a non-empty string`);
@@ -44435,6 +44745,759 @@ var quickbooksConnector = declarativeRestConnector({
44435
44745
  ]
44436
44746
  });
44437
44747
 
44748
+ // src/connectors/adapters/rabbitmq.ts
44749
+ import { createHash as createHash7 } from "crypto";
44750
+ import { isIP as isIP3 } from "net";
44751
+ import {
44752
+ connect as amqpConnect
44753
+ } from "amqplib";
44754
+ var MAX_MESSAGE_BYTES3 = 1024 * 1024;
44755
+ var MAX_NAME_BYTES = 255;
44756
+ var MAX_PEM_BYTES = 64 * 1024;
44757
+ var MAX_CA_CERTIFICATES = 16;
44758
+ var MAX_CA_BYTES = 256 * 1024;
44759
+ function createRabbitMqConnector(options = {}) {
44760
+ const connect = options.connect ?? ((connectOptions, socketOptions) => amqpConnect(connectOptions, socketOptions));
44761
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
44762
+ return {
44763
+ manifest: {
44764
+ kind: "rabbitmq",
44765
+ displayName: "RabbitMQ",
44766
+ description: "Inspect queues and exchanges and publish confirmed JSON messages over verified TLS.",
44767
+ auth: {
44768
+ kind: "api-key",
44769
+ hint: "JSON with a public RabbitMQ host, username, password, virtual host, and optional TLS client credentials."
44770
+ },
44771
+ defaultConsistencyModel: "advisory",
44772
+ category: "webhook",
44773
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
44774
+ capabilities: [
44775
+ readCapability6("rabbitmq.queues.inspect", "Check a queue and return its current message and consumer counts.", {
44776
+ type: "object",
44777
+ properties: { queue: nameSchema("queue") },
44778
+ required: ["queue"],
44779
+ additionalProperties: false
44780
+ }),
44781
+ readCapability6("rabbitmq.exchanges.inspect", "Check whether a named exchange exists and is visible to the connection.", {
44782
+ type: "object",
44783
+ properties: { exchange: nameSchema("exchange") },
44784
+ required: ["exchange"],
44785
+ additionalProperties: false
44786
+ }),
44787
+ mutationCapability6("send.message.to.queue", "Publish a persistent JSON message directly to an existing queue with broker confirmation.", {
44788
+ type: "object",
44789
+ properties: {
44790
+ queue: nameSchema("queue"),
44791
+ data: {},
44792
+ persistent: { type: "boolean", default: true },
44793
+ expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
44794
+ priority: { type: "integer", minimum: 0, maximum: 255 }
44795
+ },
44796
+ required: ["queue", "data"],
44797
+ additionalProperties: false
44798
+ }),
44799
+ mutationCapability6("send.message.to.exchange", "Publish a persistent JSON message to an existing exchange with broker confirmation.", {
44800
+ type: "object",
44801
+ properties: {
44802
+ exchange: nameSchema("exchange"),
44803
+ routingKey: { type: "string", maxLength: MAX_NAME_BYTES, default: "" },
44804
+ data: {},
44805
+ persistent: { type: "boolean", default: true },
44806
+ expirationMs: { type: "integer", minimum: 1, maximum: 6048e5 },
44807
+ priority: { type: "integer", minimum: 0, maximum: 255 }
44808
+ },
44809
+ required: ["exchange", "data"],
44810
+ additionalProperties: false
44811
+ })
44812
+ ]
44813
+ },
44814
+ async executeRead({ source, capabilityName, args }) {
44815
+ if (capabilityName === "rabbitmq.queues.inspect") {
44816
+ const queue = readName(args.queue, "queue");
44817
+ const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
44818
+ const details = await channel.checkQueue(queue);
44819
+ return { queue: details.queue, messageCount: details.messageCount, consumerCount: details.consumerCount };
44820
+ });
44821
+ return { data: data4, fetchedAt: Date.now() };
44822
+ }
44823
+ if (capabilityName === "rabbitmq.exchanges.inspect") {
44824
+ const exchange = readName(args.exchange, "exchange");
44825
+ const data4 = await withChannel(source, connect, resolveHost, async (channel) => {
44826
+ await channel.checkExchange(exchange);
44827
+ return { exchange, exists: true };
44828
+ });
44829
+ return { data: data4, fetchedAt: Date.now() };
44830
+ }
44831
+ throw new Error(`Unknown RabbitMQ read capability: ${capabilityName}`);
44832
+ },
44833
+ async executeMutation({ source, capabilityName, args, idempotencyKey }) {
44834
+ let data4;
44835
+ if (capabilityName === "send.message.to.queue") {
44836
+ const queue = readName(args.queue, "queue");
44837
+ const body = readJsonMessage(args.data);
44838
+ const publishOptions = readPublishOptions(args, idempotencyKey);
44839
+ data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
44840
+ await channel.checkQueue(queue);
44841
+ await publishConfirmed(channel, publishOptions, (options2) => channel.sendToQueue(queue, body, options2));
44842
+ return { queue, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
44843
+ });
44844
+ } else if (capabilityName === "send.message.to.exchange") {
44845
+ const exchange = readName(args.exchange, "exchange");
44846
+ const routingKey = readRoutingKey(args.routingKey);
44847
+ const body = readJsonMessage(args.data);
44848
+ const publishOptions = readPublishOptions(args, idempotencyKey);
44849
+ data4 = await withConfirmChannel(source, connect, resolveHost, async (channel) => {
44850
+ await channel.checkExchange(exchange);
44851
+ await publishConfirmed(channel, publishOptions, (options2) => channel.publish(exchange, routingKey, body, options2));
44852
+ return { exchange, routingKey, confirmed: true, messageId: publishOptions.messageId, byteLength: body.byteLength };
44853
+ });
44854
+ } else {
44855
+ throw new Error(`Unknown RabbitMQ mutation capability: ${capabilityName}`);
44856
+ }
44857
+ return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
44858
+ },
44859
+ async test(source) {
44860
+ try {
44861
+ const credentials = readCredentials2(source);
44862
+ await resolveHost(credentials.host);
44863
+ const model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44864
+ await model2.close();
44865
+ return { ok: true };
44866
+ } catch (error) {
44867
+ return { ok: false, reason: safeErrorMessage(error, credentialSecrets(source)) };
44868
+ }
44869
+ }
44870
+ };
44871
+ }
44872
+ var rabbitMqConnector = createRabbitMqConnector();
44873
+ async function withChannel(source, connect, resolveHost, run) {
44874
+ const credentials = readCredentials2(source);
44875
+ await resolveHost(credentials.host);
44876
+ let model2;
44877
+ let channel;
44878
+ try {
44879
+ model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44880
+ channel = await model2.createChannel();
44881
+ return await run(channel);
44882
+ } catch (error) {
44883
+ throw new Error(safeErrorMessage(error, credentials.secrets));
44884
+ } finally {
44885
+ await channel?.close().catch(() => void 0);
44886
+ await model2?.close().catch(() => void 0);
44887
+ }
44888
+ }
44889
+ async function withConfirmChannel(source, connect, resolveHost, run) {
44890
+ const credentials = readCredentials2(source);
44891
+ await resolveHost(credentials.host);
44892
+ let model2;
44893
+ let channel;
44894
+ try {
44895
+ model2 = await connect(credentials.connectOptions, credentials.socketOptions);
44896
+ channel = await model2.createConfirmChannel();
44897
+ return await run(channel);
44898
+ } catch (error) {
44899
+ throw new Error(safeErrorMessage(error, credentials.secrets));
44900
+ } finally {
44901
+ await channel?.close().catch(() => void 0);
44902
+ await model2?.close().catch(() => void 0);
44903
+ }
44904
+ }
44905
+ async function publishConfirmed(channel, publishOptions, publish) {
44906
+ let returned;
44907
+ const onReturn = (message) => {
44908
+ returned = message;
44909
+ };
44910
+ channel.on("return", onReturn);
44911
+ try {
44912
+ publish(publishOptions);
44913
+ await channel.waitForConfirms();
44914
+ if (returned) throw new Error("RabbitMQ accepted but could not route the message");
44915
+ } finally {
44916
+ channel.removeListener("return", onReturn);
44917
+ }
44918
+ }
44919
+ function readCredentials2(source) {
44920
+ let raw;
44921
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
44922
+ else if (source.credentials.kind === "api-key") {
44923
+ try {
44924
+ raw = JSON.parse(source.credentials.apiKey);
44925
+ } catch {
44926
+ throw new Error("RabbitMQ credential must be valid JSON");
44927
+ }
44928
+ } else {
44929
+ throw new Error("RabbitMQ requires a structured credential bundle");
44930
+ }
44931
+ if (!isPlainRecord(raw)) throw new Error("RabbitMQ credential must be a JSON object");
44932
+ const host = readHost(raw.host);
44933
+ const port = readBoundedInteger(raw.port, 5671, 1, 65535, "port");
44934
+ const username = readRequiredString(raw.username, "username", 1024);
44935
+ const password = readRequiredString(raw.password, "password", 4096);
44936
+ const vhost = readOptionalString(raw.vhost, "vhost") ?? "/";
44937
+ if (Buffer.byteLength(vhost, "utf8") > MAX_NAME_BYTES || /[\u0000-\u001f\u007f]/.test(vhost)) {
44938
+ throw new Error(`vhost must be at most ${MAX_NAME_BYTES} bytes without control characters`);
44939
+ }
44940
+ const ca = readPem(raw.tlsCa, "tlsCa", true);
44941
+ const cert = readPem(raw.tlsCert, "tlsCert", false);
44942
+ const key = readPem(raw.tlsKey, "tlsKey", false);
44943
+ if (Boolean(cert) !== Boolean(key)) throw new Error("tlsCert and tlsKey must be supplied together");
44944
+ const passphrase = readOptionalString(raw.tlsPassphrase, "tlsPassphrase");
44945
+ return {
44946
+ host,
44947
+ connectOptions: {
44948
+ protocol: "amqps",
44949
+ hostname: host,
44950
+ port,
44951
+ username,
44952
+ password,
44953
+ vhost,
44954
+ heartbeat: readBoundedInteger(raw.heartbeatSeconds, 30, 5, 300, "heartbeatSeconds"),
44955
+ frameMax: MAX_MESSAGE_BYTES3 + 4096
44956
+ },
44957
+ socketOptions: {
44958
+ minVersion: "TLSv1.2",
44959
+ rejectUnauthorized: true,
44960
+ servername: isIP3(host) === 0 ? host : void 0,
44961
+ lookup: publicDnsLookup,
44962
+ timeout: readBoundedInteger(raw.connectionTimeoutMs, 1e4, 1e3, 3e4, "connectionTimeoutMs"),
44963
+ noDelay: true,
44964
+ keepAlive: true,
44965
+ keepAliveDelay: 6e4,
44966
+ ca,
44967
+ cert,
44968
+ key,
44969
+ passphrase
44970
+ },
44971
+ secrets: [password, ...cert ? [cert] : [], ...key ? [key] : [], ...passphrase ? [passphrase] : []]
44972
+ };
44973
+ }
44974
+ function readHost(value) {
44975
+ const host = readRequiredString(value, "host", 253);
44976
+ const ipVersion = isIP3(host);
44977
+ if (ipVersion !== 0) {
44978
+ if (!isPublicNetworkAddress(host)) throw new Error("RabbitMQ host is not a public network target");
44979
+ return host;
44980
+ }
44981
+ const normalized = host.toLowerCase();
44982
+ if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
44983
+ throw new Error("RabbitMQ host must be a public hostname or IP address without a scheme or port");
44984
+ }
44985
+ return host;
44986
+ }
44987
+ function readPublishOptions(args, idempotencyKey) {
44988
+ const expirationMs = args.expirationMs === void 0 ? void 0 : readBoundedInteger(args.expirationMs, 0, 1, 6048e5, "expirationMs");
44989
+ const priority = args.priority === void 0 ? void 0 : readBoundedInteger(args.priority, 0, 0, 255, "priority");
44990
+ return {
44991
+ mandatory: true,
44992
+ persistent: readBoolean(args.persistent, true, "persistent"),
44993
+ contentType: "application/json",
44994
+ contentEncoding: "utf-8",
44995
+ messageId: createHash7("sha256").update(idempotencyKey).digest("hex"),
44996
+ timestamp: Math.floor(Date.now() / 1e3),
44997
+ appId: "tangle-integration-hub",
44998
+ expiration: expirationMs === void 0 ? void 0 : String(expirationMs),
44999
+ priority
45000
+ };
45001
+ }
45002
+ function readJsonMessage(value) {
45003
+ if (value === void 0) throw new Error("data is required");
45004
+ let serialized;
45005
+ try {
45006
+ serialized = JSON.stringify(value);
45007
+ } catch {
45008
+ throw new Error("data must be JSON-serializable");
45009
+ }
45010
+ if (serialized === void 0) throw new Error("data must be JSON-serializable");
45011
+ const body = Buffer.from(serialized);
45012
+ if (body.byteLength > MAX_MESSAGE_BYTES3) throw new Error(`data exceeds the ${MAX_MESSAGE_BYTES3}-byte limit`);
45013
+ return body;
45014
+ }
45015
+ function readName(value, label) {
45016
+ const name = readRequiredString(value, label, MAX_NAME_BYTES);
45017
+ if (Buffer.byteLength(name, "utf8") > MAX_NAME_BYTES || /[\u0000-\u001f\u007f]/.test(name)) {
45018
+ throw new Error(`${label} must be at most ${MAX_NAME_BYTES} bytes without control characters`);
45019
+ }
45020
+ return name;
45021
+ }
45022
+ function readRoutingKey(value) {
45023
+ if (value === void 0 || value === null || value === "") return "";
45024
+ return readName(value, "routingKey");
45025
+ }
45026
+ function readRequiredString(value, label, maxLength) {
45027
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
45028
+ throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
45029
+ }
45030
+ return value;
45031
+ }
45032
+ function readPem(value, label, allowArray) {
45033
+ if (value === void 0 || value === null) return void 0;
45034
+ const values = Array.isArray(value) && allowArray ? value : [value];
45035
+ if (values.length === 0 || values.length > MAX_CA_CERTIFICATES || values.some((entry) => typeof entry !== "string" || entry.length === 0 || Buffer.byteLength(entry, "utf8") > MAX_PEM_BYTES) || values.reduce((total, entry) => total + (typeof entry === "string" ? Buffer.byteLength(entry, "utf8") : 0), 0) > MAX_CA_BYTES) {
45036
+ throw new Error(
45037
+ allowArray ? `${label} must contain 1-${MAX_CA_CERTIFICATES} PEM strings, each under ${MAX_PEM_BYTES} bytes and ${MAX_CA_BYTES} bytes total` : `${label} must be a PEM string under ${MAX_PEM_BYTES} bytes`
45038
+ );
45039
+ }
45040
+ return allowArray ? values : values[0];
45041
+ }
45042
+ function credentialSecrets(source) {
45043
+ try {
45044
+ return readCredentials2(source).secrets;
45045
+ } catch {
45046
+ return [];
45047
+ }
45048
+ }
45049
+ function safeErrorMessage(error, secrets) {
45050
+ let message = error instanceof Error ? error.message : String(error);
45051
+ for (const secret of secrets) {
45052
+ if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
45053
+ }
45054
+ return message;
45055
+ }
45056
+ function readCapability6(name, description, parameters) {
45057
+ return { name, class: "read", description, parameters };
45058
+ }
45059
+ function mutationCapability6(name, description, parameters) {
45060
+ return { name, class: "mutation", description, parameters, cas: "none", externalEffect: true };
45061
+ }
45062
+ function nameSchema(label) {
45063
+ return { type: "string", minLength: 1, maxLength: MAX_NAME_BYTES, description: `Existing RabbitMQ ${label} name.` };
45064
+ }
45065
+
45066
+ // src/connectors/adapters/redis.ts
45067
+ import { isIP as isIP4 } from "net";
45068
+ import {
45069
+ createClient as createRedisClient
45070
+ } from "redis";
45071
+ var MAX_KEY_BYTES = 1024;
45072
+ var MAX_PATTERN_BYTES = 4096;
45073
+ var MAX_VALUE_BYTES = 1024 * 1024;
45074
+ var MAX_SCAN_KEYS = 1e3;
45075
+ var MAX_SCAN_BYTES = 1024 * 1024;
45076
+ var MAX_TLS_CA_BYTES = 256 * 1024;
45077
+ var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
45078
+ var SCAN_SCRIPT = `
45079
+ local page
45080
+ if ARGV[4] == '' then
45081
+ page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3])
45082
+ else
45083
+ page = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3], 'TYPE', ARGV[4])
45084
+ end
45085
+ local keys = page[2]
45086
+ if #keys > tonumber(ARGV[5]) then return redis.error_reply('Redis SCAN page exceeds the configured key limit') end
45087
+ local bytes = 0
45088
+ for _, key in ipairs(keys) do
45089
+ if string.len(key) > tonumber(ARGV[7]) then return redis.error_reply('Redis SCAN key exceeds the configured key limit') end
45090
+ bytes = bytes + string.len(key)
45091
+ if bytes > tonumber(ARGV[6]) then return redis.error_reply('Redis SCAN page exceeds the configured byte limit') end
45092
+ end
45093
+ return page
45094
+ `;
45095
+ var INSPECT_SCRIPT = `
45096
+ local kind = redis.call('TYPE', KEYS[1])
45097
+ if type(kind) == 'table' then kind = kind['ok'] end
45098
+ return { kind, redis.call('PTTL', KEYS[1]) }
45099
+ `;
45100
+ var READ_STRING_SCRIPT = `
45101
+ local kind = redis.call('TYPE', KEYS[1])
45102
+ if type(kind) == 'table' then kind = kind['ok'] end
45103
+ local ttl = redis.call('PTTL', KEYS[1])
45104
+ if kind == 'none' then return { kind, ttl, false, 0 } end
45105
+ if kind ~= 'string' then return redis.error_reply('WRONGTYPE key does not contain a string') end
45106
+ local length = redis.call('STRLEN', KEYS[1])
45107
+ if length > tonumber(ARGV[1]) then return redis.error_reply('Redis value exceeds the configured read limit') end
45108
+ return { kind, ttl, redis.call('GET', KEYS[1]), length }
45109
+ `;
45110
+ var COMPARE_SET_SCRIPT = `
45111
+ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
45112
+ if ARGV[3] ~= '' then
45113
+ redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3])
45114
+ elseif ARGV[4] == '1' then
45115
+ redis.call('SET', KEYS[1], ARGV[2], 'KEEPTTL')
45116
+ else
45117
+ redis.call('SET', KEYS[1], ARGV[2])
45118
+ end
45119
+ return 1
45120
+ `;
45121
+ var COMPARE_DELETE_SCRIPT = `
45122
+ if redis.call('GET', KEYS[1]) ~= ARGV[1] then return 0 end
45123
+ return redis.call('DEL', KEYS[1])
45124
+ `;
45125
+ function createRedisConnector(options = {}) {
45126
+ const createClient = options.createClient ?? defaultCreateClient;
45127
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
45128
+ return {
45129
+ manifest: {
45130
+ kind: "redis",
45131
+ displayName: "Redis",
45132
+ description: "Inspect and scan Redis keys and perform bounded, approved string compare-and-swap mutations over verified TLS.",
45133
+ auth: {
45134
+ kind: "api-key",
45135
+ hint: "JSON with a public Redis host, password, optional ACL username/database, and optional TLS CA. Verified TLS is mandatory."
45136
+ },
45137
+ defaultConsistencyModel: "authoritative",
45138
+ category: "database",
45139
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
45140
+ capabilities: [
45141
+ readCapability7("redis.keys.scan", "Read one bounded SCAN page without blocking the server with KEYS.", {
45142
+ type: "object",
45143
+ properties: {
45144
+ cursor: { type: "string", pattern: "^[0-9]+$", default: "0" },
45145
+ match: { type: "string", maxLength: MAX_PATTERN_BYTES, default: "*" },
45146
+ count: { type: "integer", minimum: 1, maximum: MAX_SCAN_KEYS, default: 100 },
45147
+ type: { type: "string", enum: ["string", "list", "set", "zset", "hash", "stream"] }
45148
+ },
45149
+ additionalProperties: false
45150
+ }),
45151
+ readCapability7("redis.key.inspect", "Read a key type and remaining expiry atomically.", keySchema()),
45152
+ readCapability7("redis.string.get", "Read one bounded string value, type, and remaining expiry atomically.", keySchema()),
45153
+ mutationCapability7("redis.string.set", "Set a bounded string only when the key is absent, present, or matches an expected value.", {
45154
+ type: "object",
45155
+ properties: {
45156
+ key: keyParameter(),
45157
+ value: { type: "string", maxLength: MAX_VALUE_BYTES },
45158
+ condition: { type: "string", enum: ["absent", "present", "matches"] },
45159
+ expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES },
45160
+ ttlMs: { type: "integer", minimum: 1, maximum: MAX_TTL_MS },
45161
+ keepTtl: { type: "boolean", default: false }
45162
+ },
45163
+ required: ["key", "value", "condition"],
45164
+ additionalProperties: false
45165
+ }),
45166
+ mutationCapability7("redis.string.delete", "Delete a string key only when its current value exactly matches the caller expectation.", {
45167
+ type: "object",
45168
+ properties: {
45169
+ key: keyParameter(),
45170
+ expectedValue: { type: "string", maxLength: MAX_VALUE_BYTES }
45171
+ },
45172
+ required: ["key", "expectedValue"],
45173
+ additionalProperties: false
45174
+ })
45175
+ ]
45176
+ },
45177
+ async test(source) {
45178
+ try {
45179
+ await withClient(source, createClient, resolveHost, async (client) => {
45180
+ const response = await client.ping();
45181
+ if (response !== "PONG") throw new Error("Redis PING returned an unexpected response");
45182
+ });
45183
+ return { ok: true };
45184
+ } catch (error) {
45185
+ return { ok: false, reason: safeErrorMessage2(error, credentialSecrets2(source)) };
45186
+ }
45187
+ },
45188
+ async executeRead({ source, capabilityName, args }) {
45189
+ if (capabilityName === "redis.keys.scan") {
45190
+ const cursor = readCursor(args.cursor);
45191
+ const match = readPattern(args.match);
45192
+ const count = readBoundedInteger(args.count, 100, 1, MAX_SCAN_KEYS, "count");
45193
+ const type = readScanType(args.type);
45194
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45195
+ const reply = readArrayReply(await client.eval(SCAN_SCRIPT, {
45196
+ keys: [],
45197
+ arguments: [
45198
+ cursor,
45199
+ match,
45200
+ String(count),
45201
+ type ?? "",
45202
+ String(MAX_SCAN_KEYS),
45203
+ String(MAX_SCAN_BYTES),
45204
+ String(MAX_KEY_BYTES)
45205
+ ]
45206
+ }), "scan");
45207
+ const keys = readStringArray(reply[1], "scan keys");
45208
+ assertScanPage(keys);
45209
+ return { cursor: readCursor(readReplyString(reply[0], "cursor")), keys };
45210
+ });
45211
+ return { data: data4, fetchedAt: Date.now() };
45212
+ }
45213
+ if (capabilityName === "redis.key.inspect") {
45214
+ const key = readKey(args.key);
45215
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45216
+ const reply = readArrayReply(await client.eval(INSPECT_SCRIPT, { keys: [key], arguments: [] }), "inspect");
45217
+ const type = readReplyString(reply[0], "type");
45218
+ return { key, exists: type !== "none", type, ttlMs: normalizeTtl(reply[1]) };
45219
+ });
45220
+ return { data: data4, fetchedAt: Date.now() };
45221
+ }
45222
+ if (capabilityName === "redis.string.get") {
45223
+ const key = readKey(args.key);
45224
+ const data4 = await withClient(source, createClient, resolveHost, async (client) => {
45225
+ const reply = readArrayReply(await client.eval(READ_STRING_SCRIPT, {
45226
+ keys: [key],
45227
+ arguments: [String(MAX_VALUE_BYTES)]
45228
+ }), "string read");
45229
+ const type = readReplyString(reply[0], "type");
45230
+ const exists = type !== "none";
45231
+ const value = exists ? readReplyString(reply[2], "value") : null;
45232
+ if (value !== null && Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
45233
+ throw new Error(`Redis value exceeds the ${MAX_VALUE_BYTES}-byte limit`);
45234
+ }
45235
+ return {
45236
+ key,
45237
+ exists,
45238
+ type,
45239
+ ttlMs: normalizeTtl(reply[1]),
45240
+ value,
45241
+ byteLength: exists ? readReplyInteger(reply[3], "byteLength") : 0
45242
+ };
45243
+ });
45244
+ return { data: data4, fetchedAt: Date.now() };
45245
+ }
45246
+ throw new Error(`Unknown Redis read capability: ${capabilityName}`);
45247
+ },
45248
+ async executeMutation({ source, capabilityName, args }) {
45249
+ if (capabilityName === "redis.string.set") {
45250
+ const input = readSetInput(args);
45251
+ return withClient(source, createClient, resolveHost, async (client) => {
45252
+ let committed;
45253
+ if (input.condition === "matches") {
45254
+ committed = readReplyInteger(await client.eval(COMPARE_SET_SCRIPT, {
45255
+ keys: [input.key],
45256
+ arguments: [input.expectedValue, input.value, input.ttlMs === void 0 ? "" : String(input.ttlMs), input.keepTtl ? "1" : "0"]
45257
+ }), "compare-and-set result") === 1;
45258
+ } else {
45259
+ const response = await client.set(input.key, input.value, {
45260
+ condition: input.condition === "absent" ? "NX" : "XX",
45261
+ expiration: input.ttlMs === void 0 ? input.keepTtl ? "KEEPTTL" : void 0 : { type: "PX", value: input.ttlMs }
45262
+ });
45263
+ committed = response === "OK";
45264
+ }
45265
+ if (!committed) return conflict(`Redis key ${input.key} did not satisfy condition ${input.condition}`);
45266
+ return committedResult({ key: input.key, condition: input.condition, byteLength: Buffer.byteLength(input.value, "utf8") });
45267
+ });
45268
+ }
45269
+ if (capabilityName === "redis.string.delete") {
45270
+ const key = readKey(args.key);
45271
+ const expectedValue = readValue(args.expectedValue, "expectedValue");
45272
+ return withClient(source, createClient, resolveHost, async (client) => {
45273
+ const deleted = readReplyInteger(await client.eval(COMPARE_DELETE_SCRIPT, {
45274
+ keys: [key],
45275
+ arguments: [expectedValue]
45276
+ }), "compare-and-delete result");
45277
+ if (deleted !== 1) return conflict(`Redis key ${key} did not match the expected value`);
45278
+ return committedResult({ key, deleted: true });
45279
+ });
45280
+ }
45281
+ throw new Error(`Unknown Redis mutation capability: ${capabilityName}`);
45282
+ }
45283
+ };
45284
+ }
45285
+ var redisConnector = createRedisConnector();
45286
+ function defaultCreateClient(options) {
45287
+ return createRedisClient(options);
45288
+ }
45289
+ async function withClient(source, createClient, resolveHost, run) {
45290
+ const credentials = readCredentials3(source);
45291
+ const addresses = await resolveHost(credentials.host);
45292
+ if (addresses.length === 0) throw new Error("Redis host did not resolve");
45293
+ let client;
45294
+ try {
45295
+ client = createClient({
45296
+ username: credentials.username,
45297
+ password: credentials.password,
45298
+ database: credentials.database,
45299
+ name: "tangle-integration-hub",
45300
+ disableOfflineQueue: true,
45301
+ commandsQueueMaxLength: 10,
45302
+ maintNotifications: "disabled",
45303
+ commandOptions: { abortSignal: AbortSignal.timeout(credentials.commandTimeoutMs) },
45304
+ socket: {
45305
+ host: addresses[0],
45306
+ port: credentials.port,
45307
+ tls: true,
45308
+ servername: isIP4(credentials.host) === 0 ? credentials.host : void 0,
45309
+ minVersion: "TLSv1.2",
45310
+ rejectUnauthorized: true,
45311
+ ca: credentials.tlsCa,
45312
+ connectTimeout: credentials.connectTimeoutMs,
45313
+ socketTimeout: credentials.commandTimeoutMs,
45314
+ reconnectStrategy: false
45315
+ }
45316
+ });
45317
+ client.on("error", () => void 0);
45318
+ await client.connect();
45319
+ return await run(client);
45320
+ } catch (error) {
45321
+ throw new Error(safeErrorMessage2(error, credentials.secrets));
45322
+ } finally {
45323
+ if (client?.isOpen) await client.close().catch(() => client?.destroy());
45324
+ else client?.destroy();
45325
+ }
45326
+ }
45327
+ function readCredentials3(source) {
45328
+ let raw;
45329
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
45330
+ else if (source.credentials.kind === "api-key") {
45331
+ try {
45332
+ raw = JSON.parse(source.credentials.apiKey);
45333
+ } catch {
45334
+ throw new Error("Redis credential must be valid JSON");
45335
+ }
45336
+ } else {
45337
+ throw new Error("Redis requires a structured credential bundle");
45338
+ }
45339
+ if (!isPlainRecord(raw)) throw new Error("Redis credential must be a JSON object");
45340
+ const host = readHost2(raw.host);
45341
+ const username = readOptionalString(raw.username, "username");
45342
+ if (username && (username.length > 128 || /[\u0000-\u001f\u007f]/.test(username))) {
45343
+ throw new Error("Redis username must be at most 128 characters without control characters");
45344
+ }
45345
+ const password = readRequiredString2(raw.password, "password", 4096);
45346
+ const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
45347
+ if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > MAX_TLS_CA_BYTES) {
45348
+ throw new Error(`Redis tlsCa exceeds the ${MAX_TLS_CA_BYTES}-byte limit`);
45349
+ }
45350
+ return {
45351
+ host,
45352
+ port: readBoundedInteger(raw.port, 6380, 1, 65535, "port"),
45353
+ username,
45354
+ password,
45355
+ database: readBoundedInteger(raw.database, 0, 0, 65535, "database"),
45356
+ tlsCa,
45357
+ connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
45358
+ commandTimeoutMs: readBoundedInteger(raw.commandTimeoutMs, 1e4, 1e3, 3e4, "commandTimeoutMs"),
45359
+ secrets: [password]
45360
+ };
45361
+ }
45362
+ function readHost2(value) {
45363
+ const host = readRequiredString2(value, "host", 253);
45364
+ const ipVersion = isIP4(host);
45365
+ if (ipVersion !== 0) {
45366
+ if (!isPublicNetworkAddress(host)) throw new Error("Redis host is not a public network target");
45367
+ return host;
45368
+ }
45369
+ const normalized = host.toLowerCase();
45370
+ if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || /[\s/@:\[\]]/.test(host)) {
45371
+ throw new Error("Redis host must be a public hostname or IP address without a scheme or port");
45372
+ }
45373
+ return host;
45374
+ }
45375
+ function readSetInput(args) {
45376
+ const key = readKey(args.key);
45377
+ const value = readValue(args.value, "value");
45378
+ const condition = args.condition;
45379
+ if (!["absent", "present", "matches"].includes(String(condition))) {
45380
+ throw new Error("condition must be absent, present, or matches");
45381
+ }
45382
+ const expectedValue = args.expectedValue === void 0 ? void 0 : readValue(args.expectedValue, "expectedValue");
45383
+ if (condition === "matches" && expectedValue === void 0) throw new Error("expectedValue is required when condition is matches");
45384
+ if (condition !== "matches" && expectedValue !== void 0) throw new Error("expectedValue is only valid when condition is matches");
45385
+ const ttlMs = args.ttlMs === void 0 ? void 0 : readBoundedInteger(args.ttlMs, 0, 1, MAX_TTL_MS, "ttlMs");
45386
+ const keepTtl = readBoolean(args.keepTtl, false, "keepTtl");
45387
+ if (ttlMs !== void 0 && keepTtl) throw new Error("ttlMs and keepTtl cannot be used together");
45388
+ return { key, value, condition, expectedValue, ttlMs, keepTtl };
45389
+ }
45390
+ function readCursor(value) {
45391
+ if (value === void 0 || value === null || value === "") return "0";
45392
+ if (typeof value !== "string" || !/^[0-9]+$/.test(value) || value.length > 32) {
45393
+ throw new Error("cursor must be a Redis numeric cursor string");
45394
+ }
45395
+ return value;
45396
+ }
45397
+ function readPattern(value) {
45398
+ if (value === void 0 || value === null || value === "") return "*";
45399
+ if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > MAX_PATTERN_BYTES || /[\u0000-\u001f\u007f]/.test(value)) {
45400
+ throw new Error(`match must be under ${MAX_PATTERN_BYTES} bytes without control characters`);
45401
+ }
45402
+ return value;
45403
+ }
45404
+ function readScanType(value) {
45405
+ if (value === void 0 || value === null || value === "") return void 0;
45406
+ if (!["string", "list", "set", "zset", "hash", "stream"].includes(String(value))) {
45407
+ throw new Error("type must be string, list, set, zset, hash, or stream");
45408
+ }
45409
+ return String(value);
45410
+ }
45411
+ function assertScanPage(keys) {
45412
+ if (!Array.isArray(keys) || keys.length > MAX_SCAN_KEYS) {
45413
+ throw new Error(`Redis SCAN page exceeds ${MAX_SCAN_KEYS} keys`);
45414
+ }
45415
+ if (keys.some((key) => typeof key !== "string" || Buffer.byteLength(key, "utf8") > MAX_KEY_BYTES || /[\u0000-\u001f\u007f]/.test(key)) || Buffer.byteLength(JSON.stringify(keys), "utf8") > MAX_SCAN_BYTES) {
45416
+ throw new Error(`Redis SCAN page exceeds the ${MAX_SCAN_BYTES}-byte limit`);
45417
+ }
45418
+ }
45419
+ function readKey(value) {
45420
+ return readBoundedString(value, "key", MAX_KEY_BYTES);
45421
+ }
45422
+ function readValue(value, label) {
45423
+ if (typeof value !== "string") throw new Error(`${label} must be a string`);
45424
+ if (Buffer.byteLength(value, "utf8") > MAX_VALUE_BYTES) {
45425
+ throw new Error(`${label} exceeds the ${MAX_VALUE_BYTES}-byte limit`);
45426
+ }
45427
+ return value;
45428
+ }
45429
+ function readBoundedString(value, label, maxBytes) {
45430
+ if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > maxBytes || /[\u0000-\u001f\u007f]/.test(value)) {
45431
+ throw new Error(`${label} must be a non-empty string under ${maxBytes} bytes without control characters`);
45432
+ }
45433
+ return value;
45434
+ }
45435
+ function readRequiredString2(value, label, maxLength) {
45436
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
45437
+ throw new Error(`${label} must be a non-empty string no longer than ${maxLength} characters`);
45438
+ }
45439
+ return value;
45440
+ }
45441
+ function readArrayReply(value, label) {
45442
+ if (!Array.isArray(value)) throw new Error(`Redis ${label} returned a malformed response`);
45443
+ return value;
45444
+ }
45445
+ function readStringArray(value, label) {
45446
+ if (!Array.isArray(value)) throw new Error(`Redis response ${label} is malformed`);
45447
+ return value.map((entry) => readReplyString(entry, label));
45448
+ }
45449
+ function readReplyString(value, label) {
45450
+ if (typeof value === "string") return value;
45451
+ if (Buffer.isBuffer(value)) return value.toString("utf8");
45452
+ throw new Error(`Redis response ${label} is malformed`);
45453
+ }
45454
+ function readReplyInteger(value, label) {
45455
+ const parsed = typeof value === "bigint" ? Number(value) : typeof value === "string" ? Number(value) : value;
45456
+ if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw new Error(`Redis response ${label} is malformed`);
45457
+ return parsed;
45458
+ }
45459
+ function normalizeTtl(value) {
45460
+ const ttl = readReplyInteger(value, "ttlMs");
45461
+ return ttl >= 0 ? ttl : null;
45462
+ }
45463
+ function safeErrorMessage2(error, secrets) {
45464
+ let message = error instanceof Error ? error.message : String(error);
45465
+ for (const secret of secrets) {
45466
+ if (secret.length >= 4) message = message.replaceAll(secret, "[REDACTED]");
45467
+ }
45468
+ return message;
45469
+ }
45470
+ function credentialSecrets2(source) {
45471
+ try {
45472
+ return readCredentials3(source).secrets;
45473
+ } catch {
45474
+ return [];
45475
+ }
45476
+ }
45477
+ function committedResult(data4) {
45478
+ return { status: "committed", data: data4, committedAt: Date.now(), idempotentReplay: false };
45479
+ }
45480
+ function conflict(message) {
45481
+ return { status: "conflict", alternatives: [], currentState: null, message };
45482
+ }
45483
+ function readCapability7(name, description, parameters) {
45484
+ return { name, class: "read", description, parameters };
45485
+ }
45486
+ function mutationCapability7(name, description, parameters) {
45487
+ return { name, class: "mutation", description, parameters, cas: "optimistic-read-verify", externalEffect: true };
45488
+ }
45489
+ function keyParameter() {
45490
+ return { type: "string", minLength: 1, maxLength: MAX_KEY_BYTES };
45491
+ }
45492
+ function keySchema() {
45493
+ return {
45494
+ type: "object",
45495
+ properties: { key: keyParameter() },
45496
+ required: ["key"],
45497
+ additionalProperties: false
45498
+ };
45499
+ }
45500
+
44438
45501
  // src/connectors/adapters/recall-ai.ts
44439
45502
  var recallAiRegions = [
44440
45503
  "https://us-east-1.recall.ai",
@@ -45371,7 +46434,7 @@ function createSftpConnector(options = {}) {
45371
46434
  ]
45372
46435
  },
45373
46436
  async executeRead({ source, capabilityName, args }) {
45374
- const credentials = readCredentials2(source);
46437
+ const credentials = readCredentials4(source);
45375
46438
  if (capabilityName === "sftp.list") {
45376
46439
  const path = resolveRemotePath(credentials.rootPath, args.path ?? "/");
45377
46440
  const limit = readBoundedInteger(args.limit, MAX_LIST_ENTRIES, 1, MAX_LIST_ENTRIES, "limit");
@@ -45404,7 +46467,7 @@ function createSftpConnector(options = {}) {
45404
46467
  throw new Error(`Unknown SFTP read capability: ${capabilityName}`);
45405
46468
  },
45406
46469
  async executeMutation({ source, capabilityName, args }) {
45407
- const credentials = readCredentials2(source);
46470
+ const credentials = readCredentials4(source);
45408
46471
  const prepared = prepareMutation(capabilityName, args, credentials.rootPath);
45409
46472
  return withSftp(credentials, createClient, lookupHost, async (client) => {
45410
46473
  let data4;
@@ -45438,7 +46501,7 @@ function createSftpConnector(options = {}) {
45438
46501
  },
45439
46502
  async test(source) {
45440
46503
  try {
45441
- const credentials = readCredentials2(source);
46504
+ const credentials = readCredentials4(source);
45442
46505
  await withSftp(credentials, createClient, lookupHost, (client) => client.cwd());
45443
46506
  return { ok: true };
45444
46507
  } catch (error) {
@@ -45506,7 +46569,7 @@ async function withSftp(credentials, createClient, lookupHost, run) {
45506
46569
  await client.end().catch(() => false);
45507
46570
  }
45508
46571
  }
45509
- function readCredentials2(source) {
46572
+ function readCredentials4(source) {
45510
46573
  let raw;
45511
46574
  if (source.credentials.kind === "custom") raw = source.credentials.values;
45512
46575
  else if (source.credentials.kind === "api-key") {
@@ -55209,17 +56272,17 @@ var zohoBookingsConnector = {
55209
56272
  category: "calendar",
55210
56273
  defaultConsistencyModel: "authoritative",
55211
56274
  capabilities: [
55212
- readCapability6("workspaces.list", "List Zoho Bookings workspaces.", {}),
55213
- readCapability6("services.list", "List services, optionally within one workspace.", {
56275
+ readCapability8("workspaces.list", "List Zoho Bookings workspaces.", {}),
56276
+ readCapability8("services.list", "List services, optionally within one workspace.", {
55214
56277
  workspace_id: { type: "string" }
55215
56278
  }),
55216
- readCapability6("resources.list", "List bookable resources, optionally for one service.", {
56279
+ readCapability8("resources.list", "List bookable resources, optionally for one service.", {
55217
56280
  service_id: { type: "string" }
55218
56281
  }),
55219
- readCapability6("staff.list", "List staff, optionally for one service.", {
56282
+ readCapability8("staff.list", "List staff, optionally for one service.", {
55220
56283
  service_id: { type: "string" }
55221
56284
  }),
55222
- readCapability6("appointments.list", "List appointments from a required start time.", {
56285
+ readCapability8("appointments.list", "List appointments from a required start time.", {
55223
56286
  from_time: {
55224
56287
  type: "string",
55225
56288
  description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format."
@@ -55229,17 +56292,17 @@ var zohoBookingsConnector = {
55229
56292
  staff_id: { type: "string" },
55230
56293
  status: { type: "string" }
55231
56294
  }, ["from_time"]),
55232
- readCapability6("appointments.get", "Read one appointment by booking id.", {
56295
+ readCapability8("appointments.get", "Read one appointment by booking id.", {
55233
56296
  booking_id: { type: "string" }
55234
56297
  }, ["booking_id"]),
55235
- readCapability6("availability.fetch", "Fetch open time slots for a service and date.", {
56298
+ readCapability8("availability.fetch", "Fetch open time slots for a service and date.", {
55236
56299
  service_id: { type: "string" },
55237
56300
  selected_date: { type: "string", description: "Date in YYYY-MM-DD format." },
55238
56301
  staff_id: { type: "string" },
55239
56302
  group_id: { type: "string" },
55240
56303
  resource_id: { type: "string" }
55241
56304
  }, ["service_id", "selected_date"]),
55242
- mutationCapability6("appointments.book", "Book an appointment for a customer.", {
56305
+ mutationCapability8("appointments.book", "Book an appointment for a customer.", {
55243
56306
  service_id: { type: "string" },
55244
56307
  from_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
55245
56308
  customer_details: {
@@ -55255,13 +56318,13 @@ var zohoBookingsConnector = {
55255
56318
  additional_fields: { type: "object" },
55256
56319
  payment_info: { type: "object" }
55257
56320
  }, ["service_id", "from_time", "customer_details"]),
55258
- mutationCapability6("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
56321
+ mutationCapability8("appointments.reschedule", "Move an appointment to a new time, staff member, or group.", {
55259
56322
  booking_id: { type: "string" },
55260
56323
  start_time: { type: "string", description: "Zoho timestamp in dd-MMM-yyyy HH:mm:ss format." },
55261
56324
  staff_id: { type: "string" },
55262
56325
  group_id: { type: "string" }
55263
56326
  }, ["booking_id"]),
55264
- mutationCapability6("appointments.cancel", "Cancel an appointment by booking id.", {
56327
+ mutationCapability8("appointments.cancel", "Cancel an appointment by booking id.", {
55265
56328
  booking_id: { type: "string" }
55266
56329
  }, ["booking_id"])
55267
56330
  ]
@@ -55378,7 +56441,7 @@ var zohoBookingsConnector = {
55378
56441
  }
55379
56442
  }
55380
56443
  };
55381
- function readCapability6(name, description, properties, required = []) {
56444
+ function readCapability8(name, description, properties, required = []) {
55382
56445
  return {
55383
56446
  name,
55384
56447
  class: "read",
@@ -55387,7 +56450,7 @@ function readCapability6(name, description, properties, required = []) {
55387
56450
  requiredScopes: ["zohobookings.data.READ"]
55388
56451
  };
55389
56452
  }
55390
- function mutationCapability6(name, description, properties, required) {
56453
+ function mutationCapability8(name, description, properties, required) {
55391
56454
  return {
55392
56455
  name,
55393
56456
  class: "mutation",
@@ -62731,6 +63794,393 @@ var metabaseConnector = declarativeRestConnector({
62731
63794
  ]
62732
63795
  });
62733
63796
 
63797
+ // src/connectors/adapters/mysql.ts
63798
+ import { connect as createSocket, isIP as isIP5 } from "net";
63799
+ import {
63800
+ createConnection as createMysqlConnection
63801
+ } from "mysql2/promise";
63802
+ var MAX_PARAMETERS = 1e3;
63803
+ var MAX_PARAMETER_BYTES = 10 * 1024 * 1024;
63804
+ var MAX_RESULT_ROWS = 1e4;
63805
+ var MAX_RESULT_BYTES2 = 10 * 1024 * 1024;
63806
+ var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
63807
+ function createMySqlConnector(options = {}) {
63808
+ const createConnection = options.createConnection ?? defaultCreateConnection;
63809
+ const resolveHost = options.resolveHost ?? resolvePublicHostAddresses;
63810
+ return {
63811
+ manifest: {
63812
+ kind: "mysql",
63813
+ displayName: "MySQL",
63814
+ description: "Inspect schemas and run bounded parameterized reads or approved compare-and-swap writes against MySQL over verified TLS.",
63815
+ auth: {
63816
+ kind: "api-key",
63817
+ hint: "JSON with host, port, user, password, optional database, and optional tlsCa. TLS and public network targets are mandatory."
63818
+ },
63819
+ defaultConsistencyModel: "authoritative",
63820
+ category: "database",
63821
+ rateLimit: { requests: 120, windowMs: 6e4, scope: "data-source" },
63822
+ capabilities: [
63823
+ {
63824
+ name: "mysql.databases.list",
63825
+ class: "read",
63826
+ description: "List databases visible to the connected MySQL user.",
63827
+ parameters: emptySchema2()
63828
+ },
63829
+ {
63830
+ name: "mysql.tables.list",
63831
+ class: "read",
63832
+ description: "List base tables and views in one database.",
63833
+ parameters: {
63834
+ type: "object",
63835
+ properties: { database: identifierSchema("Database name; defaults to the connection database.") },
63836
+ additionalProperties: false
63837
+ }
63838
+ },
63839
+ {
63840
+ name: "mysql.tables.describe",
63841
+ class: "read",
63842
+ description: "Read columns, types, nullability, defaults, and keys for one table.",
63843
+ parameters: {
63844
+ type: "object",
63845
+ properties: {
63846
+ database: identifierSchema("Database name; defaults to the connection database."),
63847
+ table: identifierSchema("Table name.")
63848
+ },
63849
+ required: ["table"],
63850
+ additionalProperties: false
63851
+ }
63852
+ },
63853
+ {
63854
+ name: "mysql.query",
63855
+ class: "read",
63856
+ description: "Run one parameterized SELECT, SHOW, DESCRIBE, or EXPLAIN statement inside a read-only transaction.",
63857
+ parameters: statementSchema(false)
63858
+ },
63859
+ {
63860
+ name: "mysql.execute",
63861
+ class: "mutation",
63862
+ description: "Run one approved parameterized INSERT, UPDATE, DELETE, or REPLACE statement and commit only when the affected-row count matches the caller expectation.",
63863
+ parameters: statementSchema(true),
63864
+ cas: "optimistic-read-verify",
63865
+ externalEffect: true
63866
+ }
63867
+ ]
63868
+ },
63869
+ async test(source) {
63870
+ try {
63871
+ await withConnection(source, createConnection, resolveHost, async (connection) => {
63872
+ await connection.ping();
63873
+ });
63874
+ return { ok: true };
63875
+ } catch (error) {
63876
+ return { ok: false, reason: safeErrorMessage3(error) };
63877
+ }
63878
+ },
63879
+ async executeRead(inv) {
63880
+ const query2 = readQuery2(inv);
63881
+ assertReadStatement(query2.statement);
63882
+ const data4 = await withConnection(inv.source, createConnection, resolveHost, async (connection) => {
63883
+ await executeStatement(connection, {
63884
+ statement: "START TRANSACTION READ ONLY",
63885
+ parameters: []
63886
+ }, queryTimeout(inv.source));
63887
+ try {
63888
+ const [rows, fields] = await executeStatement(connection, query2, queryTimeout(inv.source));
63889
+ if (!Array.isArray(rows)) throw new Error("MySQL read returned a non-row result");
63890
+ return boundedResult(rows, fields);
63891
+ } finally {
63892
+ await connection.rollback().catch(() => void 0);
63893
+ }
63894
+ });
63895
+ return { data: data4, fetchedAt: Date.now() };
63896
+ },
63897
+ async executeMutation(inv) {
63898
+ if (inv.capabilityName !== "mysql.execute") {
63899
+ throw new Error(`Unknown MySQL mutation capability: ${inv.capabilityName}`);
63900
+ }
63901
+ const query2 = readStatementArgs(inv.args);
63902
+ assertMutationStatement(query2.statement);
63903
+ const expectedAffectedRows = readBoundedInteger(
63904
+ inv.args.expectedAffectedRows,
63905
+ -1,
63906
+ 0,
63907
+ MAX_RESULT_ROWS,
63908
+ "expectedAffectedRows"
63909
+ );
63910
+ if (expectedAffectedRows < 0) throw new Error("expectedAffectedRows is required");
63911
+ return withConnection(inv.source, createConnection, resolveHost, async (connection) => {
63912
+ await connection.beginTransaction();
63913
+ try {
63914
+ const [result] = await executeStatement(connection, query2, queryTimeout(inv.source));
63915
+ const affectedRows = readAffectedRows(result);
63916
+ if (affectedRows !== expectedAffectedRows) {
63917
+ await connection.rollback();
63918
+ return {
63919
+ status: "conflict",
63920
+ alternatives: [],
63921
+ currentState: { affectedRows },
63922
+ message: `MySQL write affected ${affectedRows} rows; expected ${expectedAffectedRows}. The transaction was rolled back.`
63923
+ };
63924
+ }
63925
+ await connection.commit();
63926
+ return committedMutation(result, affectedRows);
63927
+ } catch (error) {
63928
+ await connection.rollback().catch(() => void 0);
63929
+ throw error;
63930
+ }
63931
+ });
63932
+ }
63933
+ };
63934
+ }
63935
+ var mysqlConnector = createMySqlConnector();
63936
+ async function defaultCreateConnection(config) {
63937
+ return createMysqlConnection(config);
63938
+ }
63939
+ async function withConnection(source, createConnection, resolveHost, run) {
63940
+ const credentials = readCredentials5(source);
63941
+ const addresses = await resolveHost(credentials.host);
63942
+ if (addresses.length === 0) throw new Error("MySQL host did not resolve");
63943
+ const address = addresses[0];
63944
+ let connection;
63945
+ try {
63946
+ connection = await createConnection({
63947
+ host: credentials.host,
63948
+ port: credentials.port,
63949
+ user: credentials.user,
63950
+ password: credentials.password,
63951
+ database: credentials.database,
63952
+ connectTimeout: credentials.connectTimeoutMs,
63953
+ multipleStatements: false,
63954
+ namedPlaceholders: false,
63955
+ supportBigNumbers: true,
63956
+ bigNumberStrings: true,
63957
+ decimalNumbers: false,
63958
+ dateStrings: true,
63959
+ timezone: "Z",
63960
+ charset: "utf8mb4",
63961
+ enableKeepAlive: true,
63962
+ maxPreparedStatements: 100,
63963
+ stream: () => createSocket({ host: address, port: credentials.port }),
63964
+ ssl: {
63965
+ ca: credentials.tlsCa,
63966
+ minVersion: "TLSv1.2",
63967
+ rejectUnauthorized: true,
63968
+ verifyIdentity: true
63969
+ }
63970
+ });
63971
+ return await run(connection);
63972
+ } finally {
63973
+ if (connection) await connection.end().catch(() => void 0);
63974
+ }
63975
+ }
63976
+ function readCredentials5(source) {
63977
+ let raw;
63978
+ if (source.credentials.kind === "custom") raw = source.credentials.values;
63979
+ else if (source.credentials.kind === "api-key") {
63980
+ try {
63981
+ raw = JSON.parse(source.credentials.apiKey);
63982
+ } catch {
63983
+ throw new Error("MySQL credential must be valid JSON");
63984
+ }
63985
+ } else {
63986
+ throw new Error("MySQL requires a structured credential bundle");
63987
+ }
63988
+ if (!isPlainRecord(raw)) throw new Error("MySQL credential must be a JSON object");
63989
+ const host = requiredCredentialString3(raw.host, "host", 253);
63990
+ validateHost(host);
63991
+ const user = requiredCredentialString3(raw.user ?? raw.username, "user", 128);
63992
+ const password = requiredCredentialString3(raw.password, "password", 4096);
63993
+ const database = readOptionalString(raw.database, "database");
63994
+ if (database && database.length > 64) throw new Error("MySQL credential database exceeds 64 characters");
63995
+ const tlsCa = readOptionalString(raw.tlsCa, "tlsCa");
63996
+ if (tlsCa && Buffer.byteLength(tlsCa, "utf8") > 1024 * 1024) {
63997
+ throw new Error("MySQL credential tlsCa exceeds the 1 MiB limit");
63998
+ }
63999
+ return {
64000
+ host,
64001
+ port: readBoundedInteger(raw.port, 3306, 1, 65535, "port"),
64002
+ user,
64003
+ password,
64004
+ database,
64005
+ tlsCa,
64006
+ connectTimeoutMs: readBoundedInteger(raw.connectTimeoutMs, 1e4, 1e3, 3e4, "connectTimeoutMs"),
64007
+ queryTimeoutMs: readBoundedInteger(raw.queryTimeoutMs, DEFAULT_QUERY_TIMEOUT_MS, 1e3, 6e4, "queryTimeoutMs")
64008
+ };
64009
+ }
64010
+ function readQuery2(inv) {
64011
+ if (inv.capabilityName === "mysql.databases.list") {
64012
+ return {
64013
+ statement: "SELECT SCHEMA_NAME AS databaseName, DEFAULT_CHARACTER_SET_NAME AS characterSet, DEFAULT_COLLATION_NAME AS collation FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME",
64014
+ parameters: []
64015
+ };
64016
+ }
64017
+ if (inv.capabilityName === "mysql.tables.list") {
64018
+ const database = readDatabase(inv.args.database, inv.source);
64019
+ return {
64020
+ statement: "SELECT TABLE_NAME AS tableName, TABLE_TYPE AS tableType, ENGINE AS engine, TABLE_ROWS AS estimatedRows, TABLE_COLLATION AS collation FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME",
64021
+ parameters: [database]
64022
+ };
64023
+ }
64024
+ if (inv.capabilityName === "mysql.tables.describe") {
64025
+ const database = readDatabase(inv.args.database, inv.source);
64026
+ const table = readIdentifier(inv.args.table, "table");
64027
+ return {
64028
+ statement: "SELECT COLUMN_NAME AS columnName, ORDINAL_POSITION AS ordinalPosition, COLUMN_DEFAULT AS columnDefault, IS_NULLABLE AS isNullable, DATA_TYPE AS dataType, COLUMN_TYPE AS columnType, COLUMN_KEY AS columnKey, EXTRA AS extra FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION",
64029
+ parameters: [database, table]
64030
+ };
64031
+ }
64032
+ if (inv.capabilityName === "mysql.query") return readStatementArgs(inv.args);
64033
+ throw new Error(`Unknown MySQL read capability: ${inv.capabilityName}`);
64034
+ }
64035
+ function readStatementArgs(args) {
64036
+ const statement = requiredString7(args.statement, "statement");
64037
+ if (Buffer.byteLength(statement, "utf8") > 1024 * 1024) throw new Error("statement exceeds the 1 MiB limit");
64038
+ const parameters = args.parameters === void 0 ? [] : args.parameters;
64039
+ if (!Array.isArray(parameters) || parameters.length > MAX_PARAMETERS) {
64040
+ throw new Error(`parameters must be an array with at most ${MAX_PARAMETERS} entries`);
64041
+ }
64042
+ for (const [index, value] of parameters.entries()) {
64043
+ if (value !== null && !["string", "number", "boolean"].includes(typeof value)) {
64044
+ throw new Error(`parameters[${index}] must be a JSON scalar or null`);
64045
+ }
64046
+ if (typeof value === "number" && !Number.isFinite(value)) {
64047
+ throw new Error(`parameters[${index}] must be finite`);
64048
+ }
64049
+ }
64050
+ if (Buffer.byteLength(JSON.stringify(parameters), "utf8") > MAX_PARAMETER_BYTES) {
64051
+ throw new Error(`parameters exceed the ${MAX_PARAMETER_BYTES}-byte limit`);
64052
+ }
64053
+ return { statement, parameters };
64054
+ }
64055
+ function assertReadStatement(statement) {
64056
+ const keyword = leadingKeyword(statement);
64057
+ if (!["SELECT", "SHOW", "DESCRIBE", "DESC", "EXPLAIN", "WITH"].includes(keyword)) {
64058
+ throw new Error("mysql.query accepts only SELECT, SHOW, DESCRIBE, or EXPLAIN statements");
64059
+ }
64060
+ if (/\b(?:INTO\s+(?:OUTFILE|DUMPFILE)|LOAD_FILE\s*\(|GET_LOCK\s*\(|RELEASE_LOCK\s*\(|SLEEP\s*\(|BENCHMARK\s*\(|FOR\s+UPDATE|LOCK\s+IN\s+SHARE\s+MODE)\b/i.test(statement)) {
64061
+ throw new Error("mysql.query rejects file, lock, and delay side effects");
64062
+ }
64063
+ }
64064
+ function assertMutationStatement(statement) {
64065
+ const keyword = leadingKeyword(statement);
64066
+ if (!["INSERT", "UPDATE", "DELETE", "REPLACE"].includes(keyword)) {
64067
+ throw new Error("mysql.execute accepts only INSERT, UPDATE, DELETE, or REPLACE statements");
64068
+ }
64069
+ }
64070
+ function leadingKeyword(statement) {
64071
+ const withoutComments = statement.replace(/^\s*(?:--[^\n]*(?:\n|$)|#[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/\s*)*/u, "").trimStart();
64072
+ return /^[A-Za-z]+/.exec(withoutComments)?.[0]?.toUpperCase() ?? "";
64073
+ }
64074
+ async function executeStatement(connection, query2, timeout) {
64075
+ return connection.execute({ sql: query2.statement, values: query2.parameters, timeout });
64076
+ }
64077
+ function boundedResult(rows, fields) {
64078
+ if (rows.length > MAX_RESULT_ROWS) throw new Error(`MySQL result exceeds ${MAX_RESULT_ROWS} rows`);
64079
+ const data4 = {
64080
+ rows: jsonSafe(rows),
64081
+ columns: fields.map((field) => ({
64082
+ name: field.name,
64083
+ table: field.table,
64084
+ database: field.schema,
64085
+ type: field.type
64086
+ })),
64087
+ rowCount: rows.length
64088
+ };
64089
+ const serialized = JSON.stringify(data4);
64090
+ if (Buffer.byteLength(serialized, "utf8") > MAX_RESULT_BYTES2) {
64091
+ throw new Error(`MySQL result exceeds the ${MAX_RESULT_BYTES2}-byte limit`);
64092
+ }
64093
+ return data4;
64094
+ }
64095
+ function readAffectedRows(result) {
64096
+ if (!isPlainRecord(result) || !Number.isInteger(result.affectedRows) || result.affectedRows < 0) {
64097
+ throw new Error("MySQL mutation did not return an affected-row count");
64098
+ }
64099
+ return result.affectedRows;
64100
+ }
64101
+ function committedMutation(result, affectedRows) {
64102
+ const header2 = result;
64103
+ const rawInsertId = header2.insertId;
64104
+ const insertId = typeof rawInsertId === "number" && Number.isSafeInteger(rawInsertId) ? rawInsertId : typeof rawInsertId === "string" ? rawInsertId : void 0;
64105
+ return {
64106
+ status: "committed",
64107
+ data: jsonSafe({
64108
+ affectedRows,
64109
+ changedRows: header2.changedRows,
64110
+ insertId,
64111
+ warningStatus: header2.warningStatus
64112
+ }),
64113
+ committedAt: Date.now(),
64114
+ idempotentReplay: false
64115
+ };
64116
+ }
64117
+ function readDatabase(value, source) {
64118
+ const explicit = value === void 0 ? void 0 : readIdentifier(value, "database");
64119
+ if (explicit) return explicit;
64120
+ const credentials = readCredentials5(source);
64121
+ if (!credentials.database) throw new Error("database is required when the connection has no default database");
64122
+ return credentials.database;
64123
+ }
64124
+ function queryTimeout(source) {
64125
+ return readCredentials5(source).queryTimeoutMs;
64126
+ }
64127
+ function requiredString7(value, label) {
64128
+ const parsed = readOptionalString(value, label);
64129
+ if (!parsed) throw new Error(`${label} is required`);
64130
+ return parsed;
64131
+ }
64132
+ function requiredCredentialString3(value, label, maxLength) {
64133
+ const parsed = readOptionalString(value, label);
64134
+ if (!parsed) throw new Error(`MySQL credential ${label} is required`);
64135
+ if (parsed.length > maxLength) throw new Error(`MySQL credential ${label} exceeds ${maxLength} characters`);
64136
+ return parsed;
64137
+ }
64138
+ function readIdentifier(value, label) {
64139
+ const parsed = requiredString7(value, label);
64140
+ if (parsed.length > 64) throw new Error(`${label} exceeds 64 characters`);
64141
+ return parsed;
64142
+ }
64143
+ function validateHost(host) {
64144
+ const normalized = host.toLowerCase();
64145
+ if (host.length > 253 || host.includes("://") || /[\s/@]/.test(host) || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || isIP5(host) === 0 && !/^[A-Za-z0-9.-]+$/.test(host) || host.split(".").some((label) => !label || label.startsWith("-") || label.endsWith("-"))) {
64146
+ throw new Error("MySQL host must be a public hostname or IP address without a scheme");
64147
+ }
64148
+ }
64149
+ function emptySchema2() {
64150
+ return { type: "object", properties: {}, additionalProperties: false };
64151
+ }
64152
+ function identifierSchema(description) {
64153
+ return { type: "string", minLength: 1, maxLength: 64, description };
64154
+ }
64155
+ function statementSchema(mutation) {
64156
+ const properties = {
64157
+ statement: { type: "string", minLength: 1, maxLength: 1024 * 1024 },
64158
+ parameters: {
64159
+ type: "array",
64160
+ maxItems: MAX_PARAMETERS,
64161
+ items: { type: ["string", "number", "boolean", "null"] },
64162
+ description: "Values bound to ? placeholders in order."
64163
+ }
64164
+ };
64165
+ if (mutation) {
64166
+ properties.expectedAffectedRows = {
64167
+ type: "integer",
64168
+ minimum: 0,
64169
+ maximum: MAX_RESULT_ROWS,
64170
+ description: "Commit only when MySQL reports exactly this many affected rows."
64171
+ };
64172
+ }
64173
+ return {
64174
+ type: "object",
64175
+ properties,
64176
+ required: mutation ? ["statement", "expectedAffectedRows"] : ["statement"],
64177
+ additionalProperties: false
64178
+ };
64179
+ }
64180
+ function safeErrorMessage3(error) {
64181
+ return error instanceof Error && error.message ? error.message : "MySQL connection failed";
64182
+ }
64183
+
62734
64184
  // src/connectors/adapters/segment.ts
62735
64185
  var segmentConnector = declarativeRestConnector({
62736
64186
  kind: "segment",
@@ -64226,6 +65676,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
64226
65676
  defineFactoryAdapter(() => digitalOceanConnector, {}),
64227
65677
  defineFactoryAdapter(() => clicksendConnector, {}),
64228
65678
  defineFactoryAdapter(() => metabaseConnector, {}),
65679
+ defineFactoryAdapter(() => mysqlConnector, {}),
64229
65680
  defineFactoryAdapter(
64230
65681
  () => microsoftDynamics365BusinessCentralConnector,
64231
65682
  microsoftOAuthEnvMap
@@ -64284,6 +65735,9 @@ var CONNECTOR_ADAPTER_FACTORIES = [
64284
65735
  defineFactoryAdapter(() => httpConnector, {}),
64285
65736
  defineFactoryAdapter(() => rssConnector, {}),
64286
65737
  defineFactoryAdapter(() => kafkaConnector, {}),
65738
+ defineFactoryAdapter(() => duckdbConnector, {}),
65739
+ defineFactoryAdapter(() => rabbitMqConnector, {}),
65740
+ defineFactoryAdapter(() => redisConnector, {}),
64287
65741
  defineFactoryAdapter(() => csvFilesConnector, {}),
64288
65742
  defineFactoryAdapter(() => excelFilesConnector, {}),
64289
65743
  defineFactoryAdapter(() => parquetFilesConnector, {}),
@@ -64356,10 +65810,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
64356
65810
  clientId: "MONDAY_OAUTH_CLIENT_ID",
64357
65811
  clientSecret: "MONDAY_OAUTH_CLIENT_SECRET"
64358
65812
  }),
64359
- defineFactoryAdapter(() => clickupConnector, {
64360
- clientId: "CLICKUP_OAUTH_CLIENT_ID",
64361
- clientSecret: "CLICKUP_OAUTH_CLIENT_SECRET"
64362
- }),
65813
+ defineFactoryAdapter(() => clickupConnector, {}),
64363
65814
  defineFactoryAdapter(() => basecampConnector, {
64364
65815
  clientId: "BASECAMP_OAUTH_CLIENT_ID",
64365
65816
  clientSecret: "BASECAMP_OAUTH_CLIENT_SECRET"
@@ -144139,6 +145590,7 @@ export {
144139
145590
  deepgramConnector,
144140
145591
  detectingAiConnector,
144141
145592
  digitalPilotConnector,
145593
+ duckdbConnector,
144142
145594
  docsbotConnector,
144143
145595
  dripConnector,
144144
145596
  echowinConnector,
@@ -144287,6 +145739,10 @@ export {
144287
145739
  pipedriveConnector,
144288
145740
  parquetFilesConnector,
144289
145741
  quickbooksConnector,
145742
+ createRabbitMqConnector,
145743
+ rabbitMqConnector,
145744
+ createRedisConnector,
145745
+ redisConnector,
144290
145746
  recallAiConnector,
144291
145747
  rampConnector,
144292
145748
  ringcentralConnector,
@@ -144354,6 +145810,8 @@ export {
144354
145810
  googleBigqueryConnector,
144355
145811
  hightouchConnector,
144356
145812
  metabaseConnector,
145813
+ createMySqlConnector,
145814
+ mysqlConnector,
144357
145815
  segmentConnector,
144358
145816
  supabaseConnector,
144359
145817
  builtwithConnector,
@@ -144745,4 +146203,4 @@ export {
144745
146203
  demandbaseConnector,
144746
146204
  adapters_exports
144747
146205
  };
144748
- //# sourceMappingURL=chunk-5LDZLVKP.js.map
146206
+ //# sourceMappingURL=chunk-FOIC32DF.js.map