@tangle-network/agent-integrations 0.53.48 → 0.53.49

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 (47) hide show
  1. package/dist/catalog.d.ts +2 -2
  2. package/dist/catalog.js +8 -8
  3. package/dist/{chunk-PXOM5AVK.js → chunk-5UCHLQ3D.js} +4 -3
  4. package/dist/{chunk-PXOM5AVK.js.map → chunk-5UCHLQ3D.js.map} +1 -1
  5. package/dist/{chunk-GY4PFAWA.js → chunk-GUZA3U6T.js} +7 -9
  6. package/dist/chunk-GUZA3U6T.js.map +1 -0
  7. package/dist/{chunk-HNEEIMQC.js → chunk-H5UCSRF4.js} +437 -236
  8. package/dist/{chunk-HNEEIMQC.js.map → chunk-H5UCSRF4.js.map} +1 -1
  9. package/dist/{chunk-EZR72HCQ.js → chunk-KBYYTHXQ.js} +2 -2
  10. package/dist/{chunk-I5EF5KIT.js → chunk-UI3U62H6.js} +2 -2
  11. package/dist/{chunk-RRSNINEV.js → chunk-W72KPXXX.js} +9 -1
  12. package/dist/chunk-W72KPXXX.js.map +1 -0
  13. package/dist/{chunk-CBWD2IOL.js → chunk-ZSXRS7D7.js} +2 -2
  14. package/dist/{chunk-Q7TDHVRZ.js → chunk-ZZA4HOBE.js} +2 -2
  15. package/dist/connect/index.d.ts +2 -2
  16. package/dist/connect/index.js +2 -2
  17. package/dist/connectors/adapters/index.d.ts +16 -19
  18. package/dist/connectors/adapters/index.js +4 -2
  19. package/dist/connectors/index.d.ts +13 -5
  20. package/dist/connectors/index.js +7 -3
  21. package/dist/{consumer-BcFIDgMD.d.ts → consumer-DrLnbqYt.d.ts} +1 -1
  22. package/dist/consumer.d.ts +3 -3
  23. package/dist/consumer.js +2 -2
  24. package/dist/{core-types-CE1oMiuI.d.ts → core-types-D26cY5tD.d.ts} +1 -1
  25. package/dist/coverage-catalog.d.ts +2 -2
  26. package/dist/{importers-CHOC-2Y8.d.ts → importers-CQKts3pm.d.ts} +1 -1
  27. package/dist/index.d.ts +10 -10
  28. package/dist/index.js +12 -8
  29. package/dist/mcp.d.ts +3 -3
  30. package/dist/mcp.js +8 -8
  31. package/dist/middleware/index.d.ts +2 -2
  32. package/dist/middleware/index.js +2 -2
  33. package/dist/registry.d.ts +2 -2
  34. package/dist/registry.js +8 -8
  35. package/dist/runtime.d.ts +2 -2
  36. package/dist/runtime.js +8 -8
  37. package/dist/specs.d.ts +2 -2
  38. package/dist/specs.js +4 -4
  39. package/dist/{tangle-id-BL_JQwKA.d.ts → tangle-id-BCOVbWrP.d.ts} +1 -1
  40. package/dist/{types-EXGqQYoU.d.ts → types-DpKn0frG.d.ts} +4 -0
  41. package/package.json +1 -1
  42. package/dist/chunk-GY4PFAWA.js.map +0 -1
  43. package/dist/chunk-RRSNINEV.js.map +0 -1
  44. /package/dist/{chunk-EZR72HCQ.js.map → chunk-KBYYTHXQ.js.map} +0 -0
  45. /package/dist/{chunk-I5EF5KIT.js.map → chunk-UI3U62H6.js.map} +0 -0
  46. /package/dist/{chunk-CBWD2IOL.js.map → chunk-ZSXRS7D7.js.map} +0 -0
  47. /package/dist/{chunk-Q7TDHVRZ.js.map → chunk-ZZA4HOBE.js.map} +0 -0
@@ -14,7 +14,7 @@ import {
14
14
  TangleIdentityUnreachableError,
15
15
  createTangleIdentityClient,
16
16
  tangleIdentity
17
- } from "./chunk-RRSNINEV.js";
17
+ } from "./chunk-W72KPXXX.js";
18
18
  import {
19
19
  __export
20
20
  } from "./chunk-PZ5AY32C.js";
