integrate-sdk 0.9.42-dev.0 → 0.9.43-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2556,6 +2556,148 @@ class IndexedDBStorage {
2556
2556
 
2557
2557
  // ../oauth/manager.ts
2558
2558
  init_email_fetcher();
2559
+
2560
+ // ../oauth/refresh.ts
2561
+ class RefreshRejectedError extends Error {
2562
+ provider;
2563
+ constructor(provider, message) {
2564
+ super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
2565
+ this.name = "RefreshRejectedError";
2566
+ this.provider = provider;
2567
+ }
2568
+ }
2569
+
2570
+ class RefreshTransientError extends Error {
2571
+ provider;
2572
+ constructor(provider, message) {
2573
+ super(message);
2574
+ this.name = "RefreshTransientError";
2575
+ this.provider = provider;
2576
+ }
2577
+ }
2578
+ var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
2579
+ function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
2580
+ if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
2581
+ return false;
2582
+ }
2583
+ const expiresAtMs = Date.parse(tokenData.expiresAt);
2584
+ if (Number.isNaN(expiresAtMs)) {
2585
+ return false;
2586
+ }
2587
+ return expiresAtMs - Date.now() <= windowMs;
2588
+ }
2589
+ async function refreshViaMcp(opts) {
2590
+ const url = new URL("/oauth/refresh", opts.serverUrl);
2591
+ const body = {
2592
+ provider: opts.provider,
2593
+ refresh_token: opts.refreshToken,
2594
+ client_id: opts.clientId
2595
+ };
2596
+ if (opts.clientSecret) {
2597
+ body.client_secret = opts.clientSecret;
2598
+ }
2599
+ if (opts.subdomain) {
2600
+ body.subdomain = opts.subdomain;
2601
+ }
2602
+ if (opts.extraConfig) {
2603
+ for (const [key, value] of Object.entries(opts.extraConfig)) {
2604
+ if (body[key] === undefined) {
2605
+ body[key] = value;
2606
+ }
2607
+ }
2608
+ }
2609
+ const headers = { "Content-Type": "application/json" };
2610
+ if (opts.apiKey) {
2611
+ headers["X-API-KEY"] = opts.apiKey;
2612
+ }
2613
+ let response;
2614
+ try {
2615
+ response = await fetch(url.toString(), {
2616
+ method: "POST",
2617
+ headers,
2618
+ body: JSON.stringify(body),
2619
+ signal: opts.signal
2620
+ });
2621
+ } catch (err) {
2622
+ throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
2623
+ }
2624
+ if (response.status === 401) {
2625
+ let parsed = {};
2626
+ try {
2627
+ parsed = await response.json();
2628
+ } catch {}
2629
+ if (parsed.error === "invalid_grant") {
2630
+ throw new RefreshRejectedError(opts.provider);
2631
+ }
2632
+ throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
2633
+ }
2634
+ if (!response.ok) {
2635
+ let text = "";
2636
+ try {
2637
+ text = await response.text();
2638
+ } catch {}
2639
+ throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
2640
+ }
2641
+ let result;
2642
+ try {
2643
+ result = await response.json();
2644
+ } catch (err) {
2645
+ throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
2646
+ }
2647
+ if (!result.accessToken) {
2648
+ throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
2649
+ }
2650
+ if (!result.refreshToken) {
2651
+ result.refreshToken = opts.refreshToken;
2652
+ }
2653
+ return result;
2654
+ }
2655
+ async function resolveAccessToken(opts) {
2656
+ const { provider, currentTokens, force = false } = opts;
2657
+ const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
2658
+ if (!needsRefresh || !currentTokens.refreshToken) {
2659
+ return currentTokens.accessToken;
2660
+ }
2661
+ try {
2662
+ const refreshed = await refreshViaMcp({
2663
+ provider,
2664
+ refreshToken: currentTokens.refreshToken,
2665
+ clientId: opts.providerOAuth.clientId,
2666
+ clientSecret: opts.providerOAuth.clientSecret,
2667
+ subdomain: opts.providerOAuth.subdomain,
2668
+ extraConfig: opts.providerOAuth.extraConfig,
2669
+ serverUrl: opts.serverUrl,
2670
+ apiKey: opts.apiKey
2671
+ });
2672
+ const updated = {
2673
+ ...currentTokens,
2674
+ accessToken: refreshed.accessToken,
2675
+ refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
2676
+ tokenType: refreshed.tokenType || currentTokens.tokenType,
2677
+ expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
2678
+ expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
2679
+ scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
2680
+ };
2681
+ if (opts.setProviderToken) {
2682
+ try {
2683
+ await opts.setProviderToken(provider, updated, updated.email, opts.context);
2684
+ } catch {}
2685
+ }
2686
+ return updated.accessToken;
2687
+ } catch (err) {
2688
+ if (err instanceof RefreshRejectedError) {
2689
+ if (opts.setProviderToken) {
2690
+ try {
2691
+ await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
2692
+ } catch {}
2693
+ }
2694
+ throw err;
2695
+ }
2696
+ return currentTokens.accessToken;
2697
+ }
2698
+ }
2699
+
2700
+ // ../oauth/manager.ts
2559
2701
  init_logger();
