Skip to content

Commit 64fcb5d

Browse files
committed
fixing nango dependency resolution
1 parent 20f0b08 commit 64fcb5d

File tree

5 files changed

+25
-108
lines changed

5 files changed

+25
-108
lines changed

packages/agents-core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
"@better-auth/sso": "^1.4.0",
6161
"@hono/node-server": "^1.14.3",
6262
"@modelcontextprotocol/sdk": "^1.17.2",
63+
"@nangohq/node": "^0.69.5",
64+
"@nangohq/types": "^0.69.5",
6365
"@openrouter/ai-sdk-provider": "^1.2.0",
6466
"@opentelemetry/api": "^1.9.0",
6567
"ai": "6.0.0-beta.124",
@@ -86,8 +88,6 @@
8688
"zod": "^4.1.11"
8789
},
8890
"optionalDependencies": {
89-
"@nangohq/node": "^0.69.5",
90-
"@nangohq/types": "^0.69.5",
9191
"@opentelemetry/auto-instrumentations-node": "^0.62.0",
9292
"@opentelemetry/baggage-span-processor": "^0.4.0",
9393
"@opentelemetry/exporter-jaeger": "^2.0.1",

packages/agents-core/src/credential-stores/defaults.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { CredentialStore } from '../types/server';
22
import { DEFAULT_NANGO_STORE_ID } from './default-constants';
33
import { createKeyChainStore } from './keychain-store';
44
import { InMemoryCredentialStore } from './memory-store';
5-
import { createNangoCredentialStore, isNangoAvailable } from './nango-store';
5+
import { createNangoCredentialStore } from './nango-store';
66

77
/**
88
* Create default credential stores based on environment variables
@@ -13,18 +13,14 @@ export function createDefaultCredentialStores(): CredentialStore[] {
1313
// Always include in-memory store
1414
stores.push(new InMemoryCredentialStore('memory-default'));
1515

16-
// Include Nango store if NANGO_SECRET_KEY is set AND Nango is installed
17-
if (process.env.NANGO_SECRET_KEY && isNangoAvailable()) {
18-
try {
19-
stores.push(
20-
createNangoCredentialStore(DEFAULT_NANGO_STORE_ID, {
21-
apiUrl: process.env.NANGO_SERVER_URL || 'https://api.nango.dev',
22-
secretKey: process.env.NANGO_SECRET_KEY,
23-
})
24-
);
25-
} catch (error) {
26-
console.warn('Failed to create Nango store:', error instanceof Error ? error.message : error);
27-
}
16+
// Include Nango store if NANGO_SECRET_KEY is set
17+
if (process.env.NANGO_SECRET_KEY) {
18+
stores.push(
19+
createNangoCredentialStore(DEFAULT_NANGO_STORE_ID, {
20+
apiUrl: process.env.NANGO_SERVER_URL || 'https://api.nango.dev',
21+
secretKey: process.env.NANGO_SECRET_KEY,
22+
})
23+
);
2824
}
2925

3026
try {

packages/agents-core/src/credential-stores/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,4 @@ export { CredentialStoreRegistry } from './CredentialStoreRegistry';
22
export { createDefaultCredentialStores } from './defaults';
33
export { createKeyChainStore, KeyChainStore } from './keychain-store';
44
export { InMemoryCredentialStore } from './memory-store';
5-
export {
6-
createNangoCredentialStore,
7-
isNangoAvailable,
8-
NangoCredentialStore,
9-
} from './nango-store';
5+
export { createNangoCredentialStore, NangoCredentialStore } from './nango-store';

packages/agents-core/src/credential-stores/nango-store.ts

Lines changed: 7 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,12 @@
11
import { z } from '@hono/zod-openapi';
2+
import { type AllAuthCredentials, type AuthModeType, Nango } from '@nangohq/node';
3+
import type { ApiKeyCredentials, ApiPublicIntegration } from '@nangohq/types';
24
import { CredentialStoreType } from '../types';
35
import type { CredentialStore } from '../types/server';
46
import { getLogger } from '../utils/logger';
57

68
const logger = getLogger('nango-credential-store');
79

8-
// Dynamic import types - these are loaded at runtime if Nango is installed
9-
type NangoType = import('@nangohq/node').Nango;
10-
type AllAuthCredentials = import('@nangohq/node').AllAuthCredentials;
11-
type AuthModeType = import('@nangohq/node').AuthModeType;
12-
type ApiKeyCredentials = import('@nangohq/types').ApiKeyCredentials;
13-
type ApiPublicIntegration = import('@nangohq/types').ApiPublicIntegration;
14-
15-
// Lazy-loaded Nango module
16-
let nangoModule: typeof import('@nangohq/node') | null = null;
17-
18-
/**
19-
* Check if Nango is available
20-
*/
21-
export function isNangoAvailable(): boolean {
22-
try {
23-
require.resolve('@nangohq/node');
24-
return true;
25-
} catch {
26-
return false;
27-
}
28-
}
29-
30-
/**
31-
* Dynamically load Nango module
32-
*/
33-
async function loadNangoModule(): Promise<typeof import('@nangohq/node')> {
34-
if (nangoModule) {
35-
return nangoModule;
36-
}
37-
38-
try {
39-
nangoModule = await import('@nangohq/node');
40-
return nangoModule;
41-
} catch {
42-
throw new Error(
43-
'Nango is not installed. To use Nango credential store, install it with: npm install @nangohq/node @nangohq/types'
44-
);
45-
}
46-
}
47-
4810
const CredentialKeySchema = z.object({
4911
connectionId: z.string().min(1, 'connectionId must be a non-empty string'),
5012
providerConfigKey: z.string().min(1, 'providerConfigKey must be a non-empty string'),
@@ -114,35 +76,20 @@ function isSupportedAuthMode(mode: unknown): mode is SupportedAuthMode {
11476
/**
11577
* Nango-based CredentialStore that fetches OAuth credentials from Nango API
11678
* Uses connectionId and providerConfigKey from metadata to fetch live credentials
117-
*
118-
* Note: This store requires the @nangohq/node and @nangohq/types packages to be installed.
119-
* If Nango is not installed, the store will throw helpful error messages.
12079
*/
12180
export class NangoCredentialStore implements CredentialStore {
12281
public readonly id: string;
12382
public readonly type = CredentialStoreType.nango;
12483
private nangoConfig: NangoConfig;
125-
private nangoClient: NangoType | null = null;
84+
private nangoClient: Nango;
12685

12786
constructor(id: string, config: NangoConfig) {
12887
this.id = id;
12988
this.nangoConfig = config;
130-
}
131-
132-
/**
133-
* Initialize Nango client lazily
134-
*/
135-
private async getNangoClient(): Promise<NangoType> {
136-
if (this.nangoClient) {
137-
return this.nangoClient;
138-
}
139-
140-
const { Nango } = await loadNangoModule();
14189
this.nangoClient = new Nango({
14290
secretKey: this.nangoConfig.secretKey,
14391
host: this.nangoConfig.apiUrl,
14492
});
145-
return this.nangoClient;
14693
}
14794

14895
private getAccessToken(credentials: AllAuthCredentials): Record<string, any> | null {
@@ -239,8 +186,7 @@ export class NangoCredentialStore implements CredentialStore {
239186
uniqueKey: string
240187
): Promise<(ApiPublicIntegration & { areCredentialsSet: boolean }) | null> {
241188
try {
242-
const nangoClient = await this.getNangoClient();
243-
const response = await nangoClient.getIntegration(
189+
const response = await this.nangoClient.getIntegration(
244190
{ uniqueKey },
245191
{ include: ['credentials'] }
246192
);
@@ -316,8 +262,7 @@ export class NangoCredentialStore implements CredentialStore {
316262
* where true race conditions could occur.
317263
*/
318264
try {
319-
const nangoClient = await this.getNangoClient();
320-
const response = await nangoClient.createIntegration({
265+
const response = await this.nangoClient.createIntegration({
321266
provider,
322267
unique_key: uniqueKey,
323268
display_name: displayName,
@@ -394,8 +339,7 @@ export class NangoCredentialStore implements CredentialStore {
394339
providerConfigKey: string;
395340
}): Promise<NangoCredentialData | null> {
396341
try {
397-
const nangoClient = await this.getNangoClient();
398-
const nangoConnection = await nangoClient.getConnection(providerConfigKey, connectionId);
342+
const nangoConnection = await this.nangoClient.getConnection(providerConfigKey, connectionId);
399343

400344
const tokenAndCredentials = this.getAccessToken(nangoConnection.credentials) ?? {};
401345

@@ -507,8 +451,7 @@ export class NangoCredentialStore implements CredentialStore {
507451

508452
const { connectionId, providerConfigKey } = parsedKey;
509453

510-
const nangoClient = await this.getNangoClient();
511-
await nangoClient.deleteConnection(providerConfigKey, connectionId);
454+
await this.nangoClient.deleteConnection(providerConfigKey, connectionId);
512455
return true;
513456
} catch (error) {
514457
logger.error(
@@ -527,14 +470,6 @@ export class NangoCredentialStore implements CredentialStore {
527470
* Check if the credential store is available and functional
528471
*/
529472
async checkAvailability(): Promise<{ available: boolean; reason?: string }> {
530-
// First check if Nango is installed
531-
if (!isNangoAvailable()) {
532-
return {
533-
available: false,
534-
reason: 'Nango is not installed. Install with: npm install @nangohq/node @nangohq/types',
535-
};
536-
}
537-
538473
if (!this.nangoConfig.secretKey) {
539474
return {
540475
available: false,
@@ -561,21 +496,11 @@ export class NangoCredentialStore implements CredentialStore {
561496
/**
562497
* Factory function to create NangoCredentialStore
563498
* Automatically reads NANGO_SECRET_KEY from environment and validates it
564-
*
565-
* Note: This function requires the @nangohq/node and @nangohq/types packages to be installed.
566-
* If Nango is not installed, this will throw a helpful error message.
567499
*/
568500
export function createNangoCredentialStore(
569501
id: string,
570502
config?: Partial<NangoConfig>
571503
): NangoCredentialStore {
572-
// Check if Nango is available
573-
if (!isNangoAvailable()) {
574-
throw new Error(
575-
'Nango is not installed. To use Nango credential store, install it with: npm install @nangohq/node @nangohq/types'
576-
);
577-
}
578-
579504
const nangoSecretKey = config?.secretKey || process.env.NANGO_SECRET_KEY;
580505

581506
if (

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)