@tangle-network/agent-integrations 0.53.48 → 0.53.50

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-HNEEIMQC.js → chunk-2JRYKWYX.js} +696 -270
  4. package/dist/{chunk-HNEEIMQC.js.map → chunk-2JRYKWYX.js.map} +1 -1
  5. package/dist/{chunk-PXOM5AVK.js → chunk-EP6EDAPV.js} +4 -3
  6. package/dist/{chunk-PXOM5AVK.js.map → chunk-EP6EDAPV.js.map} +1 -1
  7. package/dist/{chunk-EZR72HCQ.js → chunk-KBYYTHXQ.js} +2 -2
  8. package/dist/{chunk-CBWD2IOL.js → chunk-QE4WFYSK.js} +2 -2
  9. package/dist/{chunk-GY4PFAWA.js → chunk-T3WH6MMA.js} +28 -28
  10. package/dist/chunk-T3WH6MMA.js.map +1 -0
  11. package/dist/{chunk-I5EF5KIT.js → chunk-UI3U62H6.js} +2 -2
  12. package/dist/{chunk-RRSNINEV.js → chunk-W72KPXXX.js} +9 -1
  13. package/dist/chunk-W72KPXXX.js.map +1 -0
  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 +17 -5
  20. package/dist/connectors/index.js +15 -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 +20 -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-CBWD2IOL.js.map → chunk-QE4WFYSK.js.map} +0 -0
  46. /package/dist/{chunk-I5EF5KIT.js.map → chunk-UI3U62H6.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") {
@@ -1628,7 +1645,325 @@ function membershipCapability(name, method, description) {
1628
1645
  }
1629
1646
 
1630
1647
  // src/connectors/adapters/client-credentials-rest.ts
1631
- import { createHash as createHash2 } from "crypto";
1648
+ import { createHash as createHash3 } from "crypto";
1649
+
1650
+ // src/connectors/oauth.ts
1651
+ import { createHash as createHash2, randomBytes } from "crypto";
1652
+ var PENDING_TTL_MS = 10 * 60 * 1e3;
1653
+ var InMemoryOAuthFlowStore = class {
1654
+ pendingFlows = /* @__PURE__ */ new Map();
1655
+ put(state, flow) {
1656
+ this.pendingFlows.set(state, flow);
1657
+ }
1658
+ consume(state) {
1659
+ const flow = this.pendingFlows.get(state);
1660
+ this.pendingFlows.delete(state);
1661
+ if (!flow || flow.expiresAt <= Date.now()) return void 0;
1662
+ return flow;
1663
+ }
1664
+ sweep(now) {
1665
+ for (const [k, v] of this.pendingFlows) {
1666
+ if (v.expiresAt <= now) this.pendingFlows.delete(k);
1667
+ }
1668
+ }
1669
+ clear() {
1670
+ this.pendingFlows.clear();
1671
+ }
1672
+ };
1673
+ var defaultFlowStore = new InMemoryOAuthFlowStore();
1674
+ function startOAuthFlow(input) {
1675
+ const store = input.store ?? defaultFlowStore;
1676
+ const now = input.now ?? Date.now();
1677
+ store.sweep?.(now);
1678
+ const usePkce = (input.pkce ?? "required") !== "unsupported";
1679
+ const codeVerifier = usePkce ? base64Url(randomBytes(48)) : void 0;
1680
+ const codeChallenge = codeVerifier ? base64Url(createHash2("sha256").update(codeVerifier).digest()) : void 0;
1681
+ const state = base64Url(randomBytes(24));
1682
+ store.put(state, {
1683
+ codeVerifier,
1684
+ state,
1685
+ projectId: input.projectId,
1686
+ kind: input.kind,
1687
+ label: input.label,
1688
+ redirectUri: input.redirectUri,
1689
+ expiresAt: now + PENDING_TTL_MS
1690
+ });
1691
+ const url = new URL(input.authorizationUrl);
1692
+ url.searchParams.set("response_type", "code");
1693
+ url.searchParams.set(
1694
+ oauthParameterName(input.authorizationClientIdParam, "client_id"),
1695
+ input.clientId
1696
+ );
1697
+ url.searchParams.set("redirect_uri", input.redirectUri);
1698
+ url.searchParams.set("scope", input.scopes.join(input.scopeSeparator ?? " "));
1699
+ url.searchParams.set("state", state);
1700
+ if (input.extraAuthParams) {
1701
+ for (const [k, v] of Object.entries(input.extraAuthParams)) {
1702
+ url.searchParams.set(k, v);
1703
+ }
1704
+ }
1705
+ if (codeChallenge) {
1706
+ url.searchParams.set("code_challenge", codeChallenge);
1707
+ url.searchParams.set("code_challenge_method", "S256");
1708
+ } else {
1709
+ url.searchParams.delete("code_challenge");
1710
+ url.searchParams.delete("code_challenge_method");
1711
+ }
1712
+ return { authorizationUrl: url.toString(), state };
1713
+ }
1714
+ async function consumePendingFlow(state, store = defaultFlowStore) {
1715
+ await store.sweep?.(Date.now());
1716
+ const flow = await store.consume(state);
1717
+ if (!flow) {
1718
+ throw new Error("Unknown or expired OAuth state: possible CSRF, replay, or stale flow");
1719
+ }
1720
+ return flow;
1721
+ }
1722
+ async function exchangeAuthorizationCode(input) {
1723
+ const usePkce = (input.pkce ?? "required") !== "unsupported";
1724
+ if (usePkce && !input.codeVerifier) {
1725
+ throw new Error("OAuth token exchange requires a PKCE code_verifier");
1726
+ }
1727
+ const body = new URLSearchParams({
1728
+ grant_type: "authorization_code",
1729
+ code: input.code,
1730
+ redirect_uri: input.redirectUri
1731
+ });
1732
+ if (usePkce) body.set("code_verifier", input.codeVerifier);
1733
+ const headers = createOAuthTokenRequestHeaders(input.tokenRequestHeaders);
1734
+ applyTokenClientAuthentication(input, body, headers);
1735
+ const redactionValues = [
1736
+ input.code,
1737
+ ...input.codeVerifier ? [input.codeVerifier] : [],
1738
+ ...oauthClientCredentialRedactionValues(
1739
+ input.clientId,
1740
+ input.clientSecret,
1741
+ headers.authorization
1742
+ )
1743
+ ];
1744
+ let res;
1745
+ try {
1746
+ res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
1747
+ method: "POST",
1748
+ headers,
1749
+ body,
1750
+ signal: input.signal
1751
+ });
1752
+ } catch (cause) {
1753
+ const detail = redactOAuthSensitiveText(
1754
+ cause?.message ?? "unknown",
1755
+ redactionValues
1756
+ );
1757
+ throw new Error(`OAuth token exchange transport error: ${detail}`);
1758
+ }
1759
+ if (!res.ok) {
1760
+ const text = await res.text().catch(() => "");
1761
+ const statusText = redactOAuthSensitiveText(res.statusText, redactionValues);
1762
+ const detail = redactOAuthSensitiveText(text, redactionValues).slice(0, 200);
1763
+ throw new Error(
1764
+ `OAuth token exchange failed: ${res.status} ${statusText} \u2014 ${detail}`
1765
+ );
1766
+ }
1767
+ let json;
1768
+ try {
1769
+ json = await res.json();
1770
+ } catch {
1771
+ throw new Error("OAuth token exchange returned invalid JSON");
1772
+ }
1773
+ return {
1774
+ accessToken: json.access_token,
1775
+ refreshToken: json.refresh_token,
1776
+ expiresIn: json.expires_in,
1777
+ scope: json.scope,
1778
+ tokenType: json.token_type,
1779
+ idToken: json.id_token
1780
+ };
1781
+ }
1782
+ async function refreshAccessToken(input) {
1783
+ const body = new URLSearchParams({
1784
+ grant_type: "refresh_token",
1785
+ refresh_token: input.refreshToken
1786
+ });
1787
+ const headers = createOAuthTokenRequestHeaders(input.tokenRequestHeaders);
1788
+ applyTokenClientAuthentication(input, body, headers);
1789
+ const redactionValues = [
1790
+ input.refreshToken,
1791
+ ...oauthClientCredentialRedactionValues(
1792
+ input.clientId,
1793
+ input.clientSecret,
1794
+ headers.authorization
1795
+ )
1796
+ ];
1797
+ let res;
1798
+ try {
1799
+ res = await (input.fetchImpl ?? fetch)(input.tokenUrl, {
1800
+ method: "POST",
1801
+ headers,
1802
+ body,
1803
+ signal: input.signal
1804
+ });
1805
+ } catch (cause) {
1806
+ const detail = redactOAuthSensitiveText(
1807
+ cause?.message ?? "unknown",
1808
+ redactionValues
1809
+ );
1810
+ throw new Error(`OAuth refresh transport error: ${detail}`);
1811
+ }
1812
+ if (!res.ok) {
1813
+ const text = await res.text().catch(() => "");
1814
+ const statusText = redactOAuthSensitiveText(res.statusText, redactionValues);
1815
+ const detail = redactOAuthSensitiveText(text, redactionValues).slice(0, 200);
1816
+ throw new Error(`OAuth refresh failed: ${res.status} ${statusText} \u2014 ${detail}`);
1817
+ }
1818
+ let json;
1819
+ try {
1820
+ json = await res.json();
1821
+ } catch {
1822
+ throw new Error("OAuth refresh returned invalid JSON");
1823
+ }
1824
+ return {
1825
+ accessToken: json.access_token,
1826
+ // Some providers omit refresh_token on refresh — keep the previous one
1827
+ // in that case (caller passes through if undefined).
1828
+ refreshToken: json.refresh_token,
1829
+ expiresIn: json.expires_in,
1830
+ scope: json.scope,
1831
+ tokenType: json.token_type,
1832
+ idToken: json.id_token
1833
+ };
1834
+ }
1835
+ function createOAuthTokenRequestHeaders(additional) {
1836
+ const headers = new Headers(additional);
1837
+ headers.set("content-type", "application/x-www-form-urlencoded");
1838
+ headers.set("accept", "application/json");
1839
+ headers.delete("authorization");
1840
+ return Object.fromEntries(headers.entries());
1841
+ }
1842
+ function formEncodeOAuthClientCredential(value) {
1843
+ return new URLSearchParams({ value }).toString().slice("value=".length);
1844
+ }
1845
+ function createOAuthBasicAuthorizationHeader(clientId, clientSecret, encodeBase64 = defaultOAuthBase64Encode) {
1846
+ const encodedClientId = formEncodeOAuthClientCredential(clientId);
1847
+ const encodedClientSecret = formEncodeOAuthClientCredential(clientSecret);
1848
+ return `Basic ${encodeBase64(`${encodedClientId}:${encodedClientSecret}`)}`;
1849
+ }
1850
+ function defaultOAuthBase64Encode(value) {
1851
+ const encode = globalThis.btoa;
1852
+ if (typeof encode !== "function") {
1853
+ throw new Error(
1854
+ "OAuth client_secret_basic requires a base64 encoder in this runtime."
1855
+ );
1856
+ }
1857
+ return encode(value);
1858
+ }
1859
+ function oauthClientCredentialRedactionValues(clientId, clientSecret, authorization) {
1860
+ return [
1861
+ clientId,
1862
+ ...clientSecret ? [clientSecret] : [],
1863
+ ...authorization ? [
1864
+ authorization,
1865
+ ...authorization.startsWith("Basic ") ? [authorization.slice("Basic ".length)] : []
1866
+ ] : []
1867
+ ];
1868
+ }
1869
+ function redactOAuthSensitiveText(text, secrets) {
1870
+ const matchers = /* @__PURE__ */ new Map();
1871
+ for (const secret of new Set(secrets)) {
1872
+ if (secret.length === 0) continue;
1873
+ addOAuthRedactionMatcher(matchers, escapeRegExp(secret), secret.length);
1874
+ const jsonEscaped = JSON.stringify(secret).slice(1, -1);
1875
+ addOAuthRedactionMatcher(
1876
+ matchers,
1877
+ escapeRegExp(jsonEscaped),
1878
+ jsonEscaped.length
1879
+ );
1880
+ const formEncoded = formEncodeOAuthClientCredential(secret);
1881
+ addOAuthRedactionMatcher(
1882
+ matchers,
1883
+ formEncodedRedactionPattern(formEncoded),
1884
+ Math.max(formEncoded.length, formEncoded.replaceAll("+", "%20").length)
1885
+ );
1886
+ }
1887
+ return [...matchers.values()].sort((left, right) => right.representedLength - left.representedLength).reduce(
1888
+ (redacted, matcher) => redacted.replace(matcher.pattern, "[REDACTED]"),
1889
+ text
1890
+ );
1891
+ }
1892
+ function addOAuthRedactionMatcher(matchers, source, representedLength) {
1893
+ const existing = matchers.get(source);
1894
+ if (existing && existing.representedLength >= representedLength) return;
1895
+ matchers.set(source, {
1896
+ pattern: new RegExp(source, "g"),
1897
+ representedLength
1898
+ });
1899
+ }
1900
+ function formEncodedRedactionPattern(formEncoded) {
1901
+ let pattern = "";
1902
+ for (let index = 0; index < formEncoded.length; index += 1) {
1903
+ const character = formEncoded[index];
1904
+ const encodedByte = formEncoded.slice(index + 1, index + 3);
1905
+ if (character === "%" && /^[0-9A-F]{2}$/.test(encodedByte)) {
1906
+ pattern += `%${percentEncodedBytePattern(encodedByte)}`;
1907
+ index += 2;
1908
+ continue;
1909
+ }
1910
+ if (character === "+") {
1911
+ pattern += "(?:\\+|%20)";
1912
+ continue;
1913
+ }
1914
+ pattern += escapeRegExp(character);
1915
+ }
1916
+ return pattern;
1917
+ }
1918
+ function percentEncodedBytePattern(encodedByte) {
1919
+ return [...encodedByte].map(
1920
+ (character) => /[A-F]/.test(character) ? `[${character}${character.toLowerCase()}]` : character
1921
+ ).join("");
1922
+ }
1923
+ function escapeRegExp(value) {
1924
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1925
+ }
1926
+ function applyTokenClientAuthentication(input, body, headers) {
1927
+ const method = input.tokenClientAuthMethod ?? "client_secret_post";
1928
+ if (method === "none") {
1929
+ body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
1930
+ return;
1931
+ }
1932
+ if (!input.clientSecret) {
1933
+ throw new Error(`OAuth ${method} client authentication requires a client secret`);
1934
+ }
1935
+ if (method === "client_secret_post") {
1936
+ body.set(oauthParameterName(input.tokenClientIdParam, "client_id"), input.clientId);
1937
+ body.set(
1938
+ oauthParameterName(input.tokenClientSecretParam, "client_secret"),
1939
+ input.clientSecret
1940
+ );
1941
+ return;
1942
+ }
1943
+ if (method === "client_secret_basic") {
1944
+ headers.authorization = createOAuthBasicAuthorizationHeader(
1945
+ input.clientId,
1946
+ input.clientSecret
1947
+ );
1948
+ return;
1949
+ }
1950
+ throw new Error("Unsupported OAuth token client authentication method");
1951
+ }
1952
+ function base64Url(buf) {
1953
+ return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
1954
+ }
1955
+ function oauthParameterName(value, fallback) {
1956
+ const name = value ?? fallback;
1957
+ if (!/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(name)) {
1958
+ throw new Error("Invalid OAuth parameter name");
1959
+ }
1960
+ return name;
1961
+ }
1962
+ function _resetPendingFlowsForTests() {
1963
+ defaultFlowStore.clear?.();
1964
+ }
1965
+
1966
+ // src/connectors/adapters/client-credentials-rest.ts
1632
1967
  function clientCredentialsRestConnector(spec) {
1633
1968
  const regionMetadataKey = spec.regionMetadataKey ?? "region";
1634
1969
  const delegated = declarativeRestConnector({
@@ -1747,7 +2082,7 @@ function readRegionName(bundle, metadata, metadataKey, fallback) {
1747
2082
  return (bundle.region ?? metadataRegion ?? fallback).toLowerCase().trim();
1748
2083
  }
1749
2084
  function credentialFingerprint(bundle, region, scope) {
1750
- return createHash2("sha256").update(bundle.clientId).update("\0").update(bundle.clientSecret).update("\0").update(region.tokenUrl).update("\0").update(scope ?? "").digest("hex");
2085
+ return createHash3("sha256").update(bundle.clientId).update("\0").update(bundle.clientSecret).update("\0").update(region.tokenUrl).update("\0").update(scope ?? "").digest("hex");
1751
2086
  }
1752
2087
  function renderConnectionMetadata(template, metadata) {
1753
2088
  const rendered = template.replace(/\{connection\.([a-zA-Z0-9_.-]+)\}/g, (_match, key) => {
@@ -1778,7 +2113,10 @@ async function exchangeClientCredentials(displayName, sourceId, bundle, region,
1778
2113
  "content-type": "application/x-www-form-urlencoded"
1779
2114
  };
1780
2115
  if (credentialPlacement === "basic") {
1781
- headers.authorization = `Basic ${Buffer.from(`${bundle.clientId}:${bundle.clientSecret}`).toString("base64")}`;
2116
+ headers.authorization = createOAuthBasicAuthorizationHeader(
2117
+ bundle.clientId,
2118
+ bundle.clientSecret
2119
+ );
1782
2120
  }
1783
2121
  const response = await fetch(region.tokenUrl, {
1784
2122
  method: "POST",
@@ -16132,39 +16470,46 @@ var pushbulletConnector = declarativeRestConnector({
16132
16470
  });
16133
16471
 
16134
16472
  // src/connectors/adapters/reddit.ts
16135
- var redditConnector = declarativeRestConnector({
16473
+ var REDDIT_USER_AGENT = "web:tools.tangle.integration-hub:v1.0 (contact: https://tangle.tools)";
16474
+ var REDDIT_TOKEN_URL = "https://www.reddit.com/api/v1/access_token";
16475
+ var redditSpec = {
16136
16476
  kind: "reddit",
16137
16477
  displayName: "Reddit",
16138
16478
  description: "Interact with Reddit \u2014 fetch and submit posts, retrieve comments, create and edit posts/comments.",
16139
16479
  auth: {
16140
16480
  kind: "oauth2",
16141
16481
  authorizationUrl: "https://www.reddit.com/api/v1/authorize",
16142
- tokenUrl: "https://www.reddit.com/api/v1/access_token",
16143
- scopes: ["read", "submit", "edit"],
16482
+ tokenUrl: REDDIT_TOKEN_URL,
16483
+ scopes: ["identity", "read", "submit", "edit"],
16484
+ pkce: "unsupported",
16485
+ tokenClientAuthMethod: "client_secret_basic",
16486
+ extraAuthParams: { duration: "permanent" },
16487
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16144
16488
  clientIdEnv: "REDDIT_OAUTH_CLIENT_ID",
16145
16489
  clientSecretEnv: "REDDIT_OAUTH_CLIENT_SECRET"
16146
16490
  },
16147
16491
  category: "comms",
16148
16492
  defaultConsistencyModel: "authoritative",
16149
16493
  baseUrl: "https://oauth.reddit.com",
16494
+ defaultHeaders: { "User-Agent": REDDIT_USER_AGENT },
16150
16495
  test: { method: "GET", path: "/api/v1/me" },
16151
16496
  capabilities: [
16152
16497
  {
16153
16498
  name: "post.retrieve",
16154
16499
  class: "read",
16155
- description: "Retrieve a Reddit post by its ID or fullname.",
16500
+ description: "Retrieve a Reddit post by its fullname, such as t3_abc123.",
16156
16501
  parameters: {
16157
16502
  type: "object",
16158
16503
  properties: {
16159
- postId: { type: "string" }
16504
+ postId: { type: "string", description: "Reddit post fullname, such as t3_abc123." }
16160
16505
  },
16161
16506
  required: ["postId"]
16162
16507
  },
16163
16508
  request: {
16164
16509
  method: "GET",
16165
- path: "/r/{subreddit}/comments/{postId}",
16166
- headers: {
16167
- "User-Agent": "AgentDev/1.0"
16510
+ path: "/api/info",
16511
+ query: {
16512
+ id: "{postId}"
16168
16513
  }
16169
16514
  },
16170
16515
  requiredScopes: ["read"]
@@ -16183,10 +16528,7 @@ var redditConnector = declarativeRestConnector({
16183
16528
  },
16184
16529
  request: {
16185
16530
  method: "GET",
16186
- path: "/r/{subreddit}/comments/{postId}",
16187
- headers: {
16188
- "User-Agent": "AgentDev/1.0"
16189
- }
16531
+ path: "/r/{subreddit}/comments/{postId}"
16190
16532
  },
16191
16533
  requiredScopes: ["read"]
16192
16534
  },
@@ -16199,27 +16541,28 @@ var redditConnector = declarativeRestConnector({
16199
16541
  properties: {
16200
16542
  subreddit: { type: "string" },
16201
16543
  title: { type: "string" },
16544
+ kind: { type: "string", enum: ["self", "link"] },
16202
16545
  text: { type: "string" },
16203
16546
  url: { type: "string" },
16204
16547
  flair_id: { type: "string" }
16205
16548
  },
16206
- required: ["subreddit", "title"]
16549
+ required: ["subreddit", "title", "kind"]
16207
16550
  },
16208
16551
  request: {
16209
16552
  method: "POST",
16210
- path: "/r/{subreddit}/submit",
16553
+ path: "/api/submit",
16554
+ bodyEncoding: "form",
16211
16555
  body: {
16556
+ api_type: "json",
16557
+ sr: "{subreddit}",
16212
16558
  title: "{title}",
16559
+ kind: "{kind}",
16213
16560
  text: "{text}",
16214
16561
  url: "{url}",
16215
- flair_id: "{flair_id}",
16216
- kind: "self"
16217
- },
16218
- headers: {
16219
- "User-Agent": "AgentDev/1.0"
16562
+ flair_id: "{flair_id}"
16220
16563
  }
16221
16564
  },
16222
- cas: "native-idempotency",
16565
+ cas: "none",
16223
16566
  externalEffect: true,
16224
16567
  requiredScopes: ["submit"]
16225
16568
  },
@@ -16238,15 +16581,14 @@ var redditConnector = declarativeRestConnector({
16238
16581
  request: {
16239
16582
  method: "POST",
16240
16583
  path: "/api/comment",
16584
+ bodyEncoding: "form",
16241
16585
  body: {
16586
+ api_type: "json",
16242
16587
  thing_id: "{parentId}",
16243
16588
  text: "{text}"
16244
- },
16245
- headers: {
16246
- "User-Agent": "AgentDev/1.0"
16247
16589
  }
16248
16590
  },
16249
- cas: "native-idempotency",
16591
+ cas: "none",
16250
16592
  externalEffect: true,
16251
16593
  requiredScopes: ["submit"]
16252
16594
  },
@@ -16270,9 +16612,6 @@ var redditConnector = declarativeRestConnector({
16270
16612
  query: {
16271
16613
  sort: "{sort}",
16272
16614
  limit: "{limit}"
16273
- },
16274
- headers: {
16275
- "User-Agent": "AgentDev/1.0"
16276
16615
  }
16277
16616
  },
16278
16617
  requiredScopes: ["read"]
@@ -16292,15 +16631,15 @@ var redditConnector = declarativeRestConnector({
16292
16631
  request: {
16293
16632
  method: "POST",
16294
16633
  path: "/api/editusertext",
16634
+ bodyEncoding: "form",
16295
16635
  body: {
16636
+ api_type: "json",
16296
16637
  thing_id: "{thingId}",
16297
16638
  text: "{text}"
16298
- },
16299
- headers: {
16300
- "User-Agent": "AgentDev/1.0"
16301
16639
  }
16302
16640
  },
16303
- cas: "optimistic-read-verify",
16641
+ cas: "none",
16642
+ externalEffect: true,
16304
16643
  requiredScopes: ["edit"]
16305
16644
  },
16306
16645
  {
@@ -16318,15 +16657,15 @@ var redditConnector = declarativeRestConnector({
16318
16657
  request: {
16319
16658
  method: "POST",
16320
16659
  path: "/api/editusertext",
16660
+ bodyEncoding: "form",
16321
16661
  body: {
16662
+ api_type: "json",
16322
16663
  thing_id: "{thingId}",
16323
16664
  text: "{text}"
16324
- },
16325
- headers: {
16326
- "User-Agent": "AgentDev/1.0"
16327
16665
  }
16328
16666
  },
16329
- cas: "optimistic-read-verify",
16667
+ cas: "none",
16668
+ externalEffect: true,
16330
16669
  requiredScopes: ["edit"]
16331
16670
  },
16332
16671
  {
@@ -16343,13 +16682,12 @@ var redditConnector = declarativeRestConnector({
16343
16682
  request: {
16344
16683
  method: "POST",
16345
16684
  path: "/api/del",
16685
+ bodyEncoding: "form",
16346
16686
  body: {
16347
16687
  id: "{thingId}"
16348
- },
16349
- headers: {
16350
- "User-Agent": "AgentDev/1.0"
16351
16688
  }
16352
16689
  },
16690
+ cas: "none",
16353
16691
  externalEffect: true,
16354
16692
  requiredScopes: ["edit"]
16355
16693
  },
@@ -16367,18 +16705,150 @@ var redditConnector = declarativeRestConnector({
16367
16705
  request: {
16368
16706
  method: "POST",
16369
16707
  path: "/api/del",
16708
+ bodyEncoding: "form",
16370
16709
  body: {
16371
16710
  id: "{thingId}"
16372
- },
16373
- headers: {
16374
- "User-Agent": "AgentDev/1.0"
16375
16711
  }
16376
16712
  },
16713
+ cas: "none",
16377
16714
  externalEffect: true,
16378
16715
  requiredScopes: ["edit"]
16379
16716
  }
16380
16717
  ]
16381
- });
16718
+ };
16719
+ var redditConnector = declarativeRestConnector(redditSpec);
16720
+ function reddit(options) {
16721
+ const adapter = declarativeRestConnector(redditSpec);
16722
+ const now = options.now ?? Date.now;
16723
+ const refreshes = /* @__PURE__ */ new Map();
16724
+ const refresh = async (credentials) => {
16725
+ if (credentials.kind !== "oauth2" || !credentials.refreshToken) {
16726
+ throw new Error("reddit.refreshToken: missing refresh token");
16727
+ }
16728
+ const tokens = await refreshAccessToken({
16729
+ tokenUrl: REDDIT_TOKEN_URL,
16730
+ clientId: options.clientId,
16731
+ clientSecret: options.clientSecret,
16732
+ tokenClientAuthMethod: "client_secret_basic",
16733
+ refreshToken: credentials.refreshToken,
16734
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16735
+ fetchImpl: options.fetchImpl
16736
+ });
16737
+ if (!tokens.accessToken || !tokens.expiresIn) {
16738
+ throw new Error("Reddit token refresh returned an incomplete grant");
16739
+ }
16740
+ return {
16741
+ kind: "oauth2",
16742
+ accessToken: tokens.accessToken,
16743
+ refreshToken: tokens.refreshToken ?? credentials.refreshToken,
16744
+ expiresAt: now() + tokens.expiresIn * 1e3
16745
+ };
16746
+ };
16747
+ adapter.refreshToken = refresh;
16748
+ const withFreshCredentials = async (invocation) => {
16749
+ const credentials = invocation.source.credentials;
16750
+ if (credentials.kind !== "oauth2") {
16751
+ throw new CredentialsExpired("Reddit requires OAuth2 credentials.", invocation.source.id);
16752
+ }
16753
+ if (credentials.expiresAt !== void 0 && credentials.expiresAt > now() + 6e4) {
16754
+ return invocation;
16755
+ }
16756
+ if (!credentials.refreshToken) {
16757
+ throw new CredentialsExpired(
16758
+ "Reddit access token expired and no refresh token is available.",
16759
+ invocation.source.id
16760
+ );
16761
+ }
16762
+ let pending = refreshes.get(invocation.source.id);
16763
+ if (!pending) {
16764
+ let next;
16765
+ const promise = Promise.resolve().then(() => {
16766
+ if (!next.onCredentialsRotated) {
16767
+ throw new Error("reddit: credential rotation persistence callback is required before refreshing");
16768
+ }
16769
+ return refresh(credentials);
16770
+ });
16771
+ next = {
16772
+ promise,
16773
+ onCredentialsRotated: invocation.onCredentialsRotated
16774
+ };
16775
+ pending = next;
16776
+ refreshes.set(invocation.source.id, pending);
16777
+ } else if (!pending.onCredentialsRotated && invocation.onCredentialsRotated) {
16778
+ pending.onCredentialsRotated = invocation.onCredentialsRotated;
16779
+ }
16780
+ const current = pending;
16781
+ try {
16782
+ const refreshed = await current.promise;
16783
+ current.persistence ??= Promise.resolve().then(
16784
+ () => current.onCredentialsRotated?.(refreshed)
16785
+ );
16786
+ await current.persistence;
16787
+ return {
16788
+ ...invocation,
16789
+ source: { ...invocation.source, credentials: refreshed }
16790
+ };
16791
+ } finally {
16792
+ if (refreshes.get(invocation.source.id) === current) {
16793
+ refreshes.delete(invocation.source.id);
16794
+ }
16795
+ }
16796
+ };
16797
+ return {
16798
+ ...adapter,
16799
+ async exchangeOAuth(input) {
16800
+ const tokens = await exchangeAuthorizationCode({
16801
+ tokenUrl: REDDIT_TOKEN_URL,
16802
+ clientId: options.clientId,
16803
+ clientSecret: options.clientSecret,
16804
+ tokenClientAuthMethod: "client_secret_basic",
16805
+ code: input.code,
16806
+ codeVerifier: input.codeVerifier,
16807
+ pkce: "unsupported",
16808
+ redirectUri: input.redirectUri,
16809
+ tokenRequestHeaders: { "User-Agent": REDDIT_USER_AGENT },
16810
+ fetchImpl: options.fetchImpl
16811
+ });
16812
+ if (!tokens.accessToken || !tokens.refreshToken || !tokens.expiresIn) {
16813
+ throw new Error("Reddit token exchange returned an incomplete permanent grant");
16814
+ }
16815
+ return {
16816
+ credentials: {
16817
+ kind: "oauth2",
16818
+ accessToken: tokens.accessToken,
16819
+ refreshToken: tokens.refreshToken,
16820
+ expiresAt: now() + tokens.expiresIn * 1e3
16821
+ },
16822
+ scopes: tokens.scope?.split(/[\s,]+/).filter(Boolean) ?? [...redditSpec.auth.scopes],
16823
+ metadata: {}
16824
+ };
16825
+ },
16826
+ refreshToken: refresh,
16827
+ async executeRead(invocation) {
16828
+ return adapter.executeRead(await withFreshCredentials(invocation));
16829
+ },
16830
+ async executeMutation(invocation) {
16831
+ return adapter.executeMutation(await withFreshCredentials(invocation));
16832
+ },
16833
+ async test(source, onCredentialsRotated) {
16834
+ try {
16835
+ const invocation = await withFreshCredentials({
16836
+ source,
16837
+ capabilityName: "__test__",
16838
+ args: {},
16839
+ idempotencyKey: "reddit-connection-test",
16840
+ onCredentialsRotated
16841
+ });
16842
+ return adapter.test(invocation.source);
16843
+ } catch (error) {
16844
+ return {
16845
+ ok: false,
16846
+ reason: error instanceof Error ? error.message : "unknown error"
16847
+ };
16848
+ }
16849
+ }
16850
+ };
16851
+ }
16382
16852
 
16383
16853
  // src/connectors/adapters/rippling.ts
16384
16854
  var ripplingConnector = declarativeRestConnector({
@@ -24774,7 +25244,7 @@ function numberOrUndefined(value) {
24774
25244
  }
24775
25245
 
24776
25246
  // src/connectors/adapters/azure-event-grid.ts
24777
- import { createHash as createHash3, timingSafeEqual } from "crypto";
25247
+ import { createHash as createHash4, timingSafeEqual } from "crypto";
24778
25248
  var API_VERSION2 = "2018-01-01";
24779
25249
  var MAX_BATCH_BYTES = 1024 * 1024;
24780
25250
  var MAX_EVENTS = 5e3;
@@ -24843,8 +25313,8 @@ var azureEventGridConnector = {
24843
25313
  }
24844
25314
  const provided = headerValue(headers, "x-tangle-eventgrid-secret");
24845
25315
  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();
25316
+ const expectedDigest = createHash4("sha256").update(credentials.deliverySecret).digest();
25317
+ const providedDigest = createHash4("sha256").update(provided).digest();
24848
25318
  return timingSafeEqual(expectedDigest, providedDigest) ? { valid: true } : { valid: false, reason: "Invalid x-tangle-eventgrid-secret delivery header" };
24849
25319
  },
24850
25320
  async handleInboundEvent({ rawBody }) {
@@ -27362,186 +27832,6 @@ var billplzConnector = declarativeRestConnector({
27362
27832
  ]
27363
27833
  });
27364
27834
 
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
27835
  // src/connectors/adapters/cal-com.ts
27546
27836
  var authorizeUrl = "https://app.cal.com/auth/oauth2/authorize";
27547
27837
  var tokenUrl = "https://api.cal.com/v2/auth/oauth2/token";
@@ -52737,13 +53027,11 @@ function microsoftTeams(opts) {
52737
53027
  async test(source) {
52738
53028
  try {
52739
53029
  const accessToken4 = await ensureFreshAccessToken11(source.credentials, clientId, clientSecret);
52740
- const res = await fetch(`${GRAPH2}/me?$select=id`, {
53030
+ const res = await fetch(`${GRAPH2}/me/joinedTeams`, {
52741
53031
  headers: { authorization: `Bearer ${accessToken4}` },
52742
53032
  signal: AbortSignal.timeout(8e3)
52743
53033
  });
52744
- if (res.status === 401 || res.status === 403) {
52745
- return { ok: false, reason: `Microsoft rejected token (${res.status}) \u2014 reconnect required` };
52746
- }
53034
+ if (res.status === 401 || res.status === 403) return microsoftGraphTestFailure(res);
52747
53035
  if (!res.ok) return { ok: false, reason: `Microsoft Graph returned ${res.status}` };
52748
53036
  return { ok: true };
52749
53037
  } catch (err) {
@@ -52804,7 +53092,7 @@ async function graphGet2(url, accessToken4, dataSourceId) {
52804
53092
  signal: AbortSignal.timeout(1e4)
52805
53093
  });
52806
53094
  if (res.status === 401 || res.status === 403) {
52807
- throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId);
53095
+ await throwMicrosoftGraphAccessError(res, dataSourceId);
52808
53096
  }
52809
53097
  if (!res.ok) {
52810
53098
  const text = await res.text().catch(() => "");
@@ -52824,7 +53112,7 @@ async function graphPost(url, accessToken4, body, dataSourceId, op) {
52824
53112
  signal: AbortSignal.timeout(15e3)
52825
53113
  });
52826
53114
  if (res.status === 401 || res.status === 403) {
52827
- throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId);
53115
+ await throwMicrosoftGraphAccessError(res, dataSourceId);
52828
53116
  }
52829
53117
  if (!res.ok) {
52830
53118
  const text = await res.text().catch(() => "");
@@ -52832,6 +53120,57 @@ async function graphPost(url, accessToken4, body, dataSourceId, op) {
52832
53120
  }
52833
53121
  return await res.json();
52834
53122
  }
53123
+ async function microsoftGraphErrorBody(res) {
53124
+ try {
53125
+ const body = await res.json();
53126
+ return body && typeof body === "object" ? body : void 0;
53127
+ } catch {
53128
+ return void 0;
53129
+ }
53130
+ }
53131
+ function microsoftGraphErrorText(body) {
53132
+ const code = typeof body?.error?.code === "string" ? body.error.code : "";
53133
+ const message = typeof body?.error?.message === "string" ? body.error.message : "";
53134
+ return `${code} ${message}`.trim();
53135
+ }
53136
+ function microsoftGraphRequiresLicense(body) {
53137
+ return /\b(?:valid\s+)?licen[cs]e\b/i.test(microsoftGraphErrorText(body));
53138
+ }
53139
+ async function throwMicrosoftGraphAccessError(res, dataSourceId) {
53140
+ const body = await microsoftGraphErrorBody(res);
53141
+ if (microsoftGraphRequiresLicense(body)) {
53142
+ throw new ProviderConfigError(
53143
+ "Microsoft Teams requires a valid Microsoft 365 or Teams license",
53144
+ dataSourceId,
53145
+ { status: res.status, reason: "licenseRequired", body }
53146
+ );
53147
+ }
53148
+ if (res.status === 403) {
53149
+ throw new ProviderConfigError("Microsoft Graph denied the requested permission", dataSourceId, {
53150
+ status: res.status,
53151
+ reason: typeof body?.error?.code === "string" && body.error.code ? body.error.code : "forbidden",
53152
+ body
53153
+ });
53154
+ }
53155
+ throw new CredentialsExpired(`Microsoft Graph rejected token (${res.status})`, dataSourceId, {
53156
+ status: res.status,
53157
+ reason: typeof body?.error?.code === "string" && body.error.code ? body.error.code : "unauthorized",
53158
+ body
53159
+ });
53160
+ }
53161
+ async function microsoftGraphTestFailure(res) {
53162
+ const body = await microsoftGraphErrorBody(res);
53163
+ if (microsoftGraphRequiresLicense(body)) {
53164
+ return {
53165
+ ok: false,
53166
+ reason: "Microsoft Teams requires a valid Microsoft 365 or Teams license"
53167
+ };
53168
+ }
53169
+ if (res.status === 403) {
53170
+ return { ok: false, reason: "Microsoft Graph denied the requested permission" };
53171
+ }
53172
+ return { ok: false, reason: `Microsoft rejected token (${res.status}) \u2014 reconnect required` };
53173
+ }
52835
53174
  function escapeOData2(value) {
52836
53175
  return value.replace(/'/g, "''");
52837
53176
  }
@@ -55827,6 +56166,7 @@ var NOTION_SPEC = {
55827
56166
  scopes: [],
55828
56167
  clientIdEnv: "NOTION_OAUTH_CLIENT_ID",
55829
56168
  clientSecretEnv: "NOTION_OAUTH_CLIENT_SECRET",
56169
+ tokenClientAuthMethod: "client_secret_basic",
55830
56170
  extraAuthParams: { owner: "user" }
55831
56171
  },
55832
56172
  category: "doc",
@@ -56149,11 +56489,9 @@ function notion(opts) {
56149
56489
  ...withNotionOverrides(declarativeRestConnector(NOTION_SPEC)),
56150
56490
  /**
56151
56491
  * Notion's token endpoint follows RFC 6749 with one twist — it REQUIRES
56152
- * HTTP Basic auth (client_id:client_secret) and does NOT accept the client
56153
- * credentials in the form body, so we POST inline rather than via the
56154
- * generic `exchangeAuthorizationCode` helper. The workspace_id/bot_id come
56155
- * back in the response and we stash them in `metadata` so the agent can
56156
- * address resources by workspace where useful.
56492
+ * HTTP Basic auth and returns workspace metadata with the grant. We use
56493
+ * the shared Basic encoding and redaction primitives, then retain that
56494
+ * metadata for resource selection.
56157
56495
  */
56158
56496
  async exchangeOAuth(input) {
56159
56497
  if (!clientId || !clientSecret) {
@@ -56168,22 +56506,62 @@ function notion(opts) {
56168
56506
  redirect_uri: input.redirectUri,
56169
56507
  code_verifier: input.codeVerifier
56170
56508
  });
56171
- const res = await fetch(TOKEN_URL13, {
56172
- method: "POST",
56173
- headers: {
56174
- authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`,
56175
- "content-type": "application/x-www-form-urlencoded",
56176
- accept: "application/json",
56177
- "Notion-Version": NOTION_VERSION
56178
- },
56179
- body,
56180
- signal: AbortSignal.timeout(15e3)
56181
- });
56509
+ const authorization = createOAuthBasicAuthorizationHeader(
56510
+ clientId,
56511
+ clientSecret
56512
+ );
56513
+ const redactionValues = [
56514
+ input.code,
56515
+ input.codeVerifier,
56516
+ ...oauthClientCredentialRedactionValues(
56517
+ clientId,
56518
+ clientSecret,
56519
+ authorization
56520
+ )
56521
+ ];
56522
+ let res;
56523
+ try {
56524
+ res = await fetch(TOKEN_URL13, {
56525
+ method: "POST",
56526
+ headers: {
56527
+ authorization,
56528
+ "content-type": "application/x-www-form-urlencoded",
56529
+ accept: "application/json",
56530
+ "Notion-Version": NOTION_VERSION
56531
+ },
56532
+ body,
56533
+ signal: AbortSignal.timeout(15e3)
56534
+ });
56535
+ } catch (cause) {
56536
+ const detail = redactOAuthSensitiveText(
56537
+ cause?.message ?? "unknown",
56538
+ redactionValues
56539
+ );
56540
+ throw new Error(`Notion OAuth token exchange transport error: ${detail}`);
56541
+ }
56182
56542
  if (!res.ok) {
56183
56543
  const text = await res.text().catch(() => "");
56184
- throw new Error(`Notion OAuth token exchange failed: ${res.status} \u2014 ${text.slice(0, 200)}`);
56544
+ const statusText = redactOAuthSensitiveText(
56545
+ res.statusText,
56546
+ redactionValues
56547
+ );
56548
+ const detail = redactOAuthSensitiveText(text, redactionValues).slice(
56549
+ 0,
56550
+ 200
56551
+ );
56552
+ throw new Error(
56553
+ `Notion OAuth token exchange failed: ${res.status} ${statusText} \u2014 ${detail}`
56554
+ );
56555
+ }
56556
+ let json;
56557
+ try {
56558
+ json = await res.json();
56559
+ } catch {
56560
+ throw new Error("Notion OAuth token exchange returned invalid JSON");
56561
+ }
56562
+ if (!json.access_token) {
56563
+ throw new Error("Notion OAuth token exchange returned no access_token");
56185
56564
  }
56186
- const json = await res.json();
56187
56565
  return {
56188
56566
  credentials: {
56189
56567
  kind: "oauth2",
@@ -56214,6 +56592,7 @@ function notion(opts) {
56214
56592
  tokenUrl: TOKEN_URL13,
56215
56593
  clientId,
56216
56594
  clientSecret,
56595
+ tokenClientAuthMethod: "client_secret_basic",
56217
56596
  refreshToken: creds.refreshToken
56218
56597
  });
56219
56598
  return {
@@ -69160,7 +69539,8 @@ var TWITTER_SPEC = {
69160
69539
  tokenUrl: TOKEN_URL19,
69161
69540
  scopes: SCOPES13,
69162
69541
  clientIdEnv: "TWITTER_OAUTH_CLIENT_ID",
69163
- clientSecretEnv: "TWITTER_OAUTH_CLIENT_SECRET"
69542
+ clientSecretEnv: "TWITTER_OAUTH_CLIENT_SECRET",
69543
+ tokenClientAuthMethod: "client_secret_basic"
69164
69544
  },
69165
69545
  {
69166
69546
  kind: "api-key",
@@ -69457,21 +69837,61 @@ function twitter(opts) {
69457
69837
  };
69458
69838
  }
69459
69839
  async function twitterTokenRequest(clientId, clientSecret, params) {
69460
- const res = await fetch(TOKEN_URL19, {
69461
- method: "POST",
69462
- headers: {
69463
- authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`,
69464
- "content-type": "application/x-www-form-urlencoded",
69465
- accept: "application/json"
69466
- },
69467
- body: new URLSearchParams(params),
69468
- signal: AbortSignal.timeout(15e3)
69469
- });
69840
+ const authorization = createOAuthBasicAuthorizationHeader(
69841
+ clientId,
69842
+ clientSecret
69843
+ );
69844
+ const requestSecrets = [
69845
+ params.code,
69846
+ params.code_verifier,
69847
+ params.refresh_token
69848
+ ].filter((value) => typeof value === "string" && value.length > 0);
69849
+ const redactionValues = [
69850
+ ...requestSecrets,
69851
+ ...oauthClientCredentialRedactionValues(
69852
+ clientId,
69853
+ clientSecret,
69854
+ authorization
69855
+ )
69856
+ ];
69857
+ let res;
69858
+ try {
69859
+ res = await fetch(TOKEN_URL19, {
69860
+ method: "POST",
69861
+ headers: {
69862
+ authorization,
69863
+ "content-type": "application/x-www-form-urlencoded",
69864
+ accept: "application/json"
69865
+ },
69866
+ body: new URLSearchParams(params),
69867
+ signal: AbortSignal.timeout(15e3)
69868
+ });
69869
+ } catch (cause) {
69870
+ const detail = redactOAuthSensitiveText(
69871
+ cause?.message ?? "unknown",
69872
+ redactionValues
69873
+ );
69874
+ throw new Error(
69875
+ `twitter ${params.grant_type} token transport error: ${detail}`
69876
+ );
69877
+ }
69470
69878
  if (!res.ok) {
69471
69879
  const text = await res.text().catch(() => "");
69472
- throw new Error(`twitter ${params.grant_type} token request failed: ${res.status} ${res.statusText} \u2014 ${text.slice(0, 200)}`);
69880
+ const statusText = redactOAuthSensitiveText(res.statusText, redactionValues);
69881
+ const detail = redactOAuthSensitiveText(text, redactionValues).slice(0, 200);
69882
+ throw new Error(
69883
+ `twitter ${params.grant_type} token request failed: ${res.status} ${statusText} \u2014 ${detail}`
69884
+ );
69885
+ }
69886
+ let json;
69887
+ try {
69888
+ json = await res.json();
69889
+ } catch {
69890
+ throw new Error(`twitter ${params.grant_type} token request returned invalid JSON`);
69891
+ }
69892
+ if (!json.access_token) {
69893
+ throw new Error(`twitter ${params.grant_type} token request returned no access_token`);
69473
69894
  }
69474
- const json = await res.json();
69475
69895
  return {
69476
69896
  accessToken: json.access_token,
69477
69897
  refreshToken: json.refresh_token,
@@ -83842,7 +84262,7 @@ var CONNECTOR_ADAPTER_FACTORIES = [
83842
84262
  clientId: "PUSHBULLET_OAUTH_CLIENT_ID",
83843
84263
  clientSecret: "PUSHBULLET_OAUTH_CLIENT_SECRET"
83844
84264
  }),
83845
- defineFactoryAdapter(() => redditConnector, {
84265
+ defineFactoryAdapter(reddit, {
83846
84266
  clientId: "REDDIT_OAUTH_CLIENT_ID",
83847
84267
  clientSecret: "REDDIT_OAUTH_CLIENT_SECRET"
83848
84268
  }),
@@ -148676,6 +149096,11 @@ export {
148676
149096
  consumePendingFlow,
148677
149097
  exchangeAuthorizationCode,
148678
149098
  refreshAccessToken,
149099
+ createOAuthTokenRequestHeaders,
149100
+ formEncodeOAuthClientCredential,
149101
+ createOAuthBasicAuthorizationHeader,
149102
+ oauthClientCredentialRedactionValues,
149103
+ redactOAuthSensitiveText,
148679
149104
  _resetPendingFlowsForTests,
148680
149105
  declarativeRestConnector,
148681
149106
  oktaConnector,
@@ -148752,6 +149177,7 @@ export {
148752
149177
  paychexConnector,
148753
149178
  pushbulletConnector,
148754
149179
  redditConnector,
149180
+ reddit,
148755
149181
  ripplingConnector,
148756
149182
  sanityConnector,
148757
149183
  sendpulseConnector,
@@ -149332,4 +149758,4 @@ export {
149332
149758
  getresponseConnector,
149333
149759
  adapters_exports
149334
149760
  };
149335
- //# sourceMappingURL=chunk-HNEEIMQC.js.map
149761
+ //# sourceMappingURL=chunk-2JRYKWYX.js.map