@tangle-network/agent-integrations 0.52.0 → 0.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/catalog.d.ts +2 -2
  2. package/dist/catalog.js +9 -9
  3. package/dist/{chunk-S2BUASSV.js → chunk-5NLIZJOI.js} +2 -2
  4. package/dist/{chunk-XEJKVCXB.js → chunk-5QWQFX2Q.js} +1841 -34
  5. package/dist/{chunk-XEJKVCXB.js.map → chunk-5QWQFX2Q.js.map} +1 -1
  6. package/dist/{chunk-FOQ7S4TK.js → chunk-6OQNMPQE.js} +85 -3
  7. package/dist/chunk-6OQNMPQE.js.map +1 -0
  8. package/dist/{chunk-VLVSP7SP.js → chunk-6ZHVV537.js} +2 -2
  9. package/dist/{chunk-7P2LN4VT.js → chunk-LBPYXEHO.js} +71 -4
  10. package/dist/chunk-LBPYXEHO.js.map +1 -0
  11. package/dist/{chunk-PUXYDSXI.js → chunk-NLFG3UWL.js} +2 -2
  12. package/dist/{chunk-UWF33ESC.js → chunk-PDV6QQTH.js} +3 -1
  13. package/dist/chunk-PDV6QQTH.js.map +1 -0
  14. package/dist/{chunk-LA76XB7M.js → chunk-PNHETWE4.js} +2 -2
  15. package/dist/{chunk-BBUJPJRA.js → chunk-SQU63PYS.js} +5 -3
  16. package/dist/{chunk-BBUJPJRA.js.map → chunk-SQU63PYS.js.map} +1 -1
  17. package/dist/connect/index.d.ts +2 -2
  18. package/dist/connect/index.js +2 -2
  19. package/dist/connectors/adapters/index.d.ts +96 -3
  20. package/dist/connectors/adapters/index.js +12 -2
  21. package/dist/connectors/index.d.ts +4 -4
  22. package/dist/connectors/index.js +13 -3
  23. package/dist/{consumer-CTMB1xcc.d.ts → consumer-DJLQcY8t.d.ts} +1 -1
  24. package/dist/consumer.d.ts +3 -3
  25. package/dist/consumer.js +2 -2
  26. package/dist/{core-types-BZLzY8Oa.d.ts → core-types-CdAHF1wy.d.ts} +1 -1
  27. package/dist/coverage-catalog.d.ts +3 -3
  28. package/dist/coverage-catalog.js +1 -1
  29. package/dist/{importers-6Beu0SQ9.d.ts → importers-Ch76O-1f.d.ts} +1 -1
  30. package/dist/index.d.ts +9 -9
  31. package/dist/index.js +19 -9
  32. package/dist/mcp.d.ts +3 -3
  33. package/dist/mcp.js +9 -9
  34. package/dist/middleware/index.d.ts +2 -2
  35. package/dist/middleware/index.js +2 -2
  36. package/dist/registry.d.ts +2 -2
  37. package/dist/registry.js +9 -9
  38. package/dist/runtime.d.ts +2 -2
  39. package/dist/runtime.js +9 -9
  40. package/dist/specs.d.ts +2 -2
  41. package/dist/specs.js +5 -5
  42. package/dist/{tangle-id-_uZ3ywCF.d.ts → tangle-id-DkT2T54x.d.ts} +1 -1
  43. package/dist/{types-rBPdMcpb.d.ts → types-DkIrJgM-.d.ts} +8 -2
  44. package/docs/integration-execution-audit.md +143 -20
  45. package/docs/integration-execution-matrix.json +4541 -1478
  46. package/package.json +1 -1
  47. package/dist/chunk-7P2LN4VT.js.map +0 -1
  48. package/dist/chunk-FOQ7S4TK.js.map +0 -1
  49. package/dist/chunk-UWF33ESC.js.map +0 -1
  50. /package/dist/{chunk-S2BUASSV.js.map → chunk-5NLIZJOI.js.map} +0 -0
  51. /package/dist/{chunk-VLVSP7SP.js.map → chunk-6ZHVV537.js.map} +0 -0
  52. /package/dist/{chunk-PUXYDSXI.js.map → chunk-NLFG3UWL.js.map} +0 -0
  53. /package/dist/{chunk-LA76XB7M.js.map → chunk-PNHETWE4.js.map} +0 -0
@@ -14,7 +14,7 @@ import {
14
14
  TangleIdentityUnreachableError,
15
15
  createTangleIdentityClient,
16
16
  tangleIdentity
17
- } from "./chunk-UWF33ESC.js";
17
+ } from "./chunk-PDV6QQTH.js";
18
18
  import {
19
19
  __export
20
20
  } from "./chunk-PZ5AY32C.js";