@@ -530,6 +530,7 @@ __export(adapters_exports, {
530
530
  reachinboxConnector: () => reachinboxConnector,
531
531
  recallAiConnector: () => recallAiConnector,
532
532
  recurlyConnector: () => recurlyConnector,
533
+ reddit: () => reddit,
533
534
  redditConnector: () => redditConnector,
534
535
  redisConnector: () => redisConnector,
535
536
  redshiftConnector: () => redshiftConnector,
@@ -978,8 +979,9 @@ async function executeRestRequest(spec, request4, inv, requiredArgs) {
978
979
  if (hostOverride) url.host = hostOverride.replace(/\{region\}/g, awsRegion);
979
980
  if (inv.expectedEtag) headers["if-match"] = inv.expectedEtag;
980
981
  const sendsBody = request4.method !== "GET" && (request4.method !== "DELETE" || request4.body !== void 0);
982
+ const bodyEncoding = request4.bodyEncoding ?? "json";
981
983
  if (sendsBody && getHeaderCI(headers, "content-type") === void 0) {
982
- headers["content-type"] = "application/json";
984
+ headers["content-type"] = bodyEncoding === "form" ? "application/x-www-form-urlencoded" : "application/json";
983
985
  }
984
986
  let resolvedBody = sendsBody ? resolveBody(request4.body, inv.args, scope, requiredArgs) : void 0;
985
987
  if (placement.kind === "structured-json-body") {
@@ -996,7 +998,7 @@ async function executeRestRequest(spec, request4, inv, requiredArgs) {
996
998
  )
997
999
  };
998
1000
  }
999
- const bodyString = sendsBody ? JSON.stringify(resolvedBody) : void 0;
1001
+ const bodyString = sendsBody ? serializeRequestBody(spec.kind, bodyEncoding, resolvedBody) : void 0;
1000
1002
  if (placement.kind === "aws-sigv4") {
1001
1003
  signAwsRequest(headers, url, {
1002
1004
  method: request4.method,
@@ -1072,6 +1074,21 @@ async function executeRestRequest(spec, request4, inv, requiredArgs) {
1072
1074
  }
1073
1075
  return { data: data4, etag: res.headers.get("etag") ?? void 0 };
1074
1076
  }
1077
+ function serializeRequestBody(connectorKind, encoding, body) {
1078
+ if (encoding === "json") return JSON.stringify(body);
1079
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
1080
+ throw new Error(`${connectorKind}: form request bodies require a flat object`);
1081
+ }
1082
+ const form2 = new URLSearchParams();
1083
+ for (const [key, value] of Object.entries(body)) {
1084
+ if (value === void 0 || value === null) continue;
1085
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
1086
+ throw new Error(`${connectorKind}: form field ${key} must be a scalar value`);
1087
+ }
1088
+ form2.set(key, String(value));
1089
+ }
1090
+ return form2.toString();
1091
+ }
1075
1092
  function resolveBaseUrl(baseUrl4, metadata, allowedBaseUrls, allowedBaseUrlSuffixes, requirePublicHttpsBaseUrl) {
1076
1093
  let resolved;
1077
1094
  if (typeof baseUrl4 === "string") {
@@ -16131,40 +16148,228 @@ var pushbulletConnector = declarativeRestConnector({
16131
16148
  ]
16132
16149
  });
16133
16150
 
16151
+ // src/connectors/oauth.ts
16152
+ import { createHash as createHash3, randomBytes } from "crypto";
16153
+ var PENDING_TTL_MS = 10 * 60 * 1e3;
16154
+ var InMemoryOAuthFlowStore = class {
16155
+ pendingFlows = /* @__PURE__ */ new Map();
16156
+ put(state, flow) {
16157
+ this.pendingFlows.set(state, flow);
16158
+ }
16159
+ consume(state) {
16160
+ const flow = this.pendingFlows.get(state);
16161
+ this.pendingFlows.delete(state);
16162
+ if (!flow || flow.expiresAt <= Date.now()) return void 0;
16163
+ return flow;
16164
+ }
16165
+ sweep(now) {
16166
+ for (const [k, v] of this.pendingFlows) {
16167
+ if (v.expiresAt <= now) this.pendingFlows.delete(k);
16168
+ }
16169
+ }
16170
+ clear() {
16171
+ this.pendingFlows.clear();
16172
+ }
16173
+ };
16174
+ var defaultFlowStore = new InMemoryOAuthFlowStore();
16175
+ function startOAuthFlow(input) {
16176
+ const store = input.store ?? defaultFlowStore;
16177
+ const now = input.now ?? Date.now();
16178
+ store.sweep?.(now);
16179
+ const usePkce = (input.pkce ?? "required") !== "unsupported";
16180
+ const codeVerifier = usePkce ? base64Url(randomBytes(48)) : void 0;
16181
+ const codeChallenge = codeVerifier ? base64Url(createHash3("sha256").update(codeVerifier).digest()) : void 0;
16182
+ const state = base64Url(randomBytes(24));
16183
+ store.put(state, {
16184
+ codeVerifier,
16185
+ state,
16186
+ projectId: input.projectId,
16187
+ kind: input.kind,
16188
+ label: input.label,
16189
+ redirectUri: input.redirectUri,
16190
+ expiresAt: now + PENDING_TTL_MS
16191
+ });
16192
+ const url = new URL(input.authorizationUrl);
16193
+ url.searchParams.set("response_type", "code");
16194
+ url.searchParams.set(
16195
+ oauthParameterName(input.authorizationClientIdParam, "client_id"),
16196
+ input.clientId
16197
+ );
16198
+ url.searchParams.set("redirect_uri", input.redirectUri);
16199
+ url.searchParams.set("scope", input.scopes.join(input.scopeSeparator ?? " "));
16200
+ url.searchParams.set("state", state);
16201
+ if (input.extraAuthParams) {
16202
+ for (const [k, v] of Object.entries(input.extraAuthParams)) {
16203
+ url.searchParams.set(k, v);
16204
+ }
16205
+ }
16206
+ if (codeChallenge) {
16207
+ url.searchParams.set("code_challenge", codeChallenge);
16208
+ url.searchParams.set("code_challenge_method", "S256");
16209
+ } else {
16210
+ url.searchParams.delete("code_challenge");
16211
+ url.searchParams.delete("code_challenge_method");
16212
+ }
16213
+ return { authorizationUrl: url.toString(), state };
16214
+ }
16215
+ async function consumePendingFlow(state, store = defaultFlowStore) {
16216
+ await store.sweep?.(Date.now());
16217
+ const flow = await store.consume(state);
16218
+ if (!flow) {
16219
+ throw new Error("Unknown or expired OAuth state: possible CSRF, replay, or stale flow");
16220
+ }
16221
+ return flow;
16222
+ }
16223
+ async function exchangeAuthorizationCode(input) {
16224
+ const usePkce = (input.pkce ?? "required") !== "unsupported";
16225
+ if (usePkce && !input.codeVerifier) {
16226
+ throw new Error("OAuth token exchange requires a PKCE code_verifier");
16227
+ }
16228
+ const body = new URLSearchParams({
16229
+ grant_type: "authorization_code",
16230
+ code: input.code,
16231
+ redirect_uri: input.redirectUri
16232
+ });
16233
+ if (usePkce) body.set("code_verifier", input.codeVerifier);
16234
+ const headers = createOAuthTokenRequestHeaders(input.tokenRequestHeaders);
16235
+ applyTokenClientAuthentication(input, body, headers);
16236
+ const res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
16237
+ method: "POST",
16238
+ headers,
16239
+ body,
16240
+ signal: input.signal
16241
+ });
16242
+ if (!res.ok) {
16243
+ const text = await res.text().catch(() => "");
16244
+ throw new Error(`OAuth token exchange failed: ${res.status} ${res.statusText} \u2014 ${text.slice(0, 200)}`);
16245
+ }
16246
+ const json = await res.json();
16247
+ return {
16248
+ accessToken: json.access_token,
16249
+ refreshToken: json.refresh_token,
16250
+ expiresIn: json.expires_in,
16251
+ scope: json.scope,
16252
+ tokenType: json.token_type,
16253
+ idToken: json.id_token
16254
+ };
16255
+ }
16256
+ async function refreshAccessToken(input) {
16257
+ const body = new URLSearchParams({
16258
+ grant_type: "refresh_token",
16259
+ refresh_token: input.refreshToken
16260
+ });
16261
+ const headers = createOAuthTokenRequestHeaders(input.tokenRequestHeaders);
16262
+ applyTokenClientAuthentication(input, body, headers);
16263
+ const res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
16264
+ method: "POST",
16265
+ headers,
16266
+ body,
16267
+ signal: input.signal
16268
+ });
16269
+ if (!res.ok) {
16270
+ const text = await res.text().catch(() => "");
16271
+ throw new Error(`OAuth refresh failed: ${res.status} ${res.statusText} \u2014 ${text.slice(0, 200)}`);
16272
+ }
16273
+ const json = await res.json();
16274
+ return {
16275
+ accessToken: json.access_token,
16276
+ // Some providers omit refresh_token on refresh — keep the previous one
16277
+ // in that case (caller passes through if undefined).
16278
+ refreshToken: json.refresh_token,
16279
+ expiresIn: json.expires_in,
16280
+ scope: json.scope,
16281
+ tokenType: json.token_type,
16282
+ idToken: json.id_token
16283
+ };
16284
+ }
16285
+ function createOAuthTokenRequestHeaders(additional) {
16286
+ const headers = new Headers(additional);
16287
+ headers.set("content-type", "application/x-www-form-urlencoded");
16288
+ headers.set("accept", "application/json");
16289
+ headers.delete("authorization");
16290
+ return Object.fromEntries(headers.entries());
16291
+ }
16292
+ function applyTokenClientAuthentication(input, body, headers) {
16293
+ const method = input.tokenClientAuthMethod ?? "client_secret_post";
16294
+ if (method === "none") {
16295
+ body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
16296
+ return;
16297
+ }
16298
+ if (!input.clientSecret) {
16299
+ throw new Error(`OAuth ${method} client authentication requires a client secret`);
16300
+ }
16301
+ if (method === "client_secret_post") {
16302
+ body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
16303
+ body.set(
16304
+ oauthParameterName(input.tokenClientSecretParam, "client_secret"),
16305
+ input.clientSecret
16306
+ );
16307
+ return;
16308
+ }
16309
+ if (method === "client_secret_basic") {
16310
+ headers.authorization = `Basic ${Buffer.from(
16311
+ `${input.clientId}:${input.clientSecret}`,
16312
+ "utf8"
16313
+ ).toString("base64")}`;
16314
+ return;
16315
+ }
16316
+ throw new Error("Unsupported OAuth token client authentication method");
16317
+ }
16318
+ function base64Url(buf) {
16319
+ return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
16320
+ }
16321
+ function oauthParameterName(value, fallback) {
16322
+ const name = value ?? fallback;
16323
+ if (!/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(name)) {
16324
+ throw new Error("Invalid OAuth parameter name");
16325
+ }
16326
+ return name;
16327
+ }
16328
+ function _resetPendingFlowsForTests() {
16329
+ defaultFlowStore.clear?.();
16330
+ }
16331
+
16134
16332
  // src/connectors/adapters/reddit.ts
