@tangle-network/agent-integrations 0.52.0 → 0.53.0

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