@@ -32,6 +32,7 @@ __export(adapters_exports, {
32
32
  acumbamailConnector: () => acumbamailConnector,
33
33
  adobeCreativeCloudConnector: () => adobeCreativeCloudConnector,
34
34
  adpConnector: () => adpConnector,
35
+ affinityConnector: () => affinityConnector,
35
36
  afforaiConnector: () => afforaiConnector,
36
37
  agentxConnector: () => agentxConnector,
37
38
  aidbaseConnector: () => aidbaseConnector,
@@ -44,6 +45,7 @@ __export(adapters_exports, {
44
45
  altTextAiConnector: () => altTextAiConnector,
45
46
  alttextifyConnector: () => alttextifyConnector,
46
47
  amazonBedrockConnector: () => amazonBedrockConnector,
48
+ amazonS3Connector: () => amazonS3Connector,
47
49
  amazonSecretsManagerConnector: () => amazonSecretsManagerConnector,
48
50
  amazonSesConnector: () => amazonSesConnector,
49
51
  amazonSnsConnector: () => amazonSnsConnector,
@@ -263,6 +265,7 @@ __export(adapters_exports, {
263
265
  heyreachConnector: () => heyreachConnector,
264
266
  hightouchConnector: () => hightouchConnector,
265
267
  housecallProConnector: () => housecallProConnector,
268
+ httpConnector: () => httpConnector,
266
269
  hubspot: () => hubspot,
267
270
  huggingFaceConnector: () => huggingFaceConnector,
268
271
  huggingfaceConnector: () => huggingfaceConnector,
@@ -443,6 +446,7 @@ __export(adapters_exports, {
443
446
  ringcentralConnector: () => ringcentralConnector,
444
447
  ripplingConnector: () => ripplingConnector,
445
448
  robollyConnector: () => robollyConnector,
449
+ rssConnector: () => rssConnector,
446
450
  runwareConnector: () => runwareConnector,
447
451
  saasticConnector: () => saasticConnector,
448
452
  saleorConnector: () => saleorConnector,
@@ -568,6 +572,7 @@ __export(adapters_exports, {
568
572
  zohoBookingsConnector: () => zohoBookingsConnector,
569
573
  zohoCrmConnector: () => zohoCrmConnector,
570
574
  zohoDeskConnector: () => zohoDeskConnector,
575
+ zoomConnector: () => zoomConnector,
571
576
  zoominfoConnector: () => zoominfoConnector,
572
577
  zuoraConnector: () => zuoraConnector
573
578
  });
@@ -999,6 +1004,19 @@ function declarativeRestConnector(spec) {
999
1004
  async executeRead(inv) {
1000
1005
  const op = readOperation(spec, inv.capabilityName, "read");
1001
1006
  const response = await executeRestRequest(spec, op.request, inv, requiredArgsOf(op.parameters));
1007
+ if (response.outcome === "rate-limited") {
1008
+ const retryAfterMs = response.retryAfterMs ?? DEFAULT_RETRY_AFTER_MS;
1009
+ throw new ProviderRateLimited(
1010
+ `${spec.displayName} ${inv.capabilityName} rate limit (429); retry after ${retryAfterMs}ms`,
1011
+ inv.source.id,
1012
+ { status: 429, body: response.data, retryAfterMs }
1013
+ );
1014
+ }
1015
+ if (response.outcome === "conflict") {
1016
+ throw new Error(
1017
+ `${spec.kind} ${op.request.method} ${op.request.path} HTTP ${response.status ?? 409}: ${(response.message ?? "").slice(0, 300)}`
1018
+ );
1019
+ }
1002
1020
  return {
1003
1021
  data: response.data,
1004
1022
  etag: response.etag,
@@ -1008,31 +1026,7 @@ function declarativeRestConnector(spec) {
1008
1026
  async executeMutation(inv) {
1009
1027
  const op = readOperation(spec, inv.capabilityName, "mutation");
1010
1028
  const response = await executeRestRequest(spec, op.request, inv, requiredArgsOf(op.parameters));
1011
- if (response.outcome === "conflict") {
1012
- return {
1013
- status: "conflict",
1014
- alternatives: [],
1015
- // The upstream's parsed body — the contract calls this "the current
1016
- // authoritative state", so it must be what the provider returned,
1017
- // not the transport's own wrapper.
1018
- currentState: response.data,
1019
- message: response.message || `${spec.displayName} rejected the write on a state conflict`
1020
- };
1021
- }
1022
- if (response.outcome === "rate-limited") {
1023
- return {
1024
- status: "rate-limited",
1025
- retryAfterMs: response.retryAfterMs ?? DEFAULT_RETRY_AFTER_MS,
1026
- message: response.message || `${spec.displayName} throttled the write`
1027
- };
1028
- }
1029
- return {
1030
- status: "committed",
1031
- data: response.data,
1032
- etagAfter: response.etag,
1033
- committedAt: Date.now(),
1034
- idempotentReplay: false
1035
- };
1029
+ return mutationResultFromTransport(spec.displayName, response);
1036
1030
  },
1037
1031
  async test(source) {
1038
1032
  if (!spec.test) return { ok: true };
@@ -1043,6 +1037,12 @@ function declarativeRestConnector(spec) {
1043
1037
  args: {},
1044
1038
  idempotencyKey: "test"
1045
1039
  });
1040
+ if (response.outcome) {
1041
+ return {
1042
+ ok: false,
1043
+ reason: response.message || `${spec.displayName} test request was ${response.outcome}`
1044
+ };
1045
+ }
1046
1046
  validateTestResponse(spec, spec.test, response.data);
1047
1047
  return { ok: true };
1048
1048
  } catch (error) {
@@ -1099,6 +1099,33 @@ function requiredArgsOf(parameters) {
1099
1099
  const required = parameters.required;
1100
1100
  return Array.isArray(required) ? required.filter((entry) => typeof entry === "string") : void 0;
1101
1101
  }
1102
+ function mutationResultFromTransport(displayName, response) {
1103
+ if (response.outcome === "conflict") {
1104
+ return {
1105
+ status: "conflict",
1106
+ alternatives: [],
1107
+ // The upstream's parsed body — the contract calls this "the current
1108
+ // authoritative state", so it must be what the provider returned,
1109
+ // not the transport's own wrapper.
1110
+ currentState: response.data,
1111
+ message: response.message || `${displayName} rejected the write on a state conflict`
1112
+ };
1113
+ }
1114
+ if (response.outcome === "rate-limited") {
1115
+ return {
1116
+ status: "rate-limited",
1117
+ retryAfterMs: response.retryAfterMs ?? DEFAULT_RETRY_AFTER_MS,
1118
+ message: response.message || `${displayName} throttled the write`
1119
+ };
1120
+ }
1121
+ return {
1122
+ status: "committed",
1123
+ data: response.data,
1124
+ etagAfter: response.etag,
1125
+ committedAt: Date.now(),
1126
+ idempotentReplay: false
1127
+ };
1128
+ }
1102
1129
  async function executeRestRequest(spec, request, inv, requiredArgs) {
1103
1130
  const placement = spec.credentialPlacement ?? { kind: "bearer" };
1104
1131
  const aws = placement.kind === "aws-sigv4" ? parseAwsCredentialBundle(inv.source.credentials) : void 0;
@@ -1153,6 +1180,7 @@ async function executeRestRequest(spec, request, inv, requiredArgs) {
1153
1180
  return {
1154
1181
  data: parseBodyText(text2),
1155
1182
  outcome: "conflict",
1183
+ status: res.status,
1156
1184
  message: text2,
1157
1185
  etag: res.headers.get("etag") ?? void 0
1158
1186
  };
@@ -1162,6 +1190,7 @@ async function executeRestRequest(spec, request, inv, requiredArgs) {
1162
1190
  return {
1163
1191
  data: parseBodyText(text2),
1164
1192
  outcome: "rate-limited",
1193
+ status: res.status,
1165
1194
  message: text2,
1166
1195
  retryAfterMs: retryAfterMsFromHeader(res.headers.get("retry-after"))
1167
1196
  };
@@ -7398,13 +7427,7 @@ async function databasesUpdate(inv) {
7398
7427
  { method: "PATCH", path: "/databases/{databaseId}", body },
7399
7428
  inv
7400
7429
  );
7401
- return {
7402
- status: "committed",
7403
- data: response.data,
7404
- etagAfter: response.etag,
7405
- committedAt: Date.now(),
7406
- idempotentReplay: false
7407
- };
7430
+ return mutationResultFromTransport(NOTION_SPEC.displayName, response);
7408
7431
  }
7409
7432
  function withNotionOverrides(base) {
7410
7433
  return {
@@ -14008,6 +14031,910 @@ function signHeaders(creds, body, idempotencyKey) {
14008
14031
  return headers;
14009
14032
  }
14010
14033
 
14034
+ // src/connectors/adapters/public-http-target.ts
14035
+ function assertPublicHttpTarget(url, label) {
14036
+ const hostname = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
14037
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local") || hostname === "metadata.google.internal") {
14038
+ throw new Error(`${label} host is not a public network target`);
14039
+ }
14040
+ if (isPrivateIpv4(hostname) || isPrivateIpv6(hostname)) {
14041
+ throw new Error(`${label} host is not a public network target`);
14042
+ }
14043
+ }
14044
+ function isPrivateIpv4(hostname) {
14045
+ const parts = hostname.split(".");
14046
+ if (parts.length !== 4 || parts.some((part) => !/^\d{1,3}$/.test(part))) return false;
14047
+ const octets = parts.map(Number);
14048
+ if (octets.some((octet) => octet > 255)) return false;
14049
+ const [a, b] = octets;
14050
+ return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224;
14051
+ }
14052
+ function isPrivateIpv6(hostname) {
14053
+ const normalized = hostname.toLowerCase();
14054
+ if (normalized === "::" || normalized === "::1") return true;
14055
+ if (/^f[cd]/.test(normalized) || /^fe[89ab]/.test(normalized)) return true;
14056
+ const mapped = normalized.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
14057
+ return mapped ? isPrivateIpv4(mapped[1]) : false;
14058
+ }
14059
+
14060
+ // src/connectors/adapters/http.ts
14061
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:"]);
14062
+ var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
14063
+ var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
14064
+ var MAX_TIMEOUT_MS = 6e4;
14065
+ var DEFAULT_TIMEOUT_MS = 15e3;
14066
+ var httpConnector = {
14067
+ manifest: {
14068
+ kind: "http",
14069
+ displayName: "HTTP Request",
14070
+ description: "Send an arbitrary HTTP request to any http(s) URL. Useful for hitting one-off APIs, probes, public JSON feeds, or operator-controlled webhooks. No shared auth \u2014 the agent supplies the full URL, method, headers, and body at invocation time.",
14071
+ auth: { kind: "none" },
14072
+ category: "webhook",
14073
+ defaultConsistencyModel: "advisory",
14074
+ capabilities: [
14075
+ {
14076
+ name: "request.fetch",
14077
+ class: "read",
14078
+ description: "Issue a GET/HEAD/OPTIONS request to a URL. Returns the parsed JSON body if the response Content-Type is application/json, otherwise the raw text. The `etag` of the response (when present) is exposed for downstream conditional reads.",
14079
+ parameters: {
14080
+ type: "object",
14081
+ properties: {
14082
+ url: { type: "string", description: "Absolute http(s) URL to fetch." },
14083
+ method: {
14084
+ type: "string",
14085
+ enum: ["GET", "HEAD", "OPTIONS"],
14086
+ description: "HTTP verb. Defaults to GET."
14087
+ },
14088
+ headers: {
14089
+ type: "object",
14090
+ description: "Optional request headers. Header names are lower-cased before send.",
14091
+ additionalProperties: { type: "string" }
14092
+ },
14093
+ query: {
14094
+ type: "object",
14095
+ description: "Optional query-string parameters. Values are coerced to string; arrays become repeated keys.",
14096
+ additionalProperties: true
14097
+ },
14098
+ timeoutMs: {
14099
+ type: "number",
14100
+ description: `Per-request timeout in milliseconds. Defaults to ${DEFAULT_TIMEOUT_MS}, capped at ${MAX_TIMEOUT_MS}.`
14101
+ }
14102
+ },
14103
+ required: ["url"]
14104
+ }
14105
+ },
14106
+ {
14107
+ name: "request.send",
14108
+ class: "mutation",
14109
+ description: "Issue a POST/PUT/PATCH/DELETE request to a URL. The agent supplies the body verbatim \u2014 pass an object for JSON, a string for raw text. The idempotency key is forwarded as the `Idempotency-Key` header; receivers that honour it (Stripe, Square, many SaaS APIs) dedupe automatically.",
14110
+ parameters: {
14111
+ type: "object",
14112
+ properties: {
14113
+ url: { type: "string", description: "Absolute http(s) URL to call." },
14114
+ method: {
14115
+ type: "string",
14116
+ enum: ["POST", "PUT", "PATCH", "DELETE"],
14117
+ description: "HTTP verb. Defaults to POST."
14118
+ },
14119
+ headers: {
14120
+ type: "object",
14121
+ description: "Optional request headers. Header names are lower-cased before send.",
14122
+ additionalProperties: { type: "string" }
14123
+ },
14124
+ query: {
14125
+ type: "object",
14126
+ description: "Optional query-string parameters appended to the URL.",
14127
+ additionalProperties: true
14128
+ },
14129
+ body: {
14130
+ description: "Request body. Objects are JSON-encoded with `content-type: application/json`; strings are sent verbatim with the caller-supplied content-type (defaulting to text/plain)."
14131
+ },
14132
+ timeoutMs: {
14133
+ type: "number",
14134
+ description: `Per-request timeout in milliseconds. Defaults to ${DEFAULT_TIMEOUT_MS}, capped at ${MAX_TIMEOUT_MS}.`
14135
+ }
14136
+ },
14137
+ required: ["url"]
14138
+ },
14139
+ cas: "none",
14140
+ externalEffect: true
14141
+ }
14142
+ ]
14143
+ },
14144
+ async executeRead(inv) {
14145
+ const args = inv.args ?? {};
14146
+ const method = readMethod(args.method, "GET", READ_METHODS, "request.fetch");
14147
+ const url = buildUrl(readUrl(args.url), args.query);
14148
+ const headers = mergeHeaders(args.headers, inv.idempotencyKey, false, void 0);
14149
+ const res = await fetch(url.toString(), {
14150
+ method,
14151
+ headers,
14152
+ signal: AbortSignal.timeout(readTimeout(args.timeoutMs))
14153
+ });
14154
+ const data = await parseResponse(res, method);
14155
+ if (!res.ok) {
14156
+ throw new Error(`http ${method} ${url.toString()} failed: ${res.status} ${truncate(stringify(data), 200)}`);
14157
+ }
14158
+ return {
14159
+ data,
14160
+ etag: res.headers.get("etag") ?? void 0,
14161
+ fetchedAt: Date.now()
14162
+ };
14163
+ },
14164
+ async executeMutation(inv) {
14165
+ const args = inv.args ?? {};
14166
+ const method = readMethod(args.method, "POST", MUTATION_METHODS, "request.send");
14167
+ const url = buildUrl(readUrl(args.url), args.query);
14168
+ const { body, contentType } = encodeBody(args.body);
14169
+ const headers = mergeHeaders(args.headers, inv.idempotencyKey, true, contentType);
14170
+ const res = await fetch(url.toString(), {
14171
+ method,
14172
+ headers,
14173
+ body,
14174
+ signal: AbortSignal.timeout(readTimeout(args.timeoutMs))
14175
+ });
14176
+ const data = await parseResponse(res, method);
14177
+ if (res.status === 409) {
14178
+ const message = typeof data === "object" && data !== null && "message" in data && typeof data.message === "string" ? data.message : `http ${method} ${url.toString()} returned 409`;
14179
+ const alternatives = typeof data === "object" && data !== null && Array.isArray(data.alternatives) ? data.alternatives : [];
14180
+ return { status: "conflict", alternatives, message, currentState: data };
14181
+ }
14182
+ if (res.status === 429) {
14183
+ const retryAfter = parseRetryAfterMs(res.headers.get("retry-after"));
14184
+ return {
14185
+ status: "rate-limited",
14186
+ retryAfterMs: retryAfter,
14187
+ message: `http ${method} ${url.toString()} rate-limited; retry after ${retryAfter}ms`
14188
+ };
14189
+ }
14190
+ if (!res.ok) {
14191
+ throw new Error(`http ${method} ${url.toString()} failed: ${res.status} ${truncate(stringify(data), 200)}`);
14192
+ }
14193
+ return {
14194
+ status: "committed",
14195
+ data,
14196
+ etagAfter: res.headers.get("etag") ?? void 0,
14197
+ committedAt: Date.now(),
14198
+ idempotentReplay: false
14199
+ };
14200
+ },
14201
+ async test() {
14202
+ return { ok: true };
14203
+ }
14204
+ };
14205
+ function readUrl(value) {
14206
+ if (typeof value !== "string" || value.length === 0) {
14207
+ throw new Error("http.url is required");
14208
+ }
14209
+ let parsed;
14210
+ try {
14211
+ parsed = new URL(value);
14212
+ } catch {
14213
+ throw new Error(`http.url is not a valid absolute URL: ${value}`);
14214
+ }
14215
+ if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
14216
+ throw new Error(`http.url scheme not allowed: ${parsed.protocol} (only http: and https: are accepted)`);
14217
+ }
14218
+ assertPublicHttpTarget(parsed, "http.url");
14219
+ return parsed;
14220
+ }
14221
+ function readMethod(value, fallback, allowed, capability) {
14222
+ if (value === void 0 || value === null) return fallback;
14223
+ if (typeof value !== "string") {
14224
+ throw new Error(`http.method must be a string for ${capability}`);
14225
+ }
14226
+ const upper = value.toUpperCase();
14227
+ if (!allowed.has(upper)) {
14228
+ throw new Error(
14229
+ `http.method ${upper} not allowed for ${capability} (allowed: ${[...allowed].sort().join(", ")})`
14230
+ );
14231
+ }
14232
+ return upper;
14233
+ }
14234
+ function readTimeout(value) {
14235
+ if (value === void 0 || value === null) return DEFAULT_TIMEOUT_MS;
14236
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
14237
+ throw new Error("http.timeoutMs must be a positive finite number");
14238
+ }
14239
+ return Math.min(Math.floor(value), MAX_TIMEOUT_MS);
14240
+ }
14241
+ function buildUrl(base, query) {
14242
+ if (query === void 0 || query === null) return base;
14243
+ if (typeof query !== "object") {
14244
+ throw new Error("http.query must be an object of string/number/boolean values (or arrays of those)");
14245
+ }
14246
+ const url = new URL(base.toString());
14247
+ for (const [key, raw] of Object.entries(query)) {
14248
+ if (raw === void 0 || raw === null) continue;
14249
+ if (Array.isArray(raw)) {
14250
+ for (const entry of raw) url.searchParams.append(key, scalarToString(entry, key));
14251
+ continue;
14252
+ }
14253
+ url.searchParams.set(key, scalarToString(raw, key));
14254
+ }
14255
+ return url;
14256
+ }
14257
+ function scalarToString(value, key) {
14258
+ if (typeof value === "string") return value;
14259
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
14260
+ throw new Error(`http.query.${key} must be string, number, boolean, or an array of those`);
14261
+ }
14262
+ function mergeHeaders(raw, idempotencyKey, isMutation, bodyContentType) {
14263
+ const headers = new Headers();
14264
+ headers.set("accept", "application/json, */*;q=0.1");
14265
+ if (raw !== void 0 && raw !== null) {
14266
+ if (typeof raw !== "object") {
14267
+ throw new Error("http.headers must be an object of string values");
14268
+ }
14269
+ for (const [name, value] of Object.entries(raw)) {
14270
+ if (value === void 0 || value === null) continue;
14271
+ if (typeof value !== "string") {
14272
+ throw new Error(`http.headers.${name} must be a string`);
14273
+ }
14274
+ headers.set(name.toLowerCase(), value);
14275
+ }
14276
+ }
14277
+ if (isMutation) {
14278
+ if (bodyContentType && !headers.has("content-type")) {
14279
+ headers.set("content-type", bodyContentType);
14280
+ }
14281
+ if (!headers.has("idempotency-key")) {
14282
+ headers.set("idempotency-key", idempotencyKey);
14283
+ }
14284
+ }
14285
+ return headers;
14286
+ }
14287
+ function encodeBody(raw) {
14288
+ if (raw === void 0 || raw === null) return { body: void 0, contentType: void 0 };
14289
+ if (typeof raw === "string") return { body: raw, contentType: "text/plain;charset=utf-8" };
14290
+ if (typeof raw === "number" || typeof raw === "boolean") {
14291
+ return { body: String(raw), contentType: "text/plain;charset=utf-8" };
14292
+ }
14293
+ if (raw instanceof ArrayBuffer) return { body: raw, contentType: "application/octet-stream" };
14294
+ if (typeof raw === "object") {
14295
+ return { body: JSON.stringify(raw), contentType: "application/json" };
14296
+ }
14297
+ throw new Error(`http.body must be string, number, boolean, object, or ArrayBuffer (got ${typeof raw})`);
14298
+ }
14299
+ async function parseResponse(res, method) {
14300
+ if (method === "HEAD") return null;
14301
+ const contentType = res.headers.get("content-type") ?? "";
14302
+ if (contentType.includes("application/json") || contentType.includes("+json")) {
14303
+ const text = await res.text();
14304
+ if (text.length === 0) return null;
14305
+ try {
14306
+ return JSON.parse(text);
14307
+ } catch {
14308
+ return text;
14309
+ }
14310
+ }
14311
+ return await res.text();
14312
+ }
14313
+ function parseRetryAfterMs(header2) {
14314
+ if (!header2) return 1e3;
14315
+ const seconds = Number(header2);
14316
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.floor(seconds * 1e3);
14317
+ const date = Date.parse(header2);
14318
+ if (!Number.isFinite(date)) return 1e3;
14319
+ return Math.max(0, date - Date.now());
14320
+ }
14321
+ function stringify(value) {
14322
+ if (typeof value === "string") return value;
14323
+ try {
14324
+ return JSON.stringify(value);
14325
+ } catch {
14326
+ return String(value);
14327
+ }
14328
+ }
14329
+ function truncate(value, limit) {
14330
+ return value.length > limit ? `${value.slice(0, limit)}\u2026` : value;
14331
+ }
14332
+
14333
+ // src/connectors/adapters/rss.ts
14334
+ var ALLOWED_SCHEMES2 = /* @__PURE__ */ new Set(["http:", "https:"]);
14335
+ var DEFAULT_LIMIT = 20;
14336
+ var MAX_LIMIT = 200;
14337
+ var DEFAULT_TIMEOUT_MS2 = 15e3;
14338
+ var MAX_BODY_BYTES = 5 * 1024 * 1024;
14339
+ var READ_CAPABILITIES = [
14340
+ {
14341
+ name: "feed.fetch",
14342
+ class: "read",
14343
+ description: "Fetch and parse the RSS/Atom feed URL pinned in DataSource.metadata.feedUrl. Returns normalized entries newest-first. Forwards the upstream ETag/Last-Modified so downstream conditional reads can avoid re-parsing unchanged feeds.",
14344
+ parameters: {
14345
+ type: "object",
14346
+ properties: {
14347
+ limit: {
14348
+ type: "number",
14349
+ description: `Maximum entries to return, newest first. Defaults to ${DEFAULT_LIMIT}, capped at ${MAX_LIMIT}.`
14350
+ },
14351
+ since: {
14352
+ type: "string",
14353
+ description: "ISO-8601 timestamp. When set, entries with `published` (or `updated` for Atom) strictly older than this are dropped."
14354
+ }
14355
+ }
14356
+ }
14357
+ },
14358
+ {
14359
+ name: "feed.lookup",
14360
+ class: "read",
14361
+ description: "Fetch and parse an arbitrary RSS/Atom feed URL the agent constructs at request time (e.g. search-result feeds, comment streams). Same normalization as `feed.fetch`.",
14362
+ parameters: {
14363
+ type: "object",
14364
+ properties: {
14365
+ url: { type: "string", description: "Absolute http(s) URL of the feed to fetch." },
14366
+ limit: {
14367
+ type: "number",
14368
+ description: `Maximum entries to return, newest first. Defaults to ${DEFAULT_LIMIT}, capped at ${MAX_LIMIT}.`
14369
+ },
14370
+ since: {
14371
+ type: "string",
14372
+ description: "ISO-8601 timestamp. When set, entries with `published` (or `updated` for Atom) strictly older than this are dropped."
14373
+ }
14374
+ },
14375
+ required: ["url"]
14376
+ }
14377
+ }
14378
+ ];
14379
+ var rssConnector = {
14380
+ manifest: {
14381
+ kind: "rss",
14382
+ displayName: "RSS / Atom Feed",
14383
+ description: "Pull entries from any public RSS 2.0 or Atom 1.0 feed. No auth required \u2014 the operator pins one feed URL at connect time (`feed.fetch`), and the agent can also resolve ad-hoc feed URLs at request time (`feed.lookup`). The connector parses both formats and emits a normalized entry shape so downstream tools do not have to branch on the feed dialect.",
14384
+ auth: { kind: "none" },
14385
+ category: "webhook",
14386
+ defaultConsistencyModel: "cache",
14387
+ capabilities: READ_CAPABILITIES
14388
+ },
14389
+ async executeRead(inv) {
14390
+ const args = inv.args ?? {};
14391
+ const url = inv.capabilityName === "feed.lookup" ? readUrl2(args.url, "feed.lookup.url") : readUrl2(readPinnedFeedUrl(inv.source), "DataSource.metadata.feedUrl");
14392
+ const limit = clampLimit2(args.limit);
14393
+ const since = readSinceMs(args.since);
14394
+ const res = await fetch(url.toString(), {
14395
+ method: "GET",
14396
+ headers: {
14397
+ accept: "application/rss+xml, application/atom+xml, application/xml;q=0.9, text/xml;q=0.8, */*;q=0.1",
14398
+ "user-agent": "tangle-agent-integrations-rss/1.0"
14399
+ },
14400
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS2)
14401
+ });
14402
+ if (!res.ok) {
14403
+ throw new Error(`rss fetch ${res.status} ${res.statusText} for ${url.toString()}`);
14404
+ }
14405
+ const body = await readBoundedText(res, MAX_BODY_BYTES);
14406
+ const parsed = parseFeed(body, url.toString());
14407
+ const filtered = filterAndLimit(parsed.entries, since, limit);
14408
+ const data = {
14409
+ feedTitle: parsed.feedTitle,
14410
+ feedUrl: url.toString(),
14411
+ feedUpdated: parsed.feedUpdated,
14412
+ entries: filtered,
14413
+ format: parsed.format
14414
+ };
14415
+ return {
14416
+ data,
14417
+ etag: res.headers.get("etag") ?? res.headers.get("last-modified") ?? void 0,
14418
+ fetchedAt: Date.now()
14419
+ };
14420
+ },
14421
+ async test(source) {
14422
+ const raw = source.metadata?.feedUrl;
14423
+ if (typeof raw !== "string" || raw.length === 0) {
14424
+ return { ok: true };
14425
+ }
14426
+ let url;
14427
+ try {
14428
+ url = new URL(raw);
14429
+ } catch {
14430
+ return { ok: false, reason: `feedUrl is not a valid absolute URL: ${raw}` };
14431
+ }
14432
+ if (!ALLOWED_SCHEMES2.has(url.protocol)) {
14433
+ return { ok: false, reason: `feedUrl scheme not allowed: ${url.protocol}` };
14434
+ }
14435
+ try {
14436
+ const res = await fetch(url.toString(), {
14437
+ method: "HEAD",
14438
+ headers: { "user-agent": "tangle-agent-integrations-rss/1.0" },
14439
+ signal: AbortSignal.timeout(8e3)
14440
+ });
14441
+ if (res.status >= 500) return { ok: false, reason: `feedUrl returned ${res.status}` };
14442
+ return { ok: true };
14443
+ } catch (err) {
14444
+ return { ok: false, reason: err instanceof Error ? err.message : String(err) };
14445
+ }
14446
+ }
14447
+ };
14448
+ function readPinnedFeedUrl(source) {
14449
+ return source.metadata?.feedUrl;
14450
+ }
14451
+ function readUrl2(value, label) {
14452
+ if (typeof value !== "string" || value.length === 0) {
14453
+ throw new Error(`${label} is required`);
14454
+ }
14455
+ let parsed;
14456
+ try {
14457
+ parsed = new URL(value);
14458
+ } catch {
14459
+ throw new Error(`${label} is not a valid absolute URL: ${value}`);
14460
+ }
14461
+ if (!ALLOWED_SCHEMES2.has(parsed.protocol)) {
14462
+ throw new Error(`${label} scheme not allowed: ${parsed.protocol} (only http: and https: are accepted)`);
14463
+ }
14464
+ assertPublicHttpTarget(parsed, label);
14465
+ return parsed;
14466
+ }
14467
+ function clampLimit2(value) {
14468
+ if (value === void 0 || value === null) return DEFAULT_LIMIT;
14469
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
14470
+ throw new Error("rss.limit must be a positive finite number");
14471
+ }
14472
+ return Math.min(Math.floor(value), MAX_LIMIT);
14473
+ }
14474
+ function readSinceMs(value) {
14475
+ if (value === void 0 || value === null) return null;
14476
+ if (typeof value !== "string") {
14477
+ throw new Error("rss.since must be an ISO-8601 string");
14478
+ }
14479
+ const ms = Date.parse(value);
14480
+ if (!Number.isFinite(ms)) {
14481
+ throw new Error(`rss.since is not a valid date: ${value}`);
14482
+ }
14483
+ return ms;
14484
+ }
14485
+ async function readBoundedText(res, maxBytes) {
14486
+ const reader = res.body?.getReader();
14487
+ if (!reader) return await res.text();
14488
+ const decoder = new TextDecoder("utf-8", { fatal: false });
14489
+ let out = "";
14490
+ let total = 0;
14491
+ while (true) {
14492
+ const { value, done } = await reader.read();
14493
+ if (done) break;
14494
+ if (!value) continue;
14495
+ total += value.byteLength;
14496
+ if (total > maxBytes) {
14497
+ try {
14498
+ await reader.cancel();
14499
+ } catch {
14500
+ }
14501
+ throw new Error(`rss feed body exceeded ${maxBytes} bytes`);
14502
+ }
14503
+ out += decoder.decode(value, { stream: true });
14504
+ }
14505
+ out += decoder.decode();
14506
+ return out;
14507
+ }
14508
+ function filterAndLimit(entries, sinceMs, limit) {
14509
+ let filtered = entries;
14510
+ if (sinceMs !== null) {
14511
+ filtered = entries.filter((entry) => {
14512
+ const ts = entry.published ?? entry.updated;
14513
+ if (!ts) return true;
14514
+ const ms = Date.parse(ts);
14515
+ if (!Number.isFinite(ms)) return true;
14516
+ return ms >= sinceMs;
14517
+ });
14518
+ }
14519
+ return filtered.slice(0, limit);
14520
+ }
14521
+ function parseFeed(body, feedUrl) {
14522
+ const text = body.replace(/^/, "").trimStart();
14523
+ const rssIdx = text.search(/<rss\b/i);
14524
+ const atomIdx = text.search(/<feed\b/i);
14525
+ if (rssIdx === -1 && atomIdx === -1) {
14526
+ return { feedTitle: null, feedUpdated: null, entries: [], format: "unknown" };
14527
+ }
14528
+ if (atomIdx !== -1 && (rssIdx === -1 || atomIdx < rssIdx)) {
14529
+ return parseAtom(text, feedUrl);
14530
+ }
14531
+ return parseRss(text);
14532
+ }
14533
+ function parseRss(text) {
14534
+ const channelOuter = extractFirstElement(text, "channel");
14535
+ if (!channelOuter) {
14536
+ return { feedTitle: null, feedUpdated: null, entries: [], format: "rss" };
14537
+ }
14538
+ const channelInner = stripAllElements(channelOuter, "item");
14539
+ const feedTitle = decodeXml(firstTagText(channelInner, "title"));
14540
+ const items = collectElements(channelOuter, "item");
14541
+ const entries = items.map((item) => {
14542
+ const guid = firstTagText(item, "guid");
14543
+ const link = firstTagText(item, "link");
14544
+ const title = firstTagText(item, "title");
14545
+ const description = firstTagText(item, "description");
14546
+ const contentEncoded = firstTagText(item, "content:encoded");
14547
+ const pubDate = firstTagText(item, "pubDate");
14548
+ const author = firstTagText(item, "author") ?? firstTagText(item, "dc:creator");
14549
+ const categories = collectElements(item, "category").map((cat) => decodeXml(stripTags(cat)) ?? "").filter(Boolean);
14550
+ const id = decodeXml(guid) ?? decodeXml(link) ?? hash(item);
14551
+ return {
14552
+ id,
14553
+ title: decodeXml(title),
14554
+ link: decodeXml(link),
14555
+ summary: decodeXml(description),
14556
+ content: decodeXml(contentEncoded),
14557
+ published: normalizeDate(decodeXml(pubDate)),
14558
+ updated: null,
14559
+ author: decodeXml(author),
14560
+ categories
14561
+ };
14562
+ });
14563
+ return { feedTitle, feedUpdated: null, entries, format: "rss" };
14564
+ }
14565
+ function parseAtom(text, feedUrl) {
14566
+ const feedOuter = extractFirstElement(text, "feed") ?? text;
14567
+ const feedInner = stripAllElements(feedOuter, "entry");
14568
+ const feedTitle = decodeXml(firstTagText(feedInner, "title"));
14569
+ const feedUpdated = normalizeDate(decodeXml(firstTagText(feedInner, "updated")));
14570
+ const items = collectElements(feedOuter, "entry");
14571
+ const entries = items.map((item) => {
14572
+ const id = decodeXml(firstTagText(item, "id"));
14573
+ const title = decodeXml(firstTagText(item, "title"));
14574
+ const summary = decodeXml(firstTagText(item, "summary"));
14575
+ const contentNode = firstTagText(item, "content");
14576
+ const published = normalizeDate(decodeXml(firstTagText(item, "published")));
14577
+ const updated = normalizeDate(decodeXml(firstTagText(item, "updated")));
14578
+ const author = decodeXml(firstTagText(extractFirstElement(item, "author") ?? "", "name")) ?? decodeXml(firstTagText(item, "author"));
14579
+ const link = pickAtomLink(item);
14580
+ const categories = collectAtomCategories(item);
14581
+ return {
14582
+ id: id ?? link ?? hash(item),
14583
+ title,
14584
+ link,
14585
+ summary,
14586
+ content: decodeXml(contentNode),
14587
+ published,
14588
+ updated,
14589
+ author,
14590
+ categories
14591
+ };
14592
+ });
14593
+ return { feedTitle, feedUpdated, entries, format: "atom" };
14594
+ }
14595
+ function extractFirstElement(text, tag) {
14596
+ const escaped = escapeRegex(tag);
14597
+ const openRe = new RegExp(`<${escaped}\\b([^>]*)>`, "i");
14598
+ const m = openRe.exec(text);
14599
+ if (!m) return null;
14600
+ const openEnd = m.index + m[0].length;
14601
+ if (m[0].endsWith("/>")) return m[0];
14602
+ const closeRe = new RegExp(`</${escaped}\\s*>`, "gi");
14603
+ closeRe.lastIndex = openEnd;
14604
+ const close = closeRe.exec(text);
14605
+ if (!close) return text.slice(m.index);
14606
+ return text.slice(m.index, close.index + close[0].length);
14607
+ }
14608
+ function collectElements(text, tag) {
14609
+ const escaped = escapeRegex(tag);
14610
+ const out = [];
14611
+ const openRe = new RegExp(`<${escaped}\\b([^>]*)>`, "gi");
14612
+ let m;
14613
+ while ((m = openRe.exec(text)) !== null) {
14614
+ const start = m.index;
14615
+ if (m[0].endsWith("/>")) {
14616
+ out.push(m[0]);
14617
+ continue;
14618
+ }
14619
+ const after = m.index + m[0].length;
14620
+ const closeRe = new RegExp(`</${escaped}\\s*>`, "gi");
14621
+ closeRe.lastIndex = after;
14622
+ const close = closeRe.exec(text);
14623
+ if (!close) break;
14624
+ out.push(text.slice(start, close.index + close[0].length));
14625
+ openRe.lastIndex = close.index + close[0].length;
14626
+ }
14627
+ return out;
14628
+ }
14629
+ function stripAllElements(text, tag) {
14630
+ const escaped = escapeRegex(tag);
14631
+ let out = text;
14632
+ const re = new RegExp(`<${escaped}\\b[^>]*>[\\s\\S]*?</${escaped}\\s*>`, "gi");
14633
+ out = out.replace(re, "");
14634
+ const selfRe = new RegExp(`<${escaped}\\b[^>]*/>`, "gi");
14635
+ out = out.replace(selfRe, "");
14636
+ return out;
14637
+ }
14638
+ function firstTagText(text, tag) {
14639
+ const escaped = escapeRegex(tag);
14640
+ const re = new RegExp(`<${escaped}\\b([^>]*)>([\\s\\S]*?)</${escaped}\\s*>`, "i");
14641
+ const m = re.exec(text);
14642
+ if (m) return unwrapCdata(m[2]);
14643
+ const selfRe = new RegExp(`<${escaped}\\b([^>]*)/>`, "i");
14644
+ const sm = selfRe.exec(text);
14645
+ if (sm) return null;
14646
+ return null;
14647
+ }
14648
+ function pickAtomLink(entryOuter) {
14649
+ const linkRe = /<link\b([^>]*)\/?>(?:([\s\S]*?)<\/link\s*>)?/gi;
14650
+ let bestHref = null;
14651
+ let fallback = null;
14652
+ let m;
14653
+ while ((m = linkRe.exec(entryOuter)) !== null) {
14654
+ const attrs = m[1] ?? "";
14655
+ const href = readAttr(attrs, "href");
14656
+ if (!href) continue;
14657
+ const rel = readAttr(attrs, "rel");
14658
+ if (!fallback) fallback = href;
14659
+ if (!rel || rel === "alternate") {
14660
+ bestHref = href;
14661
+ break;
14662
+ }
14663
+ }
14664
+ return bestHref ?? fallback;
14665
+ }
14666
+ function collectAtomCategories(entryOuter) {
14667
+ const out = [];
14668
+ const re = /<category\b([^>]*)(?:\/>|>([\s\S]*?)<\/category\s*>)/gi;
14669
+ let m;
14670
+ while ((m = re.exec(entryOuter)) !== null) {
14671
+ const term = readAttr(m[1] ?? "", "term");
14672
+ if (term) out.push(decodeXml(term) ?? "");
14673
+ else if (m[2]) {
14674
+ const inner = decodeXml(stripTags(m[2]));
14675
+ if (inner) out.push(inner);
14676
+ }
14677
+ }
14678
+ return out.filter(Boolean);
14679
+ }
14680
+ function readAttr(attrs, name) {
14681
+ const re = new RegExp(`\\b${escapeRegex(name)}\\s*=\\s*("([^"]*)"|'([^']*)')`, "i");
14682
+ const m = re.exec(attrs);
14683
+ if (!m) return null;
14684
+ return m[2] ?? m[3] ?? null;
14685
+ }
14686
+ function stripTags(value) {
14687
+ return value.replace(/<[^>]*>/g, "");
14688
+ }
14689
+ function unwrapCdata(value) {
14690
+ const cdata = /<!\[CDATA\[([\s\S]*?)\]\]>/g;
14691
+ let out = "";
14692
+ let lastIdx = 0;
14693
+ let m;
14694
+ while ((m = cdata.exec(value)) !== null) {
14695
+ out += value.slice(lastIdx, m.index);
14696
+ out += m[1];
14697
+ lastIdx = m.index + m[0].length;
14698
+ }
14699
+ out += value.slice(lastIdx);
14700
+ return out;
14701
+ }
14702
+ function decodeXml(value) {
14703
+ if (value === null) return null;
14704
+ const trimmed = value.trim();
14705
+ if (trimmed.length === 0) return null;
14706
+ return trimmed.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, d) => {
14707
+ const code = Number(d);
14708
+ return Number.isFinite(code) ? String.fromCodePoint(code) : _;
14709
+ }).replace(/&#x([0-9a-fA-F]+);/g, (_, h) => {
14710
+ const code = parseInt(h, 16);
14711
+ return Number.isFinite(code) ? String.fromCodePoint(code) : _;
14712
+ }).replace(/&amp;/g, "&");
14713
+ }
14714
+ function normalizeDate(value) {
14715
+ if (!value) return null;
14716
+ const ms = Date.parse(value);
14717
+ if (!Number.isFinite(ms)) return null;
14718
+ return new Date(ms).toISOString();
14719
+ }
14720
+ function escapeRegex(value) {
14721
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14722
+ }
14723
+ function hash(value) {
14724
+ let h = 2166136261;
14725
+ for (let i = 0; i < value.length; i++) {
14726
+ h ^= value.charCodeAt(i);
14727
+ h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
14728
+ }
14729
+ return `rss-${h.toString(16).padStart(8, "0")}`;
14730
+ }
14731
+
14732
+ // src/connectors/adapters/amazon-s3.ts
14733
+ var amazonS3Connector = declarativeRestConnector({
14734
+ kind: "amazon-s3",
14735
+ displayName: "Amazon S3",
14736
+ description: "Scalable storage in the cloud. Read, upload, delete, and manage files in S3 buckets.",
14737
+ auth: {
14738
+ kind: "api-key",
14739
+ hint: 'AWS credentials as JSON: {"accessKeyId":"AKIA\u2026","secretAccessKey":"\u2026","region":"us-east-1"} for an IAM principal with S3 permissions. Optional "sessionToken" and "endpoint" (S3-compatible stores). Requests are signed with AWS Signature V4; the region selects the s3.<region>.amazonaws.com endpoint.'
14740
+ },
14741
+ category: "storage",
14742
+ defaultConsistencyModel: "authoritative",
14743
+ // S3 REST API, path-style. Each request is signed with AWS Signature V4
14744
+ // (service `s3`, which signs the request path verbatim); the bundle's region
14745
+ // is substituted into the `{region}` host template.
14746
+ credentialPlacement: { kind: "aws-sigv4", service: "s3" },
14747
+ baseUrl: "https://s3.{region}.amazonaws.com",
14748
+ test: { method: "GET", path: "/" },
14749
+ capabilities: [
14750
+ {
14751
+ name: "files.list",
14752
+ class: "read",
14753
+ description: "List files in an S3 bucket folder.",
14754
+ parameters: {
14755
+ type: "object",
14756
+ properties: {
14757
+ prefix: { type: "string", description: "Folder path to filter results (e.g., docs/)" },
14758
+ maxKeys: { type: "integer", description: "Maximum number of files to return (1\u20131000)" }
14759
+ },
14760
+ required: []
14761
+ },
14762
+ request: {
14763
+ method: "GET",
14764
+ path: "/",
14765
+ query: { prefix: "{prefix}", "max-keys": "{maxKeys}" }
14766
+ }
14767
+ },
14768
+ {
14769
+ name: "files.read",
14770
+ class: "read",
14771
+ description: "Read a file from S3.",
14772
+ parameters: {
14773
+ type: "object",
14774
+ properties: {
14775
+ key: { type: "string", description: "The full path to the file in S3" }
14776
+ },
14777
+ required: ["key"]
14778
+ },
14779
+ request: {
14780
+ method: "GET",
14781
+ path: "/{key}"
14782
+ }
14783
+ },
14784
+ {
14785
+ name: "files.upload",
14786
+ class: "mutation",
14787
+ description: "Upload a file to S3.",
14788
+ parameters: {
14789
+ type: "object",
14790
+ properties: {
14791
+ key: { type: "string", description: "The file path and name in S3" },
14792
+ contentType: { type: "string", description: "MIME type of the file" }
14793
+ },
14794
+ required: ["key"]
14795
+ },
14796
+ request: {
14797
+ method: "PUT",
14798
+ path: "/{key}",
14799
+ headers: { "Content-Type": "{contentType}" }
14800
+ },
14801
+ cas: "native-idempotency"
14802
+ },
14803
+ {
14804
+ name: "files.delete",
14805
+ class: "mutation",
14806
+ description: "Delete a file from S3.",
14807
+ parameters: {
14808
+ type: "object",
14809
+ properties: {
14810
+ key: { type: "string", description: "The full path to the file to delete" }
14811
+ },
14812
+ required: ["key"]
14813
+ },
14814
+ request: {
14815
+ method: "DELETE",
14816
+ path: "/{key}"
14817
+ },
14818
+ cas: "optimistic-read-verify"
14819
+ },
14820
+ {
14821
+ name: "files.generateSignedUrl",
14822
+ class: "read",
14823
+ description: "Generate a signed URL for temporary access to a file.",
14824
+ parameters: {
14825
+ type: "object",
14826
+ properties: {
14827
+ key: { type: "string", description: "The file path in S3" },
14828
+ expiresIn: { type: "integer", description: "URL validity duration in minutes" }
14829
+ },
14830
+ required: ["key", "expiresIn"]
14831
+ },
14832
+ request: {
14833
+ method: "GET",
14834
+ path: "/{key}",
14835
+ query: { "X-Amz-Expires": "{expiresIn}" }
14836
+ }
14837
+ },
14838
+ {
14839
+ name: "files.moveFile",
14840
+ class: "mutation",
14841
+ description: "Move a file to a different location in S3.",
14842
+ parameters: {
14843
+ type: "object",
14844
+ properties: {
14845
+ sourceKey: { type: "string", description: "Current file path" },
14846
+ destinationKey: { type: "string", description: "New file path" }
14847
+ },
14848
+ required: ["sourceKey", "destinationKey"]
14849
+ },
14850
+ request: {
14851
+ method: "PUT",
14852
+ path: "/{destinationKey}",
14853
+ headers: { "x-amz-copy-source": "{sourceKey}" }
14854
+ },
14855
+ cas: "optimistic-read-verify"
14856
+ },
14857
+ {
14858
+ name: "files.copyFile",
14859
+ class: "mutation",
14860
+ description: "Server-side copy of an object to a new key. Source is left in place.",
14861
+ parameters: {
14862
+ type: "object",
14863
+ properties: {
14864
+ sourceKey: {
14865
+ type: "string",
14866
+ description: "Existing object identifier as {bucket}/{key}."
14867
+ },
14868
+ destinationKey: {
14869
+ type: "string",
14870
+ description: "New object key (relative to the destination bucket)."
14871
+ }
14872
+ },
14873
+ required: ["sourceKey", "destinationKey"]
14874
+ },
14875
+ request: {
14876
+ method: "PUT",
14877
+ path: "/{destinationKey}",
14878
+ headers: { "x-amz-copy-source": "{sourceKey}" }
14879
+ },
14880
+ cas: "native-idempotency",
14881
+ externalEffect: true
14882
+ },
14883
+ {
14884
+ name: "files.setMetadata",
14885
+ class: "mutation",
14886
+ description: "Replace object metadata in place via the S3 copy-self pattern (x-amz-metadata-directive: REPLACE).",
14887
+ parameters: {
14888
+ type: "object",
14889
+ properties: {
14890
+ key: { type: "string", description: "Object key whose metadata should be replaced." },
14891
+ contentType: { type: "string", description: "New Content-Type for the object." },
14892
+ metadata: {
14893
+ type: "string",
14894
+ description: "Serialized x-amz-meta-* JSON forwarded as the x-amz-meta-user header (caller-controlled)."
14895
+ }
14896
+ },
14897
+ required: ["key"]
14898
+ },
14899
+ request: {
14900
+ method: "PUT",
14901
+ path: "/{key}",
14902
+ headers: {
14903
+ "x-amz-copy-source": "{key}",
14904
+ "x-amz-metadata-directive": "REPLACE",
14905
+ "Content-Type": "{contentType}",
14906
+ "x-amz-meta-user": "{metadata}"
14907
+ }
14908
+ },
14909
+ cas: "native-idempotency",
14910
+ externalEffect: true
14911
+ },
14912
+ {
14913
+ name: "files.createBucket",
14914
+ class: "mutation",
14915
+ description: "Create a new bucket in the configured region. Request is a PUT against the bucket name path.",
14916
+ parameters: {
14917
+ type: "object",
14918
+ properties: {
14919
+ bucket: { type: "string", description: "Bucket name to create." },
14920
+ region: {
14921
+ type: "string",
14922
+ description: "AWS region for the bucket (sent via x-amz-bucket-region)."
14923
+ }
14924
+ },
14925
+ required: ["bucket"]
14926
+ },
14927
+ request: {
14928
+ method: "PUT",
14929
+ path: "/{bucket}",
14930
+ headers: { "x-amz-bucket-region": "{region}" }
14931
+ },
14932
+ cas: "native-idempotency",
14933
+ externalEffect: true
14934
+ }
14935
+ ]
14936
+ });
14937
+
14011
14938
  // src/connectors/adapters/stripe-webhook-receiver.ts
14012
14939
  var stripeWebhookReceiverConnector = {
14013
14940
  manifest: {
@@ -93359,6 +94286,881 @@ var granolaConnector = declarativeRestConnector({
93359
94286
  ]
93360
94287
  });
93361
94288
 
94289
+ // src/connectors/adapters/zoom.ts
94290
+ var zoomConnector = declarativeRestConnector({
94291
+ kind: "zoom",
94292
+ displayName: "Zoom",
94293
+ description: "Create and manage Zoom meetings, webinars, users, and cloud recordings via the Zoom API v2.",
94294
+ auth: {
94295
+ kind: "oauth2",
94296
+ authorizationUrl: "https://zoom.us/oauth/authorize",
94297
+ tokenUrl: "https://zoom.us/oauth/token",
94298
+ scopes: [
94299
+ "user:read:user",
94300
+ "user:write:user",
94301
+ "meeting:read:meeting",
94302
+ "meeting:write:meeting",
94303
+ "meeting:update:meeting",
94304
+ "meeting:delete:meeting",
94305
+ "webinar:read:webinar",
94306
+ "webinar:write:webinar",
94307
+ "webinar:update:webinar",
94308
+ "webinar:delete:webinar",
94309
+ "recording:read:recording",
94310
+ "recording:write:recording"
94311
+ ],
94312
+ clientIdEnv: "ZOOM_OAUTH_CLIENT_ID",
94313
+ clientSecretEnv: "ZOOM_OAUTH_CLIENT_SECRET"
94314
+ },
94315
+ category: "calendar",
94316
+ defaultConsistencyModel: "authoritative",
94317
+ baseUrl: "https://api.zoom.us",
94318
+ test: { method: "GET", path: "/v2/users/me" },
94319
+ capabilities: [
94320
+ {
94321
+ name: "users.get",
94322
+ class: "read",
94323
+ description: 'Read a Zoom user by id, email, or the "me" alias for the authenticated user.',
94324
+ parameters: {
94325
+ type: "object",
94326
+ properties: {
94327
+ userId: { type: "string", description: 'User id, email address, or "me".' }
94328
+ },
94329
+ required: ["userId"]
94330
+ },
94331
+ request: { method: "GET", path: "/v2/users/{userId}" },
94332
+ requiredScopes: ["user:read:user"]
94333
+ },
94334
+ {
94335
+ name: "users.list",
94336
+ class: "read",
94337
+ description: "List users on the Zoom account.",
94338
+ parameters: {
94339
+ type: "object",
94340
+ properties: {
94341
+ status: { type: "string", enum: ["active", "inactive", "pending"] },
94342
+ page_size: { type: "integer", minimum: 1, maximum: 300 },
94343
+ next_page_token: { type: "string" },
94344
+ role_id: { type: "string" }
94345
+ }
94346
+ },
94347
+ request: {
94348
+ method: "GET",
94349
+ path: "/v2/users",
94350
+ query: {
94351
+ status: "{status}",
94352
+ page_size: "{page_size}",
94353
+ next_page_token: "{next_page_token}",
94354
+ role_id: "{role_id}"
94355
+ }
94356
+ },
94357
+ requiredScopes: ["user:read:user"]
94358
+ },
94359
+ {
94360
+ name: "meetings.list",
94361
+ class: "read",
94362
+ description: "List meetings for a user (scheduled, live, or upcoming).",
94363
+ parameters: {
94364
+ type: "object",
94365
+ properties: {
94366
+ userId: { type: "string", description: 'User id, email, or "me".' },
94367
+ type: {
94368
+ type: "string",
94369
+ enum: ["scheduled", "live", "upcoming", "upcoming_meetings", "previous_meetings"],
94370
+ description: "Subset of meetings to return; defaults to scheduled."
94371
+ },
94372
+ page_size: { type: "integer", minimum: 1, maximum: 300 },
94373
+ next_page_token: { type: "string" }
94374
+ },
94375
+ required: ["userId"]
94376
+ },
94377
+ request: {
94378
+ method: "GET",
94379
+ path: "/v2/users/{userId}/meetings",
94380
+ query: {
94381
+ type: "{type}",
94382
+ page_size: "{page_size}",
94383
+ next_page_token: "{next_page_token}"
94384
+ }
94385
+ },
94386
+ requiredScopes: ["meeting:read:meeting"]
94387
+ },
94388
+ {
94389
+ name: "meetings.get",
94390
+ class: "read",
94391
+ description: "Read a Zoom meeting by numeric meeting id.",
94392
+ parameters: {
94393
+ type: "object",
94394
+ properties: {
94395
+ meetingId: { type: "string", description: "Numeric Zoom meeting id." },
94396
+ occurrence_id: { type: "string", description: "Optional recurring-meeting occurrence id." }
94397
+ },
94398
+ required: ["meetingId"]
94399
+ },
94400
+ request: {
94401
+ method: "GET",
94402
+ path: "/v2/meetings/{meetingId}",
94403
+ query: { occurrence_id: "{occurrence_id}" }
94404
+ },
94405
+ requiredScopes: ["meeting:read:meeting"]
94406
+ },
94407
+ {
94408
+ name: "meetings.create",
94409
+ class: "mutation",
94410
+ description: "Schedule a Zoom meeting for a user. Body follows the v2 meetings contract (topic, start_time, duration, type, settings, ...).",
94411
+ parameters: {
94412
+ type: "object",
94413
+ properties: {
94414
+ userId: { type: "string", description: 'User id, email, or "me".' },
94415
+ topic: { type: "string" },
94416
+ type: {
94417
+ type: "integer",
94418
+ enum: [1, 2, 3, 8],
94419
+ description: "1=instant, 2=scheduled, 3=recurring no fixed time, 8=recurring with fixed time."
94420
+ },
94421
+ start_time: { type: "string", description: "ISO-8601 start time (UTC or with offset)." },
94422
+ duration: { type: "integer", description: "Meeting length in minutes." },
94423
+ timezone: { type: "string" },
94424
+ password: { type: "string" },
94425
+ agenda: { type: "string" },
94426
+ settings: { type: "object" },
94427
+ recurrence: { type: "object" },
94428
+ tracking_fields: { type: "array", items: { type: "object" } }
94429
+ },
94430
+ required: ["userId", "topic"]
94431
+ },
94432
+ request: {
94433
+ method: "POST",
94434
+ path: "/v2/users/{userId}/meetings",
94435
+ body: {
94436
+ topic: "{topic}",
94437
+ type: "{type}",
94438
+ start_time: "{start_time}",
94439
+ duration: "{duration}",
94440
+ timezone: "{timezone}",
94441
+ password: "{password}",
94442
+ agenda: "{agenda}",
94443
+ settings: "{settings}",
94444
+ recurrence: "{recurrence}",
94445
+ tracking_fields: "{tracking_fields}"
94446
+ }
94447
+ },
94448
+ cas: "none",
94449
+ externalEffect: true,
94450
+ requiredScopes: ["meeting:write:meeting"]
94451
+ },
94452
+ {
94453
+ name: "meetings.update",
94454
+ class: "mutation",
94455
+ description: "Update a scheduled Zoom meeting in place.",
94456
+ parameters: {
94457
+ type: "object",
94458
+ properties: {
94459
+ meetingId: { type: "string" },
94460
+ occurrence_id: { type: "string" },
94461
+ topic: { type: "string" },
94462
+ start_time: { type: "string" },
94463
+ duration: { type: "integer" },
94464
+ timezone: { type: "string" },
94465
+ password: { type: "string" },
94466
+ agenda: { type: "string" },
94467
+ settings: { type: "object" },
94468
+ recurrence: { type: "object" }
94469
+ },
94470
+ required: ["meetingId"]
94471
+ },
94472
+ request: {
94473
+ method: "PATCH",
94474
+ path: "/v2/meetings/{meetingId}",
94475
+ query: { occurrence_id: "{occurrence_id}" },
94476
+ body: {
94477
+ topic: "{topic}",
94478
+ start_time: "{start_time}",
94479
+ duration: "{duration}",
94480
+ timezone: "{timezone}",
94481
+ password: "{password}",
94482
+ agenda: "{agenda}",
94483
+ settings: "{settings}",
94484
+ recurrence: "{recurrence}"
94485
+ }
94486
+ },
94487
+ cas: "native-idempotency",
94488
+ requiredScopes: ["meeting:update:meeting"]
94489
+ },
94490
+ {
94491
+ name: "meetings.delete",
94492
+ class: "mutation",
94493
+ description: "Delete a Zoom meeting. Optionally notify hosts/registrants and scope to a single occurrence.",
94494
+ parameters: {
94495
+ type: "object",
94496
+ properties: {
94497
+ meetingId: { type: "string" },
94498
+ occurrence_id: { type: "string" },
94499
+ schedule_for_reminder: { type: "boolean" },
94500
+ cancel_meeting_reminder: { type: "boolean" }
94501
+ },
94502
+ required: ["meetingId"]
94503
+ },
94504
+ request: {
94505
+ method: "DELETE",
94506
+ path: "/v2/meetings/{meetingId}",
94507
+ query: {
94508
+ occurrence_id: "{occurrence_id}",
94509
+ schedule_for_reminder: "{schedule_for_reminder}",
94510
+ cancel_meeting_reminder: "{cancel_meeting_reminder}"
94511
+ }
94512
+ },
94513
+ cas: "native-idempotency",
94514
+ externalEffect: true,
94515
+ requiredScopes: ["meeting:delete:meeting"]
94516
+ },
94517
+ {
94518
+ name: "meetings.list-registrants",
94519
+ class: "read",
94520
+ description: "List registrants for a Zoom meeting.",
94521
+ parameters: {
94522
+ type: "object",
94523
+ properties: {
94524
+ meetingId: { type: "string" },
94525
+ occurrence_id: { type: "string" },
94526
+ status: { type: "string", enum: ["pending", "approved", "denied"] },
94527
+ page_size: { type: "integer", minimum: 1, maximum: 300 },
94528
+ next_page_token: { type: "string" }
94529
+ },
94530
+ required: ["meetingId"]
94531
+ },
94532
+ request: {
94533
+ method: "GET",
94534
+ path: "/v2/meetings/{meetingId}/registrants",
94535
+ query: {
94536
+ occurrence_id: "{occurrence_id}",
94537
+ status: "{status}",
94538
+ page_size: "{page_size}",
94539
+ next_page_token: "{next_page_token}"
94540
+ }
94541
+ },
94542
+ requiredScopes: ["meeting:read:meeting"]
94543
+ },
94544
+ {
94545
+ name: "meetings.add-registrant",
94546
+ class: "mutation",
94547
+ description: "Register a participant for a Zoom meeting that requires registration.",
94548
+ parameters: {
94549
+ type: "object",
94550
+ properties: {
94551
+ meetingId: { type: "string" },
94552
+ occurrence_ids: { type: "string", description: "Comma-separated occurrence ids for recurring meetings." },
94553
+ email: { type: "string" },
94554
+ first_name: { type: "string" },
94555
+ last_name: { type: "string" },
94556
+ address: { type: "string" },
94557
+ city: { type: "string" },
94558
+ country: { type: "string" },
94559
+ zip: { type: "string" },
94560
+ state: { type: "string" },
94561
+ phone: { type: "string" },
94562
+ industry: { type: "string" },
94563
+ org: { type: "string" },
94564
+ job_title: { type: "string" },
94565
+ purchasing_time_frame: { type: "string" },
94566
+ role_in_purchase_process: { type: "string" },
94567
+ no_of_employees: { type: "string" },
94568
+ comments: { type: "string" },
94569
+ custom_questions: { type: "array", items: { type: "object" } },
94570
+ language: { type: "string" },
94571
+ auto_approve: { type: "boolean" }
94572
+ },
94573
+ required: ["meetingId", "email", "first_name"]
94574
+ },
94575
+ request: {
94576
+ method: "POST",
94577
+ path: "/v2/meetings/{meetingId}/registrants",
94578
+ query: { occurrence_ids: "{occurrence_ids}" },
94579
+ body: {
94580
+ email: "{email}",
94581
+ first_name: "{first_name}",
94582
+ last_name: "{last_name}",
94583
+ address: "{address}",
94584
+ city: "{city}",
94585
+ country: "{country}",
94586
+ zip: "{zip}",
94587
+ state: "{state}",
94588
+ phone: "{phone}",
94589
+ industry: "{industry}",
94590
+ org: "{org}",
94591
+ job_title: "{job_title}",
94592
+ purchasing_time_frame: "{purchasing_time_frame}",
94593
+ role_in_purchase_process: "{role_in_purchase_process}",
94594
+ no_of_employees: "{no_of_employees}",
94595
+ comments: "{comments}",
94596
+ custom_questions: "{custom_questions}",
94597
+ language: "{language}",
94598
+ auto_approve: "{auto_approve}"
94599
+ }
94600
+ },
94601
+ cas: "native-idempotency",
94602
+ requiredScopes: ["meeting:write:meeting"]
94603
+ },
94604
+ {
94605
+ name: "webinars.list",
94606
+ class: "read",
94607
+ description: "List webinars for a user.",
94608
+ parameters: {
94609
+ type: "object",
94610
+ properties: {
94611
+ userId: { type: "string" },
94612
+ page_size: { type: "integer", minimum: 1, maximum: 300 },
94613
+ next_page_token: { type: "string" }
94614
+ },
94615
+ required: ["userId"]
94616
+ },
94617
+ request: {
94618
+ method: "GET",
94619
+ path: "/v2/users/{userId}/webinars",
94620
+ query: {
94621
+ page_size: "{page_size}",
94622
+ next_page_token: "{next_page_token}"
94623
+ }
94624
+ },
94625
+ requiredScopes: ["webinar:read:webinar"]
94626
+ },
94627
+ {
94628
+ name: "webinars.get",
94629
+ class: "read",
94630
+ description: "Read a Zoom webinar by id.",
94631
+ parameters: {
94632
+ type: "object",
94633
+ properties: {
94634
+ webinarId: { type: "string" },
94635
+ occurrence_id: { type: "string" }
94636
+ },
94637
+ required: ["webinarId"]
94638
+ },
94639
+ request: {
94640
+ method: "GET",
94641
+ path: "/v2/webinars/{webinarId}",
94642
+ query: { occurrence_id: "{occurrence_id}" }
94643
+ },
94644
+ requiredScopes: ["webinar:read:webinar"]
94645
+ },
94646
+ {
94647
+ name: "webinars.create",
94648
+ class: "mutation",
94649
+ description: "Schedule a Zoom webinar for a user.",
94650
+ parameters: {
94651
+ type: "object",
94652
+ properties: {
94653
+ userId: { type: "string" },
94654
+ topic: { type: "string" },
94655
+ type: {
94656
+ type: "integer",
94657
+ enum: [5, 6, 9],
94658
+ description: "5=scheduled, 6=recurring no fixed time, 9=recurring with fixed time."
94659
+ },
94660
+ start_time: { type: "string" },
94661
+ duration: { type: "integer" },
94662
+ timezone: { type: "string" },
94663
+ password: { type: "string" },
94664
+ agenda: { type: "string" },
94665
+ settings: { type: "object" },
94666
+ recurrence: { type: "object" }
94667
+ },
94668
+ required: ["userId", "topic"]
94669
+ },
94670
+ request: {
94671
+ method: "POST",
94672
+ path: "/v2/users/{userId}/webinars",
94673
+ body: {
94674
+ topic: "{topic}",
94675
+ type: "{type}",
94676
+ start_time: "{start_time}",
94677
+ duration: "{duration}",
94678
+ timezone: "{timezone}",
94679
+ password: "{password}",
94680
+ agenda: "{agenda}",
94681
+ settings: "{settings}",
94682
+ recurrence: "{recurrence}"
94683
+ }
94684
+ },
94685
+ cas: "none",
94686
+ externalEffect: true,
94687
+ requiredScopes: ["webinar:write:webinar"]
94688
+ },
94689
+ {
94690
+ name: "recordings.list",
94691
+ class: "read",
94692
+ description: "List cloud recordings for a user.",
94693
+ parameters: {
94694
+ type: "object",
94695
+ properties: {
94696
+ userId: { type: "string", description: 'User id, email, or "me".' },
94697
+ from: { type: "string", description: "YYYY-MM-DD lower bound." },
94698
+ to: { type: "string", description: "YYYY-MM-DD upper bound." },
94699
+ page_size: { type: "integer", minimum: 1, maximum: 300 },
94700
+ next_page_token: { type: "string" },
94701
+ mc: { type: "string" },
94702
+ trash: { type: "boolean" },
94703
+ trash_type: { type: "string", enum: ["meeting_recordings", "recording_file"] }
94704
+ },
94705
+ required: ["userId"]
94706
+ },
94707
+ request: {
94708
+ method: "GET",
94709
+ path: "/v2/users/{userId}/recordings",
94710
+ query: {
94711
+ from: "{from}",
94712
+ to: "{to}",
94713
+ page_size: "{page_size}",
94714
+ next_page_token: "{next_page_token}",
94715
+ mc: "{mc}",
94716
+ trash: "{trash}",
94717
+ trash_type: "{trash_type}"
94718
+ }
94719
+ },
94720
+ requiredScopes: ["recording:read:recording"]
94721
+ },
94722
+ {
94723
+ // Zoom does not expose a DELETE /meetings/{id}/live; ending an in-flight
94724
+ // meeting is modeled as a status PUT with `action: "end"`. The endpoint
94725
+ // is idempotent — repeating it on an already-ended meeting returns 204.
94726
+ name: "meetings.end",
94727
+ class: "mutation",
94728
+ description: 'End a live Zoom meeting via the status endpoint (PUT /meetings/{id}/status, action="end").',
94729
+ parameters: {
94730
+ type: "object",
94731
+ properties: {
94732
+ meetingId: { type: "string", description: "Numeric Zoom meeting id of the live meeting to end." }
94733
+ },
94734
+ required: ["meetingId"]
94735
+ },
94736
+ request: {
94737
+ method: "PUT",
94738
+ path: "/v2/meetings/{meetingId}/status",
94739
+ body: { action: "end" }
94740
+ },
94741
+ cas: "native-idempotency",
94742
+ externalEffect: true,
94743
+ requiredScopes: ["meeting:update:meeting"]
94744
+ },
94745
+ {
94746
+ // Mirrors the meeting update contract: a PATCH against the webinar
94747
+ // record. Zoom returns 204 on success and rejects with 300/400 on
94748
+ // schedule conflicts; the declarative-rest layer surfaces both.
94749
+ name: "webinars.update",
94750
+ class: "mutation",
94751
+ description: "Update a scheduled Zoom webinar in place.",
94752
+ parameters: {
94753
+ type: "object",
94754
+ properties: {
94755
+ webinarId: { type: "string" },
94756
+ occurrence_id: { type: "string" },
94757
+ topic: { type: "string" },
94758
+ start_time: { type: "string" },
94759
+ duration: { type: "integer" },
94760
+ timezone: { type: "string" },
94761
+ password: { type: "string" },
94762
+ agenda: { type: "string" },
94763
+ settings: { type: "object" },
94764
+ recurrence: { type: "object" }
94765
+ },
94766
+ required: ["webinarId"]
94767
+ },
94768
+ request: {
94769
+ // Optional-field forest: any of topic/start_time/duration/etc. may be
94770
+ // absent, and the bare {x} body template requires every placeholder.
94771
+ // body: 'args' passes the resolved arg bag through verbatim, dropping
94772
+ // webinarId-on-path duplication via Zoom's lenient PATCH (extra
94773
+ // fields are ignored). Keeps the caller's optional-field semantics
94774
+ // exact instead of forcing them to send every field.
94775
+ method: "PATCH",
94776
+ path: "/v2/webinars/{webinarId}",
94777
+ query: { occurrence_id: "{occurrence_id}" },
94778
+ body: "args"
94779
+ },
94780
+ cas: "native-idempotency",
94781
+ externalEffect: true,
94782
+ requiredScopes: ["webinar:update:webinar"]
94783
+ },
94784
+ {
94785
+ name: "webinars.delete",
94786
+ class: "mutation",
94787
+ description: "Delete a Zoom webinar. Optionally scope to a single occurrence and toggle host/panelist cancellation notifications.",
94788
+ parameters: {
94789
+ type: "object",
94790
+ properties: {
94791
+ webinarId: { type: "string" },
94792
+ occurrence_id: { type: "string" },
94793
+ cancel_webinar_reminder: { type: "boolean" }
94794
+ },
94795
+ required: ["webinarId"]
94796
+ },
94797
+ request: {
94798
+ method: "DELETE",
94799
+ path: "/v2/webinars/{webinarId}",
94800
+ query: {
94801
+ occurrence_id: "{occurrence_id}",
94802
+ cancel_webinar_reminder: "{cancel_webinar_reminder}"
94803
+ }
94804
+ },
94805
+ cas: "native-idempotency",
94806
+ externalEffect: true,
94807
+ requiredScopes: ["webinar:delete:webinar"]
94808
+ },
94809
+ {
94810
+ // `action=trash` moves the recording to the cloud trash (recoverable for
94811
+ // 30 days); `action=delete` permanently deletes. The caller picks the
94812
+ // mode — we won't default to one because the destructive variant is
94813
+ // unrecoverable and the safe variant breaks downstream search indexes
94814
+ // that watch the trash queue.
94815
+ name: "recordings.delete",
94816
+ class: "mutation",
94817
+ description: 'Delete (or trash) a cloud recording for a meeting. action="trash" is recoverable; action="delete" is permanent.',
94818
+ parameters: {
94819
+ type: "object",
94820
+ properties: {
94821
+ meetingId: { type: "string", description: "Meeting id or recording UUID." },
94822
+ action: { type: "string", enum: ["trash", "delete"], description: "trash (default, recoverable) or delete (permanent)." }
94823
+ },
94824
+ required: ["meetingId"]
94825
+ },
94826
+ request: {
94827
+ method: "DELETE",
94828
+ path: "/v2/meetings/{meetingId}/recordings",
94829
+ query: { action: "{action}" }
94830
+ },
94831
+ cas: "native-idempotency",
94832
+ externalEffect: true,
94833
+ requiredScopes: ["recording:write:recording"]
94834
+ },
94835
+ {
94836
+ // Zoom user provisioning is a single POST with an `action` discriminator:
94837
+ // create (manual invite), autoCreate (provision into the account), or
94838
+ // custCreate (custom-managed user). The user_info sub-object carries
94839
+ // email/type/first_name/last_name. We expose `action` + `user_info`
94840
+ // verbatim so callers can hit any of the three provisioning modes.
94841
+ name: "users.create",
94842
+ class: "mutation",
94843
+ description: "Create a user in the Zoom account. `action` selects the provisioning mode; user_info carries the user payload.",
94844
+ parameters: {
94845
+ type: "object",
94846
+ properties: {
94847
+ action: {
94848
+ type: "string",
94849
+ enum: ["create", "autoCreate", "custCreate", "ssoCreate"],
94850
+ description: "Provisioning mode: create (invite), autoCreate (provision), custCreate (custom-managed), ssoCreate (SSO)."
94851
+ },
94852
+ user_info: {
94853
+ type: "object",
94854
+ description: "User payload (email, type, first_name, last_name, password for custCreate, etc.)."
94855
+ }
94856
+ },
94857
+ required: ["action", "user_info"]
94858
+ },
94859
+ request: {
94860
+ method: "POST",
94861
+ path: "/v2/users",
94862
+ body: { action: "{action}", user_info: "{user_info}" }
94863
+ },
94864
+ cas: "native-idempotency",
94865
+ externalEffect: true,
94866
+ requiredScopes: ["user:write:user"]
94867
+ },
94868
+ {
94869
+ name: "recordings.get",
94870
+ class: "read",
94871
+ description: 'List recording files for a meeting or recording id (use a double-encoded UUID if it begins with "/" or contains "//").',
94872
+ parameters: {
94873
+ type: "object",
94874
+ properties: {
94875
+ meetingId: { type: "string", description: "Meeting id or recording UUID." },
94876
+ include_fields: { type: "string", description: "Comma-separated extra fields to include (e.g. download_access_token)." },
94877
+ ttl: { type: "integer", description: "TTL in seconds for download_access_token (max 604800)." }
94878
+ },
94879
+ required: ["meetingId"]
94880
+ },
94881
+ request: {
94882
+ method: "GET",
94883
+ path: "/v2/meetings/{meetingId}/recordings",
94884
+ query: {
94885
+ include_fields: "{include_fields}",
94886
+ ttl: "{ttl}"
94887
+ }
94888
+ },
94889
+ requiredScopes: ["recording:read:recording"]
94890
+ }
94891
+ ]
94892
+ });
94893
+
94894
+ // src/connectors/adapters/affinity.ts
94895
+ var affinityConnector = declarativeRestConnector({
94896
+ kind: "affinity",
94897
+ displayName: "Affinity",
94898
+ description: "Synchronize Affinity people, organizations, opportunities, lists, notes, interactions, and field values.",
94899
+ auth: {
94900
+ kind: "api-key",
94901
+ hint: "Affinity API key from Settings > API. Availability depends on the workspace plan and key owner permissions."
94902
+ },
94903
+ category: "crm",
94904
+ defaultConsistencyModel: "authoritative",
94905
+ baseUrl: "https://api.affinity.co",
94906
+ test: { method: "GET", path: "/auth/whoami" },
94907
+ capabilities: [
94908
+ collectionRead("people.list", "List or search people.", "/persons", "person"),
94909
+ entityRead("people.get", "Read one person, including current organizations and opportunities.", "/persons/{personId}", "personId"),
94910
+ entityCreate("people.create", "Create a person.", "/persons"),
94911
+ entityUpdate("people.update", "Update a person.", "/persons/{personId}", "personId"),
94912
+ entityDelete("people.delete", "Delete a person.", "/persons/{personId}", "personId"),
94913
+ collectionRead("organizations.list", "List or search organizations.", "/organizations", "organization"),
94914
+ entityRead("organizations.get", "Read one organization.", "/organizations/{organizationId}", "organizationId"),
94915
+ entityCreate("organizations.create", "Create an organization.", "/organizations"),
94916
+ entityUpdate("organizations.update", "Update an organization.", "/organizations/{organizationId}", "organizationId"),
94917
+ entityDelete("organizations.delete", "Delete an organization.", "/organizations/{organizationId}", "organizationId"),
94918
+ collectionRead("opportunities.list", "List or search opportunities.", "/opportunities", "opportunity"),
94919
+ entityRead("opportunities.get", "Read one opportunity.", "/opportunities/{opportunityId}", "opportunityId"),
94920
+ entityCreate("opportunities.create", "Create an opportunity.", "/opportunities"),
94921
+ entityUpdate("opportunities.update", "Update an opportunity.", "/opportunities/{opportunityId}", "opportunityId"),
94922
+ entityDelete("opportunities.delete", "Delete an opportunity.", "/opportunities/{opportunityId}", "opportunityId"),
94923
+ collectionRead("lists.list", "List CRM lists.", "/lists", "list"),
94924
+ entityRead("lists.get", "Read one CRM list and its field definitions.", "/lists/{listId}", "listId"),
94925
+ {
94926
+ name: "list-entries.list",
94927
+ class: "read",
94928
+ description: "List entries in an Affinity list.",
94929
+ parameters: {
94930
+ type: "object",
94931
+ properties: {
94932
+ listId: { type: "integer" },
94933
+ page_size: { type: "integer", minimum: 1, maximum: 500 },
94934
+ page_token: { type: "string" }
94935
+ },
94936
+ required: ["listId"]
94937
+ },
94938
+ request: {
94939
+ method: "GET",
94940
+ path: "/lists/{listId}/list-entries",
94941
+ query: { page_size: "{page_size}", page_token: "{page_token}" }
94942
+ }
94943
+ },
94944
+ {
94945
+ name: "list-entries.create",
94946
+ class: "mutation",
94947
+ description: "Add an entity to an Affinity list.",
94948
+ parameters: {
94949
+ type: "object",
94950
+ properties: {
94951
+ listId: { type: "integer" },
94952
+ entity_id: { type: "integer" }
94953
+ },
94954
+ required: ["listId", "entity_id"]
94955
+ },
94956
+ request: { method: "POST", path: "/lists/{listId}/list-entries", body: { entity_id: "{entity_id}" } },
94957
+ cas: "native-idempotency",
94958
+ externalEffect: true
94959
+ },
94960
+ {
94961
+ name: "list-entries.delete",
94962
+ class: "mutation",
94963
+ description: "Remove a list entry.",
94964
+ parameters: idParameters("listEntryId"),
94965
+ request: { method: "DELETE", path: "/list-entries/{listEntryId}" },
94966
+ cas: "native-idempotency",
94967
+ externalEffect: true
94968
+ },
94969
+ {
94970
+ name: "field-values.list",
94971
+ class: "read",
94972
+ description: "Read mapped custom-field values for one person, organization, opportunity, or list entry.",
94973
+ parameters: {
94974
+ type: "object",
94975
+ properties: {
94976
+ person_id: { type: "integer" },
94977
+ organization_id: { type: "integer" },
94978
+ opportunity_id: { type: "integer" },
94979
+ list_entry_id: { type: "integer" }
94980
+ }
94981
+ },
94982
+ request: {
94983
+ method: "GET",
94984
+ path: "/field-values",
94985
+ query: {
94986
+ person_id: "{person_id}",
94987
+ organization_id: "{organization_id}",
94988
+ opportunity_id: "{opportunity_id}",
94989
+ list_entry_id: "{list_entry_id}"
94990
+ }
94991
+ }
94992
+ },
94993
+ entityCreate("field-values.create", "Create a custom-field value.", "/field-values"),
94994
+ entityUpdate("field-values.update", "Update a custom-field value.", "/field-values/{fieldValueId}", "fieldValueId"),
94995
+ entityDelete("field-values.delete", "Delete a custom-field value.", "/field-values/{fieldValueId}", "fieldValueId"),
94996
+ {
94997
+ name: "field-value-changes.list",
94998
+ class: "read",
94999
+ description: "Read custom-field change history for synchronization and evidence.",
95000
+ parameters: {
95001
+ type: "object",
95002
+ properties: {
95003
+ field_id: { type: "integer" },
95004
+ action_type: { type: "integer" },
95005
+ page_size: { type: "integer", minimum: 1, maximum: 500 },
95006
+ page_token: { type: "string" }
95007
+ },
95008
+ required: ["field_id"]
95009
+ },
95010
+ request: {
95011
+ method: "GET",
95012
+ path: "/field-value-changes",
95013
+ query: {
95014
+ field_id: "{field_id}",
95015
+ action_type: "{action_type}",
95016
+ page_size: "{page_size}",
95017
+ page_token: "{page_token}"
95018
+ }
95019
+ }
95020
+ },
95021
+ {
95022
+ name: "interactions.list",
95023
+ class: "read",
95024
+ description: "List email, meeting, call, and chat interactions for a person or organization.",
95025
+ parameters: {
95026
+ type: "object",
95027
+ properties: {
95028
+ person_id: { type: "integer" },
95029
+ organization_id: { type: "integer" },
95030
+ type: { type: "integer" },
95031
+ start_time: { type: "string" },
95032
+ end_time: { type: "string" },
95033
+ page_size: { type: "integer", minimum: 1, maximum: 500 },
95034
+ page_token: { type: "string" }
95035
+ }
95036
+ },
95037
+ request: {
95038
+ method: "GET",
95039
+ path: "/interactions",
95040
+ query: {
95041
+ person_id: "{person_id}",
95042
+ organization_id: "{organization_id}",
95043
+ type: "{type}",
95044
+ start_time: "{start_time}",
95045
+ end_time: "{end_time}",
95046
+ page_size: "{page_size}",
95047
+ page_token: "{page_token}"
95048
+ }
95049
+ }
95050
+ },
95051
+ {
95052
+ name: "notes.list",
95053
+ class: "read",
95054
+ description: "List notes linked to people, organizations, or opportunities.",
95055
+ parameters: {
95056
+ type: "object",
95057
+ properties: {
95058
+ person_id: { type: "integer" },
95059
+ organization_id: { type: "integer" },
95060
+ opportunity_id: { type: "integer" },
95061
+ page_size: { type: "integer", minimum: 1, maximum: 500 },
95062
+ page_token: { type: "string" }
95063
+ }
95064
+ },
95065
+ request: {
95066
+ method: "GET",
95067
+ path: "/notes",
95068
+ query: {
95069
+ person_id: "{person_id}",
95070
+ organization_id: "{organization_id}",
95071
+ opportunity_id: "{opportunity_id}",
95072
+ page_size: "{page_size}",
95073
+ page_token: "{page_token}"
95074
+ }
95075
+ }
95076
+ },
95077
+ entityCreate("notes.create", "Create a note linked to CRM records.", "/notes"),
95078
+ entityUpdate("notes.update", "Update a note.", "/notes/{noteId}", "noteId"),
95079
+ entityDelete("notes.delete", "Delete a note.", "/notes/{noteId}", "noteId")
95080
+ ]
95081
+ });
95082
+ function collectionRead(name, description, path, entity) {
95083
+ return {
95084
+ name,
95085
+ class: "read",
95086
+ description,
95087
+ parameters: {
95088
+ type: "object",
95089
+ properties: {
95090
+ term: { type: "string", description: `Search ${entity} names and identifiers.` },
95091
+ page_size: { type: "integer", minimum: 1, maximum: 500 },
95092
+ page_token: { type: "string" }
95093
+ }
95094
+ },
95095
+ request: {
95096
+ method: "GET",
95097
+ path,
95098
+ query: { term: "{term}", page_size: "{page_size}", page_token: "{page_token}" }
95099
+ }
95100
+ };
95101
+ }
95102
+ function entityRead(name, description, path, id) {
95103
+ return {
95104
+ name,
95105
+ class: "read",
95106
+ description,
95107
+ parameters: idParameters(id),
95108
+ request: { method: "GET", path }
95109
+ };
95110
+ }
95111
+ function entityCreate(name, description, path) {
95112
+ return {
95113
+ name,
95114
+ class: "mutation",
95115
+ description,
95116
+ parameters: arbitraryBodyParameters(),
95117
+ request: { method: "POST", path, body: "args" },
95118
+ cas: "native-idempotency",
95119
+ externalEffect: true
95120
+ };
95121
+ }
95122
+ function entityUpdate(name, description, path, id) {
95123
+ return {
95124
+ name,
95125
+ class: "mutation",
95126
+ description,
95127
+ parameters: {
95128
+ type: "object",
95129
+ properties: { [id]: { type: "integer" } },
95130
+ required: [id],
95131
+ additionalProperties: true
95132
+ },
95133
+ request: { method: "PUT", path, body: "args" },
95134
+ cas: "optimistic-read-verify",
95135
+ externalEffect: true
95136
+ };
95137
+ }
95138
+ function entityDelete(name, description, path, id) {
95139
+ return {
95140
+ name,
95141
+ class: "mutation",
95142
+ description,
95143
+ parameters: idParameters(id),
95144
+ request: { method: "DELETE", path },
95145
+ cas: "native-idempotency",
95146
+ externalEffect: true
95147
+ };
95148
+ }
95149
+ function idParameters(id) {
95150
+ return {
95151
+ type: "object",
95152
+ properties: { [id]: { type: "integer" } },
95153
+ required: [id]
95154
+ };
95155
+ }
95156
+ function arbitraryBodyParameters() {
95157
+ return {
95158
+ type: "object",
95159
+ additionalProperties: true,
95160
+ properties: {}
95161
+ };
95162
+ }
95163
+
93362
95164
  // src/connectors/adapters/griptape.ts
93363
95165
  var griptapeConnector = declarativeRestConnector({
93364
95166
  kind: "griptape",
@@ -125448,6 +127250,9 @@ export {
125448
127250
  phonyConnector,
125449
127251
  stripePackConnector,
125450
127252
  webhookConnector,
127253
+ httpConnector,
127254
+ rssConnector,
127255
+ amazonS3Connector,
125451
127256
  stripeWebhookReceiverConnector,
125452
127257
  slackEventsConnector,
125453
127258
  githubConnector,
@@ -125769,6 +127574,8 @@ export {
125769
127574
  gptzeroDetectAiConnector,
125770
127575
  gristConnector,
125771
127576
  granolaConnector,
127577
+ zoomConnector,
127578
+ affinityConnector,
125772
127579
  griptapeConnector,
125773
127580
  heartbeatConnector,
125774
127581
  hedyConnector,
@@ -125956,4 +127763,4 @@ export {
125956
127763
  demandbaseConnector,
125957
127764
  adapters_exports
125958
127765
  };
125959
- //# sourceMappingURL=chunk-XEJKVCXB.js.map
127766
+ //# sourceMappingURL=chunk-5QWQFX2Q.js.map