16135
- var redditConnector = declarativeRestConnector({
16333
+ var REDDIT_USER_AGENT = "web:tools.tangle.integration-hub:v1.0 (contact: https://tangle.tools)";
16334
+ var REDDIT_TOKEN_URL = "https://www.reddit.com/api/v1/access_token";
16335
+ var redditSpec = {
16136
16336
  kind: "reddit",
16137
16337
  displayName: "Reddit",
16138
16338
  description: "Interact with Reddit \u2014 fetch and submit posts, retrieve comments, create and edit posts/comments.",
16139
16339
  auth: {
16140
16340
  kind: "oauth2",
16141
16341
  authorizationUrl: "https://www.reddit.com/api/v1/authorize",
16142
- tokenUrl: "https://www.reddit.com/api/v1/access_token",
16143
- scopes: ["read", "submit", "edit"],
16342
+ tokenUrl: REDDIT_TOKEN_URL,
16343
+ scopes: ["identity", "read", "submit", "edit"],
16344
+ pkce: "unsupported",
16345
+ tokenClientAuthMethod: "client_secret_basic",
16346
+ extraAuthParams: { duration: "permanent" },
16347
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16144
16348
  clientIdEnv: "REDDIT_OAUTH_CLIENT_ID",
16145
16349
  clientSecretEnv: "REDDIT_OAUTH_CLIENT_SECRET"
16146
16350
  },
16147
16351
  category: "comms",
16148
16352
  defaultConsistencyModel: "authoritative",
16149
16353
  baseUrl: "https://oauth.reddit.com",
16354
+ defaultHeaders: { "User-Agent": REDDIT_USER_AGENT },
16150
16355
  test: { method: "GET", path: "/api/v1/me" },
16151
16356
  capabilities: [
16152
16357
  {
16153
16358
  name: "post.retrieve",
16154
16359
  class: "read",
16155
- description: "Retrieve a Reddit post by its ID or fullname.",
16360
+ description: "Retrieve a Reddit post by its fullname, such as t3_abc123.",
16156
16361
  parameters: {
16157
16362
  type: "object",
16158
16363
  properties: {
16159
- postId: { type: "string" }
16364
+ postId: { type: "string", description: "Reddit post fullname, such as t3_abc123." }
16160
16365
  },
16161
16366
  required: ["postId"]
16162
16367
  },
16163
16368
  request: {
16164
16369
  method: "GET",
16165
- path: "/r/{subreddit}/comments/{postId}",
16166
- headers: {
16167
- "User-Agent": "AgentDev/1.0"
16370
+ path: "/api/info",
16371
+ query: {
16372
+ id: "{postId}"
16168
16373
  }
16169
16374
  },
16170
16375
  requiredScopes: ["read"]
@@ -16183,10 +16388,7 @@ var redditConnector = declarativeRestConnector({
16183
16388
  },
16184
16389
  request: {
16185
16390
  method: "GET",
16186
- path: "/r/{subreddit}/comments/{postId}",
16187
- headers: {
16188
- "User-Agent": "AgentDev/1.0"
16189
- }
16391
+ path: "/r/{subreddit}/comments/{postId}"
16190
16392
  },
16191
16393
  requiredScopes: ["read"]
16192
16394
  },
@@ -16199,27 +16401,28 @@ var redditConnector = declarativeRestConnector({
16199
16401
  properties: {
16200
16402
  subreddit: { type: "string" },
16201
16403
  title: { type: "string" },
16404
+ kind: { type: "string", enum: ["self", "link"] },
16202
16405
  text: { type: "string" },
16203
16406
  url: { type: "string" },
16204
16407
  flair_id: { type: "string" }
16205
16408
  },
16206
- required: ["subreddit", "title"]
16409
+ required: ["subreddit", "title", "kind"]
16207
16410
  },
16208
16411
  request: {
16209
16412
  method: "POST",
16210
- path: "/r/{subreddit}/submit",
16413
+ path: "/api/submit",
16414
+ bodyEncoding: "form",
16211
16415
  body: {
16416
+ api_type: "json",
16417
+ sr: "{subreddit}",
16212
16418
  title: "{title}",
16419
+ kind: "{kind}",
16213
16420
  text: "{text}",
16214
16421
  url: "{url}",
16215
- flair_id: "{flair_id}",
16216
- kind: "self"
16217
- },
16218
- headers: {
16219
- "User-Agent": "AgentDev/1.0"
16422
+ flair_id: "{flair_id}"
16220
16423
  }
16221
16424
  },
16222
- cas: "native-idempotency",
16425
+ cas: "none",
16223
16426
  externalEffect: true,
16224
16427
  requiredScopes: ["submit"]
16225
16428
  },
@@ -16238,15 +16441,14 @@ var redditConnector = declarativeRestConnector({
16238
16441
  request: {
16239
16442
  method: "POST",
16240
16443
  path: "/api/comment",
16444
+ bodyEncoding: "form",
16241
16445
  body: {
16446
+ api_type: "json",
16242
16447
  thing_id: "{parentId}",
16243
16448
  text: "{text}"
16244
- },
16245
- headers: {
16246
- "User-Agent": "AgentDev/1.0"
16247
16449
  }
16248
16450
  },
16249
- cas: "native-idempotency",
16451
+ cas: "none",
16250
16452
  externalEffect: true,
16251
16453
  requiredScopes: ["submit"]
16252
16454
  },
@@ -16270,9 +16472,6 @@ var redditConnector = declarativeRestConnector({
16270
16472
  query: {
16271
16473
  sort: "{sort}",
16272
16474
  limit: "{limit}"
16273
- },
16274
- headers: {
16275
- "User-Agent": "AgentDev/1.0"
16276
16475
  }
16277
16476
  },
16278
16477
  requiredScopes: ["read"]
@@ -16292,15 +16491,15 @@ var redditConnector = declarativeRestConnector({
16292
16491
  request: {
16293
16492
  method: "POST",
16294
16493
  path: "/api/editusertext",
16494
+ bodyEncoding: "form",
16295
16495
  body: {
16496
+ api_type: "json",
16296
16497
  thing_id: "{thingId}",
16297
16498
  text: "{text}"
16298
- },
16299
- headers: {
16300
- "User-Agent": "AgentDev/1.0"
16301
16499
  }
16302
16500
  },
16303
- cas: "optimistic-read-verify",
16501
+ cas: "none",
16502
+ externalEffect: true,
16304
16503
  requiredScopes: ["edit"]
16305
16504
  },
16306
16505
  {
@@ -16318,15 +16517,15 @@ var redditConnector = declarativeRestConnector({
16318
16517
  request: {
16319
16518
  method: "POST",
16320
16519
  path: "/api/editusertext",
16520
+ bodyEncoding: "form",
16321
16521
  body: {
16522
+ api_type: "json",
16322
16523
  thing_id: "{thingId}",
16323
16524
  text: "{text}"
16324
- },
16325
- headers: {
16326
- "User-Agent": "AgentDev/1.0"
16327
16525
  }
16328
16526
  },
16329
- cas: "optimistic-read-verify",
16527
+ cas: "none",
16528
+ externalEffect: true,
16330
16529
  requiredScopes: ["edit"]
16331
16530
  },
16332
16531
  {
@@ -16343,13 +16542,12 @@ var redditConnector = declarativeRestConnector({
16343
16542
  request: {
16344
16543
  method: "POST",
16345
16544
  path: "/api/del",
16545
+ bodyEncoding: "form",
16346
16546
  body: {
16347
16547
  id: "{thingId}"
16348
- },
16349
- headers: {
16350
- "User-Agent": "AgentDev/1.0"
16351
16548
  }
16352
16549
  },
16550
+ cas: "none",
16353
16551
  externalEffect: true,
16354
16552
  requiredScopes: ["edit"]
16355
16553
  },
@@ -16367,18 +16565,150 @@ var redditConnector = declarativeRestConnector({
16367
16565
  request: {
16368
16566
  method: "POST",
16369
16567
  path: "/api/del",
16568
+ bodyEncoding: "form",
16370
16569
  body: {
16371
16570
  id: "{thingId}"
16372
- },
16373
- headers: {
16374
- "User-Agent": "AgentDev/1.0"
16375
16571
  }
16376
16572
  },
16573
+ cas: "none",
16377
16574
  externalEffect: true,
16378
16575
  requiredScopes: ["edit"]
16379
16576
  }
16380
16577
  ]
16381
- });
16578
+ };
16579
+ var redditConnector = declarativeRestConnector(redditSpec);
16580
+ function reddit(options) {
16581
+ const adapter = declarativeRestConnector(redditSpec);
16582
+ const now = options.now ?? Date.now;
16583
+ const refreshes = /* @__PURE__ */ new Map();
16584
+ const refresh = async (credentials) => {
16585
+ if (credentials.kind !== "oauth2" || !credentials.refreshToken) {
16586
+ throw new Error("reddit.refreshToken: missing refresh token");
16587
+ }
16588
+ const tokens = await refreshAccessToken({
16589
+ tokenUrl: REDDIT_TOKEN_URL,
16590
+ clientId: options.clientId,
16591
+ clientSecret: options.clientSecret,
16592
+ tokenClientAuthMethod: "client_secret_basic",
16593
+ refreshToken: credentials.refreshToken,
16594
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16595
+ fetchImpl: options.fetchImpl
16596
+ });
16597
+ if (!tokens.accessToken || !tokens.expiresIn) {
16598
+ throw new Error("Reddit token refresh returned an incomplete grant");
16599
+ }
16600
+ return {
16601
+ kind: "oauth2",
16602
+ accessToken: tokens.accessToken,
16603
+ refreshToken: tokens.refreshToken ?? credentials.refreshToken,
16604
+ expiresAt: now() + tokens.expiresIn * 1e3
16605
+ };
16606
+ };
16607
+ adapter.refreshToken = refresh;
16608
+ const withFreshCredentials = async (invocation) => {
16609
+ const credentials = invocation.source.credentials;
16610
+ if (credentials.kind !== "oauth2") {
16611
+ throw new CredentialsExpired("Reddit requires OAuth2 credentials.", invocation.source.id);
16612
+ }
16613
+ if (credentials.expiresAt !== void 0 && credentials.expiresAt > now() + 6e4) {
16614
+ return invocation;
16615
+ }
16616
+ if (!credentials.refreshToken) {
16617
+ throw new CredentialsExpired(
16618
+ "Reddit access token expired and no refresh token is available.",
16619
+ invocation.source.id
16620
+ );
16621
+ }
16622
+ let pending = refreshes.get(invocation.source.id);
16623
+ if (!pending) {
16624
+ let next;
16625
+ const promise = Promise.resolve().then(() => {
16626
+ if (!next.onCredentialsRotated) {
16627
+ throw new Error("reddit: credential rotation persistence callback is required before refreshing");
16628
+ }
16629
+ return refresh(credentials);
16630
+ });
16631
+ next = {
16632
+ promise,
16633
+ onCredentialsRotated: invocation.onCredentialsRotated
16634
+ };
16635
+ pending = next;
16636
+ refreshes.set(invocation.source.id, pending);
16637
+ } else if (!pending.onCredentialsRotated && invocation.onCredentialsRotated) {
16638
+ pending.onCredentialsRotated = invocation.onCredentialsRotated;
16639
+ }
16640
+ const current = pending;
16641
+ try {
16642
+ const refreshed = await current.promise;
16643
+ current.persistence ??= Promise.resolve().then(
16644
+ () => current.onCredentialsRotated?.(refreshed)
16645
+ );
16646
+ await current.persistence;
16647
+ return {
16648
+ ...invocation,
16649
+ source: { ...invocation.source, credentials: refreshed }
16650
+ };
16651
+ } finally {
16652
+ if (refreshes.get(invocation.source.id) === current) {
16653
+ refreshes.delete(invocation.source.id);
16654
+ }
16655
+ }
16656
+ };
16657
+ return {
16658
+ ...adapter,
16659
+ async exchangeOAuth(input) {
16660
+ const tokens = await exchangeAuthorizationCode({
16661
+ tokenUrl: REDDIT_TOKEN_URL,
16662
+ clientId: options.clientId,
16663
+ clientSecret: options.clientSecret,
16664
+ tokenClientAuthMethod: "client_secret_basic",
16665
+ code: input.code,
16666
+ codeVerifier: input.codeVerifier,
16667
+ pkce: "unsupported",
16668
+ redirectUri: input.redirectUri,
16669
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16670
+ fetchImpl: options.fetchImpl
16671
+ });
16672
+ if (!tokens.accessToken || !tokens.refreshToken || !tokens.expiresIn) {
16673
+ throw new Error("Reddit token exchange returned an incomplete permanent grant");
16674
+ }
16675
+ return {
16676
+ credentials: {
16677
+ kind: "oauth2",
16678
+ accessToken: tokens.accessToken,
16679
+ refreshToken: tokens.refreshToken,
16680
+ expiresAt: now() + tokens.expiresIn * 1e3
16681
+ },
16682
+ scopes: tokens.scope?.split(/[\s,]+/).filter(Boolean) ?? [...redditSpec.auth.scopes],
16683
+ metadata: {}
16684
+ };
16685
+ },
16686
+ refreshToken: refresh,
16687
+ async executeRead(invocation) {
16688
+ return adapter.executeRead(await withFreshCredentials(invocation));
16689
+ },
16690
+ async executeMutation(invocation) {
16691
+ return adapter.executeMutation(await withFreshCredentials(invocation));
16692
+ },
16693
+ async test(source, onCredentialsRotated) {
16694
+ try {
16695
+ const invocation = await withFreshCredentials({
16696
+ source,
16697
+ capabilityName: "__test__",
16698
+ args: {},
16699
+ idempotencyKey: "reddit-connection-test",
16700
+ onCredentialsRotated
16701
+ });
16702
+ return adapter.test(invocation.source);
16703
+ } catch (error) {
16704
+ return {
16705
+ ok: false,
16706
+ reason: error instanceof Error ? error.message : "unknown error"
16707
+ };
16708
+ }
16709
+ }
16710
+ };
16711
+ }
16382
16712
 
16383
16713
  // src/connectors/adapters/rippling.ts
16384
16714
  var ripplingConnector = declarativeRestConnector({
@@ -24774,7 +25104,7 @@ function numberOrUndefined(value) {
24774
25104
  }
24775
25105
 
24776
25106
  // src/connectors/adapters/azure-event-grid.ts
24777
- import { createHash as createHash3, timingSafeEqual } from "crypto";
25107
+ import { createHash as createHash4, timingSafeEqual } from "crypto";
24778
25108
  var API_VERSION2 = "2018-01-01";
24779
25109
  var MAX_BATCH_BYTES = 1024 * 1024;
24780
25110
  var MAX_EVENTS = 5e3;
@@ -24843,8 +25173,8 @@ var azureEventGridConnector = {
24843
25173
  }
24844
25174
  const provided = headerValue(headers, "x-tangle-eventgrid-secret");
24845
25175
  if (!provided) return { valid: false, reason: "Missing x-tangle-eventgrid-secret delivery header" };
24846
- const expectedDigest = createHash3("sha256").update(credentials.deliverySecret).digest();
24847
- const providedDigest = createHash3("sha256").update(provided).digest();
25176
+ const expectedDigest = createHash4("sha256").update(credentials.deliverySecret).digest();
25177
+ const providedDigest = createHash4("sha256").update(provided).digest();
24848
25178
  return timingSafeEqual(expectedDigest, providedDigest) ? { valid: true } : { valid: false, reason: "Invalid x-tangle-eventgrid-secret delivery header" };
24849
25179
  },
24850
25180
  async handleInboundEvent({ rawBody }) {
@@ -27362,186 +27692,6 @@ var billplzConnector = declarativeRestConnector({
27362
27692
  ]
27363
27693
  });
27364
27694
 
27365
- // src/connectors/oauth.ts
27366
- import { createHash as createHash4, randomBytes } from "crypto";
27367
- var PENDING_TTL_MS = 10 * 60 * 1e3;
27368
- var InMemoryOAuthFlowStore = class {
27369
- pendingFlows = /* @__PURE__ */ new Map();
27370
- put(state, flow) {
27371
- this.pendingFlows.set(state, flow);
27372
- }
27373
- consume(state) {
27374
- const flow = this.pendingFlows.get(state);
27375
- this.pendingFlows.delete(state);
27376
- if (!flow || flow.expiresAt <= Date.now()) return void 0;
27377
- return flow;
27378
- }
27379
- sweep(now) {
27380
- for (const [k, v] of this.pendingFlows) {
27381
- if (v.expiresAt <= now) this.pendingFlows.delete(k);
27382
- }
27383
- }
27384
- clear() {
27385
- this.pendingFlows.clear();
27386
- }
27387
- };
27388
- var defaultFlowStore = new InMemoryOAuthFlowStore();
27389
- function startOAuthFlow(input) {
27390
- const store = input.store ?? defaultFlowStore;
27391
- const now = input.now ?? Date.now();
27392
- store.sweep?.(now);
27393
- const usePkce = (input.pkce ?? "required") !== "unsupported";
27394
- const codeVerifier = usePkce ? base64Url(randomBytes(48)) : void 0;
27395
- const codeChallenge = codeVerifier ? base64Url(createHash4("sha256").update(codeVerifier).digest()) : void 0;
27396
- const state = base64Url(randomBytes(24));
27397
- store.put(state, {
27398
- codeVerifier,
27399
- state,
27400
- projectId: input.projectId,
27401
- kind: input.kind,
27402
- label: input.label,
27403
- redirectUri: input.redirectUri,
27404
- expiresAt: now + PENDING_TTL_MS
27405
- });
27406
- const url = new URL(input.authorizationUrl);
27407
- url.searchParams.set("response_type", "code");
27408
- url.searchParams.set(
27409
- oauthParameterName(input.authorizationClientIdParam, "client_id"),
27410
- input.clientId
27411
- );
27412
- url.searchParams.set("redirect_uri", input.redirectUri);
27413
- url.searchParams.set("scope", input.scopes.join(input.scopeSeparator ?? " "));
27414
- url.searchParams.set("state", state);
27415
- if (input.extraAuthParams) {
27416
- for (const [k, v] of Object.entries(input.extraAuthParams)) {
27417
- url.searchParams.set(k, v);
27418
- }
27419
- }
27420
- if (codeChallenge) {
27421
- url.searchParams.set("code_challenge", codeChallenge);
27422
- url.searchParams.set("code_challenge_method", "S256");
27423
- } else {
27424
- url.searchParams.delete("code_challenge");
27425
- url.searchParams.delete("code_challenge_method");
27426
- }
27427
- return { authorizationUrl: url.toString(), state };
27428
- }
27429
- async function consumePendingFlow(state, store = defaultFlowStore) {
27430
- await store.sweep?.(Date.now());
27431
- const flow = await store.consume(state);
27432
- if (!flow) {
27433
- throw new Error("Unknown or expired OAuth state: possible CSRF, replay, or stale flow");
27434
- }
27435
- return flow;
27436
- }
27437
- async function exchangeAuthorizationCode(input) {
27438
- const usePkce = (input.pkce ?? "required") !== "unsupported";
27439
- if (usePkce && !input.codeVerifier) {
27440
- throw new Error("OAuth token exchange requires a PKCE code_verifier");
27441
- }
27442
- const body = new URLSearchParams({
27443
- grant_type: "authorization_code",
27444
- code: input.code,
27445
- redirect_uri: input.redirectUri
27446
- });
27447
- if (usePkce) body.set("code_verifier", input.codeVerifier);
27448
- const headers = {
27449
- "content-type": "application/x-www-form-urlencoded",
27450
- accept: "application/json"
27451
- };
27452
- applyTokenClientAuthentication(input, body, headers);
27453
- const res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
27454
- method: "POST",
27455
- headers,
27456
- body,
27457
- signal: input.signal
27458
- });
27459
- if (!res.ok) {
27460
- const text = await res.text().catch(() => "");
27461
- throw new Error(`OAuth token exchange failed: ${res.status} ${res.statusText} \u2014 ${text.slice(0, 200)}`);
27462
- }
27463
- const json = await res.json();
27464
- return {
27465
- accessToken: json.access_token,
27466
- refreshToken: json.refresh_token,
27467
- expiresIn: json.expires_in,
27468
- scope: json.scope,
27469
- tokenType: json.token_type,
27470
- idToken: json.id_token
27471
- };
27472
- }
27473
- async function refreshAccessToken(input) {
27474
- const body = new URLSearchParams({
27475
- grant_type: "refresh_token",
27476
- refresh_token: input.refreshToken
27477
- });
27478
- const headers = {
27479
- "content-type": "application/x-www-form-urlencoded",
27480
- accept: "application/json"
27481
- };
27482
- applyTokenClientAuthentication(input, body, headers);
27483
- const res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
27484
- method: "POST",
27485
- headers,
27486
- body,
27487
- signal: input.signal
27488
- });
27489
- if (!res.ok) {
27490
- const text = await res.text().catch(() => "");
27491
- throw new Error(`OAuth refresh failed: ${res.status} ${res.statusText} \u2014 ${text.slice(0, 200)}`);
27492
- }
27493
- const json = await res.json();
27494
- return {
27495
- accessToken: json.access_token,
27496
- // Some providers omit refresh_token on refresh — keep the previous one
27497
- // in that case (caller passes through if undefined).
27498
- refreshToken: json.refresh_token,
27499
- expiresIn: json.expires_in,
27500
- scope: json.scope,
27501
- tokenType: json.token_type,
27502
- idToken: json.id_token
27503
- };
27504
- }
27505
- function applyTokenClientAuthentication(input, body, headers) {
27506
- const method = input.tokenClientAuthMethod ?? "client_secret_post";
27507
- if (method === "none") {
27508
- body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
27509
- return;
27510
- }
27511
- if (!input.clientSecret) {
27512
- throw new Error(`OAuth ${method} client authentication requires a client secret`);
27513
- }
27514
- if (method === "client_secret_post") {
27515
- body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
27516
- body.set(
27517
- oauthParameterName(input.tokenClientSecretParam, "client_secret"),
27518
- input.clientSecret
27519
- );
27520
- return;
27521
- }
27522
- if (method === "client_secret_basic") {
27523
- headers.authorization = `Basic ${Buffer.from(
27524
- `${input.clientId}:${input.clientSecret}`,
27525
- "utf8"
27526
- ).toString("base64")}`;
27527
- return;
27528
- }
27529
- throw new Error("Unsupported OAuth token client authentication method");
27530
- }
27531
- function base64Url(buf) {
27532
- return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
27533
- }
27534
- function oauthParameterName(value, fallback) {
27535
- const name = value ?? fallback;
27536
- if (!/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(name)) {
27537
- throw new Error("Invalid OAuth parameter name");
27538
- }
27539
- return name;
27540
- }
27541
- function _resetPendingFlowsForTests() {
27542
- defaultFlowStore.clear?.();
27543
- }
27544
-
27545
27695
  // src/connectors/adapters/cal-com.ts
27546
27696
  var authorizeUrl = "https://app.cal.com/auth/oauth2/authorize";
27547
27697
  var tokenUrl = "https://api.cal.com/v2/auth/oauth2/token";
@@ -52737,13 +52887,11 @@ function microsoftTeams(opts) {
52737
52887
  async test(source) {
52738
52888
  try {
52739
52889
  const accessToken4 = await ensureFreshAccessToken11(source.credentials, clientId, clientSecret);
52740
- const res = await fetch(`${GRAPH2}/me?$select=id`, {
52890
+ const res = await fetch(`${GRAPH2}/me/joinedTeams`, {
52741
52891
  headers: { authorization: `Bearer ${accessToken4}` },
52742
52892
  signal: AbortSignal.timeout(8e3)
52743
52893
  });
52744
- if (res.status === 401 || res.status === 403) {
52745
- return { ok: false, reason: `Microsoft rejected token (${res.status}) \u2014 reconnect required` };
52746
- }
52894
+ if (res.status === 401 || res.status === 403) return microsoftGraphTestFailure(res);
52747
52895
  if (!res.ok) return { ok: false, reason: `Microsoft Graph returned ${res.status}` };
52748
52896
  return { ok: true };
52749
52897
  } catch (err) {
@@ -52804,7 +52952,7 @@ async function graphGet2(url, accessToken4, dataSourceId) {
52804
52952
  signal: AbortSignal.timeout(1e4)
52805
52953
  });
52806
52954
  if (res.status === 401 || res.status === 403) {
52807
- throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId);
52955
+ await throwMicrosoftGraphAccessError(res, dataSourceId);
52808
52956
  }
52809
52957
  if (!res.ok) {
52810
52958
  const text = await res.text().catch(() => "");
@@ -52824,7 +52972,7 @@ async function graphPost(url, accessToken4, body, dataSourceId, op) {
52824
52972
  signal: AbortSignal.timeout(15e3)
52825
52973
  });
52826
52974
  if (res.status === 401 || res.status === 403) {
52827
- throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId);
52975
+ await throwMicrosoftGraphAccessError(res, dataSourceId);
52828
52976
  }
52829
52977
  if (!res.ok) {
52830
52978
  const text = await res.text().catch(() => "");
@@ -52832,6 +52980,57 @@ async function graphPost(url, accessToken4, body, dataSourceId, op) {
52832
52980
  }
52833
52981
  return await res.json();
52834
52982
  }
52983
+ async function microsoftGraphErrorBody(res) {
52984
+ try {
52985
+ const body = await res.json();
52986
+ return body && typeof body === "object" ? body : void 0;
52987
+ } catch {
52988
+ return void 0;
52989
+ }
52990
+ }
52991
+ function microsoftGraphErrorText(body) {
52992
+ const code = typeof body?.error?.code === "string" ? body.error.code : "";
52993
+ const message = typeof body?.error?.message === "string" ? body.error.message : "";
52994
+ return `${code} ${message}`.trim();
52995
+ }
52996
+ function microsoftGraphRequiresLicense(body) {
52997
+ return /\b(?:valid\s+)?licen[cs]e\b/i.test(microsoftGraphErrorText(body));
52998
+ }
52999
+ async function throwMicrosoftGraphAccessError(res, dataSourceId) {
53000
+ const body = await microsoftGraphErrorBody(res);
53001
+ if (microsoftGraphRequiresLicense(body)) {
53002
+ throw new ProviderConfigError(
53003
+ "Microsoft Teams requires a valid Microsoft 365 or Teams license",
53004
+ dataSourceId,
53005
+ { status: res.status, reason: "licenseRequired", body }
53006
+ );
53007
+ }
53008
+ if (res.status === 403) {
53009
+ throw new ProviderConfigError("Microsoft Graph denied the requested permission", dataSourceId, {
53010
+ status: res.status,
53011
+ reason: typeof body?.error?.code === "string" && body.error.code ? body.error.code : "forbidden",
53012
+ body
53013
+ });
53014
+ }
53015
+ throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId, {
53016
+ status: res.status,
53017
+ reason: typeof body?.error?.code === "string" && body.error.code ? body.error.code : "unauthorized",
53018
+ body
53019
+ });
53020
+ }
53021
+ async function microsoftGraphTestFailure(res) {
53022
+ const body = await microsoftGraphErrorBody(res);
53023
+ if (microsoftGraphRequiresLicense(body)) {
53024
+ return {
53025
+ ok: false,
53026
+ reason: "Microsoft Teams requires a valid Microsoft 365 or Teams license"
53027
+ };
53028
+ }
53029
+ if (res.status === 403) {
53030
+ return { ok: false, reason: "Microsoft Graph denied the requested permission" };
53031
+ }
53032
+ return { ok: false, reason: `Microsoft rejected token (${res.status}) \u2014 reconnect required` };
53033
+ }
52835
53034
  function escapeOData2(value) {
52836
53035
  return value.replace(/'/g, "''");
52837
53036
  }
@@ -83842,7 +84041,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
83842
84041
  clientId: "PUSHBULLET_OAUTH_CLIENT_ID",
83843
84042
  clientSecret: "PUSHBULLET_OAUTH_CLIENT_SECRET"
83844
84043
  }),
83845
- defineFactoryAdapter(() => redditConnector, {
84044
+ defineFactoryAdapter(reddit, {
83846
84045
  clientId: "REDDIT_OAUTH_CLIENT_ID",
83847
84046
  clientSecret: "REDDIT_OAUTH_CLIENT_SECRET"
83848
84047
  }),
@@ -148676,6 +148875,7 @@ export {
148676
148875
  consumePendingFlow,
148677
148876
  exchangeAuthorizationCode,
148678
148877
  refreshAccessToken,
148878
+ createOAuthTokenRequestHeaders,
148679
148879
  _resetPendingFlowsForTests,
148680
148880
  declarativeRestConnector,
148681
148881
  oktaConnector,
@@ -148752,6 +148952,7 @@ export {
148752
148952
  paychexConnector,
148753
148953
  pushbulletConnector,
148754
148954
  redditConnector,
148955
+ reddit,
148755
148956
  ripplingConnector,
148756
148957
  sanityConnector,
148757
148958
  sendpulseConnector,
@@ -149332,4 +149533,4 @@ export {
149332
149533
  getresponseConnector,
149333
149534
  adapters_exports
149334
149535
  };
149335
- //# sourceMappingURL=chunk-HNEEIMQC.js.map
149536
+ //# sourceMappingURL=chunk-H5UCSRF4.js.map