2560
2702
  var logger6 = createLogger("OAuth");
2561
2703
 
@@ -2571,7 +2713,10 @@ class OAuthManager {
2571
2713
  removeTokenCallback;
2572
2714
  indexedDBStorage;
2573
2715
  skipLocalStorage = false;
2574
- constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks) {
2716
+ providerOAuth = {};
2717
+ mcpServerUrl;
2718
+ mcpApiKey;
2719
+ constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
2575
2720
  this.oauthApiBase = oauthApiBase;
2576
2721
  this.apiBaseUrl = apiBaseUrl;
2577
2722
  this.windowManager = new OAuthWindowManager;
@@ -2583,6 +2728,9 @@ class OAuthManager {
2583
2728
  this.getTokenCallback = tokenCallbacks?.getProviderToken;
2584
2729
  this.setTokenCallback = tokenCallbacks?.setProviderToken;
2585
2730
  this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
2731
+ this.providerOAuth = refreshConfig?.providers ?? {};
2732
+ this.mcpServerUrl = refreshConfig?.mcpServerUrl;
2733
+ this.mcpApiKey = refreshConfig?.apiKey;
2586
2734
  this.indexedDBStorage = new IndexedDBStorage;
2587
2735
  this.cleanupExpiredPendingAuths();
2588
2736
  }
@@ -2792,7 +2940,9 @@ class OAuthManager {
2792
2940
  try {
2793
2941
  const tokenData = await this.getTokenCallback(provider, email, context);
2794
2942
  if (tokenData) {
2795
- this.providerTokens.set(provider, tokenData);
2943
+ const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
2944
+ this.providerTokens.set(provider, refreshed);
2945
+ return refreshed;
2796
2946
  }
2797
2947
  return tokenData;
2798
2948
  } catch (error) {
@@ -2832,6 +2982,59 @@ class OAuthManager {
2832
2982
  }
2833
2983
  await this.saveProviderToken(provider, tokenData, tokenEmail, context);
2834
2984
  }
2985
+ configureTokenRefresh(refreshConfig) {
2986
+ if (refreshConfig.providers) {
2987
+ this.providerOAuth = refreshConfig.providers;
2988
+ }
2989
+ if (refreshConfig.mcpServerUrl !== undefined) {
2990
+ this.mcpServerUrl = refreshConfig.mcpServerUrl;
2991
+ }
2992
+ if (refreshConfig.apiKey !== undefined) {
2993
+ this.mcpApiKey = refreshConfig.apiKey;
2994
+ }
2995
+ }
2996
+ async maybeRefreshTokenData(provider, tokenData, context) {
2997
+ const credentials = this.providerOAuth[provider];
2998
+ const serverUrl = this.mcpServerUrl;
2999
+ if (!credentials || !serverUrl) {
3000
+ return tokenData;
3001
+ }
3002
+ if (!shouldRefreshToken(tokenData)) {
3003
+ return tokenData;
3004
+ }
3005
+ try {
3006
+ const newAccessToken = await resolveAccessToken({
3007
+ provider,
3008
+ currentTokens: tokenData,
3009
+ providerOAuth: {
3010
+ clientId: credentials.clientId,
3011
+ clientSecret: credentials.clientSecret,
3012
+ subdomain: credentials.config?.subdomain
3013
+ },
3014
+ serverUrl,
3015
+ apiKey: this.mcpApiKey,
3016
+ setProviderToken: this.setTokenCallback,
3017
+ context
3018
+ });
3019
+ if (newAccessToken === tokenData.accessToken) {
3020
+ return tokenData;
3021
+ }
3022
+ if (this.getTokenCallback) {
3023
+ try {
3024
+ const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
3025
+ if (reloaded) {
3026
+ return reloaded;
3027
+ }
3028
+ } catch {}
3029
+ }
3030
+ return { ...tokenData, accessToken: newAccessToken };
3031
+ } catch (err) {
3032
+ if (err instanceof RefreshRejectedError) {
3033
+ throw err;
3034
+ }
3035
+ return tokenData;
3036
+ }
3037
+ }
2835
3038
  clearProviderToken(provider) {
2836
3039
  this.providerTokens.delete(provider);
2837
3040
  if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
@@ -3288,10 +3491,26 @@ class MCPClientBase {
3288
3491
  };
3289
3492
  this.onReauthRequired = config.onReauthRequired;
3290
3493
  this.maxReauthRetries = config.maxReauthRetries ?? 1;
3494
+ const refreshProviders = {};
3495
+ for (const integration of this.integrations) {
3496
+ const oauth = integration?.oauth;
3497
+ if (oauth?.clientId && oauth?.provider) {
3498
+ refreshProviders[oauth.provider] = {
3499
+ clientId: oauth.clientId,
3500
+ clientSecret: oauth.clientSecret,
3501
+ config: oauth.config
3502
+ };
3503
+ }
3504
+ }
3505
+ const mcpServerUrl = config.serverUrl;
3291
3506
  this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
3292
3507
  getProviderToken: config.getProviderToken,
3293
3508
  setProviderToken: config.setProviderToken,
3294
3509
  removeProviderToken: config.removeProviderToken
3510
+ }, {
3511
+ providers: refreshProviders,
3512
+ mcpServerUrl,
3513
+ apiKey: config.apiKey
3295
3514
  });
3296
3515
  this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
3297
3516
  for (const integration of this.integrations) {
@@ -2556,6 +2556,148 @@ class IndexedDBStorage {
2556
2556
 
2557
2557
  // ../oauth/manager.ts
2558
2558
  init_email_fetcher();
2559
+
2560
+ // ../oauth/refresh.ts
2561
+ class RefreshRejectedError extends Error {
2562
+ provider;
2563
+ constructor(provider, message) {
2564
+ super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
2565
+ this.name = "RefreshRejectedError";
2566
+ this.provider = provider;
2567
+ }
2568
+ }
2569
+
2570
+ class RefreshTransientError extends Error {
2571
+ provider;
2572
+ constructor(provider, message) {
2573
+ super(message);
2574
+ this.name = "RefreshTransientError";
2575
+ this.provider = provider;
2576
+ }
2577
+ }
2578
+ var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
2579
+ function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
2580
+ if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
2581
+ return false;
2582
+ }
2583
+ const expiresAtMs = Date.parse(tokenData.expiresAt);
2584
+ if (Number.isNaN(expiresAtMs)) {
2585
+ return false;
2586
+ }
2587
+ return expiresAtMs - Date.now() <= windowMs;
2588
+ }
2589
+ async function refreshViaMcp(opts) {
2590
+ const url = new URL("/oauth/refresh", opts.serverUrl);
2591
+ const body = {
2592
+ provider: opts.provider,
2593
+ refresh_token: opts.refreshToken,
2594
+ client_id: opts.clientId
2595
+ };
2596
+ if (opts.clientSecret) {
2597
+ body.client_secret = opts.clientSecret;
2598
+ }
2599
+ if (opts.subdomain) {
2600
+ body.subdomain = opts.subdomain;
2601
+ }
2602
+ if (opts.extraConfig) {
2603
+ for (const [key, value] of Object.entries(opts.extraConfig)) {
2604
+ if (body[key] === undefined) {
2605
+ body[key] = value;
2606
+ }
2607
+ }
2608
+ }
2609
+ const headers = { "Content-Type": "application/json" };
2610
+ if (opts.apiKey) {
2611
+ headers["X-API-KEY"] = opts.apiKey;
2612
+ }
2613
+ let response;
2614
+ try {
2615
+ response = await fetch(url.toString(), {
2616
+ method: "POST",
2617
+ headers,
2618
+ body: JSON.stringify(body),
2619
+ signal: opts.signal
2620
+ });
2621
+ } catch (err) {
2622
+ throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
2623
+ }
2624
+ if (response.status === 401) {
2625
+ let parsed = {};
2626
+ try {
2627
+ parsed = await response.json();
2628
+ } catch {}
2629
+ if (parsed.error === "invalid_grant") {
2630
+ throw new RefreshRejectedError(opts.provider);
2631
+ }
2632
+ throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
2633
+ }
2634
+ if (!response.ok) {
2635
+ let text = "";
2636
+ try {
2637
+ text = await response.text();
2638
+ } catch {}
2639
+ throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
2640
+ }
2641
+ let result;
2642
+ try {
2643
+ result = await response.json();
2644
+ } catch (err) {
2645
+ throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
2646
+ }
2647
+ if (!result.accessToken) {
2648
+ throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
2649
+ }
2650
+ if (!result.refreshToken) {
2651
+ result.refreshToken = opts.refreshToken;
2652
+ }
2653
+ return result;
2654
+ }
2655
+ async function resolveAccessToken(opts) {
2656
+ const { provider, currentTokens, force = false } = opts;
2657
+ const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
2658
+ if (!needsRefresh || !currentTokens.refreshToken) {
2659
+ return currentTokens.accessToken;
2660
+ }
2661
+ try {
2662
+ const refreshed = await refreshViaMcp({
2663
+ provider,
2664
+ refreshToken: currentTokens.refreshToken,
2665
+ clientId: opts.providerOAuth.clientId,
2666
+ clientSecret: opts.providerOAuth.clientSecret,
2667
+ subdomain: opts.providerOAuth.subdomain,
2668
+ extraConfig: opts.providerOAuth.extraConfig,
2669
+ serverUrl: opts.serverUrl,
2670
+ apiKey: opts.apiKey
2671
+ });
2672
+ const updated = {
2673
+ ...currentTokens,
2674
+ accessToken: refreshed.accessToken,
2675
+ refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
2676
+ tokenType: refreshed.tokenType || currentTokens.tokenType,
2677
+ expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
2678
+ expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
2679
+ scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
2680
+ };
2681
+ if (opts.setProviderToken) {
2682
+ try {
2683
+ await opts.setProviderToken(provider, updated, updated.email, opts.context);
2684
+ } catch {}
2685
+ }
2686
+ return updated.accessToken;
2687
+ } catch (err) {
2688
+ if (err instanceof RefreshRejectedError) {
2689
+ if (opts.setProviderToken) {
2690
+ try {
2691
+ await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
2692
+ } catch {}
2693
+ }
2694
+ throw err;
2695
+ }
2696
+ return currentTokens.accessToken;
2697
+ }
2698
+ }
2699
+
2700
+ // ../oauth/manager.ts
2559
2701
  init_logger();
2560
2702
  var logger6 = createLogger("OAuth");
2561
2703
 
@@ -2571,7 +2713,10 @@ class OAuthManager {
2571
2713
  removeTokenCallback;
2572
2714
  indexedDBStorage;
2573
2715
  skipLocalStorage = false;
2574
- constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks) {
2716
+ providerOAuth = {};
2717
+ mcpServerUrl;
2718
+ mcpApiKey;
2719
+ constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
2575
2720
  this.oauthApiBase = oauthApiBase;
2576
2721
  this.apiBaseUrl = apiBaseUrl;
2577
2722
  this.windowManager = new OAuthWindowManager;
@@ -2583,6 +2728,9 @@ class OAuthManager {
2583
2728
  this.getTokenCallback = tokenCallbacks?.getProviderToken;
2584
2729
  this.setTokenCallback = tokenCallbacks?.setProviderToken;
2585
2730
  this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
2731
+ this.providerOAuth = refreshConfig?.providers ?? {};
2732
+ this.mcpServerUrl = refreshConfig?.mcpServerUrl;
2733
+ this.mcpApiKey = refreshConfig?.apiKey;
2586
2734
  this.indexedDBStorage = new IndexedDBStorage;
2587
2735
  this.cleanupExpiredPendingAuths();
2588
2736
  }
@@ -2792,7 +2940,9 @@ class OAuthManager {
2792
2940
  try {
2793
2941
  const tokenData = await this.getTokenCallback(provider, email, context);
2794
2942
  if (tokenData) {
2795
- this.providerTokens.set(provider, tokenData);
2943
+ const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
2944
+ this.providerTokens.set(provider, refreshed);
2945
+ return refreshed;
2796
2946
  }
2797
2947
  return tokenData;
2798
2948
  } catch (error) {
@@ -2832,6 +2982,59 @@ class OAuthManager {
2832
2982
  }
2833
2983
  await this.saveProviderToken(provider, tokenData, tokenEmail, context);
2834
2984
  }
2985
+ configureTokenRefresh(refreshConfig) {
2986
+ if (refreshConfig.providers) {
2987
+ this.providerOAuth = refreshConfig.providers;
2988
+ }
2989
+ if (refreshConfig.mcpServerUrl !== undefined) {
2990
+ this.mcpServerUrl = refreshConfig.mcpServerUrl;
2991
+ }
2992
+ if (refreshConfig.apiKey !== undefined) {
2993
+ this.mcpApiKey = refreshConfig.apiKey;
2994
+ }
2995
+ }
2996
+ async maybeRefreshTokenData(provider, tokenData, context) {
2997
+ const credentials = this.providerOAuth[provider];
2998
+ const serverUrl = this.mcpServerUrl;
2999
+ if (!credentials || !serverUrl) {
3000
+ return tokenData;
3001
+ }
3002
+ if (!shouldRefreshToken(tokenData)) {
3003
+ return tokenData;
3004
+ }
3005
+ try {
3006
+ const newAccessToken = await resolveAccessToken({
3007
+ provider,
3008
+ currentTokens: tokenData,
3009
+ providerOAuth: {
3010
+ clientId: credentials.clientId,
3011
+ clientSecret: credentials.clientSecret,
3012
+ subdomain: credentials.config?.subdomain
3013
+ },
3014
+ serverUrl,
3015
+ apiKey: this.mcpApiKey,
3016
+ setProviderToken: this.setTokenCallback,
3017
+ context
3018
+ });
3019
+ if (newAccessToken === tokenData.accessToken) {
3020
+ return tokenData;
3021
+ }
3022
+ if (this.getTokenCallback) {
3023
+ try {
3024
+ const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
3025
+ if (reloaded) {
3026
+ return reloaded;
3027
+ }
3028
+ } catch {}
3029
+ }
3030
+ return { ...tokenData, accessToken: newAccessToken };
3031
+ } catch (err) {
3032
+ if (err instanceof RefreshRejectedError) {
3033
+ throw err;
3034
+ }
3035
+ return tokenData;
3036
+ }
3037
+ }
2835
3038
  clearProviderToken(provider) {
2836
3039
  this.providerTokens.delete(provider);
2837
3040
  if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
@@ -3288,10 +3491,26 @@ class MCPClientBase {
3288
3491
  };
3289
3492
  this.onReauthRequired = config.onReauthRequired;
3290
3493
  this.maxReauthRetries = config.maxReauthRetries ?? 1;
3494
+ const refreshProviders = {};
3495
+ for (const integration of this.integrations) {
3496
+ const oauth = integration?.oauth;
3497
+ if (oauth?.clientId && oauth?.provider) {
3498
+ refreshProviders[oauth.provider] = {
3499
+ clientId: oauth.clientId,
3500
+ clientSecret: oauth.clientSecret,
3501
+ config: oauth.config
3502
+ };
3503
+ }
3504
+ }
3505
+ const mcpServerUrl = config.serverUrl;
3291
3506
  this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
3292
3507
  getProviderToken: config.getProviderToken,
3293
3508
  setProviderToken: config.setProviderToken,
3294
3509
  removeProviderToken: config.removeProviderToken
3510
+ }, {
3511
+ providers: refreshProviders,
3512
+ mcpServerUrl,
3513
+ apiKey: config.apiKey
3295
3514
  });
3296
3515
  this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
3297
3516
  for (const integration of this.integrations) {