@juspay/neurolink 9.14.0 → 9.15.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.
- package/CHANGELOG.md +6 -0
- package/README.md +15 -15
- package/dist/auth/anthropicOAuth.d.ts +377 -0
- package/dist/auth/anthropicOAuth.js +914 -0
- package/dist/auth/index.d.ts +20 -0
- package/dist/auth/index.js +29 -0
- package/dist/auth/tokenStore.d.ts +225 -0
- package/dist/auth/tokenStore.js +521 -0
- package/dist/cli/commands/auth.d.ts +50 -0
- package/dist/cli/commands/auth.js +1115 -0
- package/dist/cli/factories/authCommandFactory.d.ts +52 -0
- package/dist/cli/factories/authCommandFactory.js +146 -0
- package/dist/cli/factories/commandFactory.d.ts +6 -0
- package/dist/cli/factories/commandFactory.js +92 -2
- package/dist/cli/parser.js +11 -2
- package/dist/constants/enums.d.ts +20 -0
- package/dist/constants/enums.js +30 -0
- package/dist/constants/index.d.ts +3 -1
- package/dist/constants/index.js +11 -1
- package/dist/index.d.ts +1 -1
- package/dist/lib/auth/anthropicOAuth.d.ts +377 -0
- package/dist/lib/auth/anthropicOAuth.js +915 -0
- package/dist/lib/auth/index.d.ts +20 -0
- package/dist/lib/auth/index.js +30 -0
- package/dist/lib/auth/tokenStore.d.ts +225 -0
- package/dist/lib/auth/tokenStore.js +522 -0
- package/dist/lib/constants/enums.d.ts +20 -0
- package/dist/lib/constants/enums.js +30 -0
- package/dist/lib/constants/index.d.ts +3 -1
- package/dist/lib/constants/index.js +11 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/models/anthropicModels.d.ts +267 -0
- package/dist/lib/models/anthropicModels.js +528 -0
- package/dist/lib/providers/anthropic.d.ts +123 -2
- package/dist/lib/providers/anthropic.js +800 -10
- package/dist/lib/types/errors.d.ts +62 -0
- package/dist/lib/types/errors.js +107 -0
- package/dist/lib/types/index.d.ts +2 -1
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/providers.d.ts +107 -0
- package/dist/lib/types/providers.js +69 -0
- package/dist/lib/types/subscriptionTypes.d.ts +893 -0
- package/dist/lib/types/subscriptionTypes.js +8 -0
- package/dist/lib/utils/providerConfig.d.ts +167 -0
- package/dist/lib/utils/providerConfig.js +619 -9
- package/dist/models/anthropicModels.d.ts +267 -0
- package/dist/models/anthropicModels.js +527 -0
- package/dist/providers/anthropic.d.ts +123 -2
- package/dist/providers/anthropic.js +800 -10
- package/dist/types/errors.d.ts +62 -0
- package/dist/types/errors.js +107 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +2 -0
- package/dist/types/providers.d.ts +107 -0
- package/dist/types/providers.js +69 -0
- package/dist/types/subscriptionTypes.d.ts +893 -0
- package/dist/types/subscriptionTypes.js +7 -0
- package/dist/utils/providerConfig.d.ts +167 -0
- package/dist/utils/providerConfig.js +619 -9
- package/package.json +2 -1
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeuroLink OAuth Token Store
|
|
3
|
+
*
|
|
4
|
+
* Secure storage for OAuth tokens with encoding support and multi-provider capability.
|
|
5
|
+
* Stores tokens in the user's home directory with restrictive permissions.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Multi-provider token storage in a single tokens.json file
|
|
9
|
+
* - Secure file storage with 0o600 permissions
|
|
10
|
+
* - Token expiration checking with configurable buffer
|
|
11
|
+
* - Simple XOR-based obfuscation (not encryption, but not plaintext)
|
|
12
|
+
* - Automatic token refresh via configurable refresher functions
|
|
13
|
+
* - Cross-platform support (Unix/macOS/Windows)
|
|
14
|
+
*/
|
|
15
|
+
import { promises as fs } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { createHash } from "crypto";
|
|
19
|
+
import { logger } from "../utils/logger.js";
|
|
20
|
+
import { TokenStoreError } from "../types/errors.js";
|
|
21
|
+
// Re-export for backward compatibility
|
|
22
|
+
export { TokenStoreError } from "../types/errors.js";
|
|
23
|
+
const { readFile, writeFile, mkdir, unlink, access, chmod, rename } = fs;
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// TOKEN STORE CLASS
|
|
26
|
+
// =============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Secure token storage for OAuth tokens with multi-provider support
|
|
29
|
+
*
|
|
30
|
+
* Provides persistent storage for OAuth tokens with:
|
|
31
|
+
* - Multi-provider support in a single file
|
|
32
|
+
* - Secure file permissions (0o600)
|
|
33
|
+
* - Optional obfuscation/encoding
|
|
34
|
+
* - Token expiration checking with buffer
|
|
35
|
+
* - Automatic token refresh via configurable refreshers
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const store = new TokenStore();
|
|
40
|
+
*
|
|
41
|
+
* // Save tokens for a provider
|
|
42
|
+
* await store.saveTokens("anthropic", {
|
|
43
|
+
* accessToken: "sk-...",
|
|
44
|
+
* refreshToken: "rt-...",
|
|
45
|
+
* expiresAt: Date.now() + 3600000,
|
|
46
|
+
* tokenType: "Bearer",
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // Set up automatic refresh
|
|
50
|
+
* store.setTokenRefresher("anthropic", async (refreshToken) => {
|
|
51
|
+
* // Call OAuth refresh endpoint
|
|
52
|
+
* return newTokens;
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Get a valid token (auto-refreshes if needed)
|
|
56
|
+
* const token = await store.getValidToken("anthropic");
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export class TokenStore {
|
|
60
|
+
static STORAGE_VERSION = "2.0";
|
|
61
|
+
static NEUROLINK_DIR = ".neurolink";
|
|
62
|
+
static TOKEN_FILE = "tokens.json";
|
|
63
|
+
static FILE_PERMISSIONS = 0o600;
|
|
64
|
+
static DIR_PERMISSIONS = 0o700;
|
|
65
|
+
/** Default expiration buffer: 5 minutes */
|
|
66
|
+
static DEFAULT_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
67
|
+
storagePath;
|
|
68
|
+
encryptionEnabled;
|
|
69
|
+
encryptionKey;
|
|
70
|
+
tokenRefreshers = new Map();
|
|
71
|
+
/**
|
|
72
|
+
* Creates a new TokenStore instance
|
|
73
|
+
*
|
|
74
|
+
* @param options - Configuration options
|
|
75
|
+
* @param options.encryptionEnabled - Whether to enable token obfuscation (default: true)
|
|
76
|
+
* @param options.customStoragePath - Override the default storage path
|
|
77
|
+
*/
|
|
78
|
+
constructor(options = {}) {
|
|
79
|
+
const { encryptionEnabled = true, customStoragePath } = options;
|
|
80
|
+
this.encryptionEnabled = encryptionEnabled;
|
|
81
|
+
this.encryptionKey = this.deriveEncryptionKey();
|
|
82
|
+
if (customStoragePath) {
|
|
83
|
+
this.storagePath = customStoragePath;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
this.storagePath = join(homedir(), TokenStore.NEUROLINK_DIR, TokenStore.TOKEN_FILE);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ===========================================================================
|
|
90
|
+
// PUBLIC METHODS
|
|
91
|
+
// ===========================================================================
|
|
92
|
+
/**
|
|
93
|
+
* Gets the path where tokens are stored
|
|
94
|
+
*
|
|
95
|
+
* @returns The absolute path to the token storage file
|
|
96
|
+
*/
|
|
97
|
+
getStoragePath() {
|
|
98
|
+
return this.storagePath;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Saves OAuth tokens for a specific provider
|
|
102
|
+
*
|
|
103
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
104
|
+
* @param tokens - The OAuth tokens to save
|
|
105
|
+
* @throws TokenStoreError if storage fails
|
|
106
|
+
*/
|
|
107
|
+
async saveTokens(provider, tokens) {
|
|
108
|
+
// Validate tokens before saving
|
|
109
|
+
this.validateTokens(tokens);
|
|
110
|
+
const storageDir = join(this.storagePath, "..");
|
|
111
|
+
await this.ensureDirectory(storageDir);
|
|
112
|
+
// Load existing data or create new
|
|
113
|
+
let storageData;
|
|
114
|
+
try {
|
|
115
|
+
storageData = await this.loadStorageData();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (error instanceof TokenStoreError && error.code === "NOT_FOUND") {
|
|
119
|
+
// Create new storage structure
|
|
120
|
+
storageData = {
|
|
121
|
+
version: TokenStore.STORAGE_VERSION,
|
|
122
|
+
lastModified: Date.now(),
|
|
123
|
+
providers: {},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Update provider tokens
|
|
131
|
+
storageData.providers[provider] = {
|
|
132
|
+
tokens,
|
|
133
|
+
createdAt: Date.now(),
|
|
134
|
+
lastAccessed: Date.now(),
|
|
135
|
+
};
|
|
136
|
+
storageData.lastModified = Date.now();
|
|
137
|
+
try {
|
|
138
|
+
const content = this.encryptionEnabled
|
|
139
|
+
? this.obfuscate(JSON.stringify(storageData))
|
|
140
|
+
: JSON.stringify(storageData, null, 2);
|
|
141
|
+
// Write to temporary file first for atomic operation
|
|
142
|
+
const tempPath = `${this.storagePath}.tmp`;
|
|
143
|
+
await writeFile(tempPath, content, "utf-8");
|
|
144
|
+
// Set restrictive permissions before moving to final location
|
|
145
|
+
await chmod(tempPath, TokenStore.FILE_PERMISSIONS);
|
|
146
|
+
// Rename to final location (atomic on most filesystems)
|
|
147
|
+
await fs.rename(tempPath, this.storagePath);
|
|
148
|
+
logger.debug("OAuth tokens saved successfully", {
|
|
149
|
+
provider,
|
|
150
|
+
path: this.storagePath,
|
|
151
|
+
encrypted: this.encryptionEnabled,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
+
logger.error("Failed to save OAuth tokens", {
|
|
157
|
+
provider,
|
|
158
|
+
error: errorMessage,
|
|
159
|
+
});
|
|
160
|
+
throw new TokenStoreError(`Failed to save tokens for ${provider}: ${errorMessage}`, "STORAGE_ERROR");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Loads stored OAuth tokens for a specific provider
|
|
165
|
+
*
|
|
166
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
167
|
+
* @returns The stored tokens, or null if not found
|
|
168
|
+
* @throws TokenStoreError if reading fails (other than file not found)
|
|
169
|
+
*/
|
|
170
|
+
async loadTokens(provider) {
|
|
171
|
+
try {
|
|
172
|
+
const storageData = await this.loadStorageData();
|
|
173
|
+
const providerData = storageData.providers[provider];
|
|
174
|
+
if (!providerData) {
|
|
175
|
+
logger.debug("No stored tokens found for provider", { provider });
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
// Update last accessed time
|
|
179
|
+
providerData.lastAccessed = Date.now();
|
|
180
|
+
await this.saveStorageData(storageData);
|
|
181
|
+
logger.debug("OAuth tokens retrieved successfully", {
|
|
182
|
+
provider,
|
|
183
|
+
expiresAt: new Date(providerData.tokens.expiresAt).toISOString(),
|
|
184
|
+
isExpired: this.isTokenExpired(providerData.tokens),
|
|
185
|
+
});
|
|
186
|
+
return providerData.tokens;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (error instanceof TokenStoreError && error.code === "NOT_FOUND") {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Clears stored tokens for a specific provider
|
|
197
|
+
*
|
|
198
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
199
|
+
* @throws TokenStoreError if deletion fails
|
|
200
|
+
*/
|
|
201
|
+
async clearTokens(provider) {
|
|
202
|
+
try {
|
|
203
|
+
const storageData = await this.loadStorageData();
|
|
204
|
+
if (!storageData.providers[provider]) {
|
|
205
|
+
logger.debug("No tokens to clear for provider", { provider });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
delete storageData.providers[provider];
|
|
209
|
+
storageData.lastModified = Date.now();
|
|
210
|
+
// If no more providers, delete the file entirely
|
|
211
|
+
if (Object.keys(storageData.providers).length === 0) {
|
|
212
|
+
await this.deleteStorageFile();
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
await this.saveStorageData(storageData);
|
|
216
|
+
}
|
|
217
|
+
logger.info("OAuth tokens cleared successfully", { provider });
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (error instanceof TokenStoreError && error.code === "NOT_FOUND") {
|
|
221
|
+
logger.debug("No tokens to clear for provider", { provider });
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
225
|
+
logger.error("Failed to clear OAuth tokens", {
|
|
226
|
+
provider,
|
|
227
|
+
error: errorMessage,
|
|
228
|
+
});
|
|
229
|
+
throw new TokenStoreError(`Failed to clear tokens for ${provider}: ${errorMessage}`, "STORAGE_ERROR");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Checks if the given tokens are expired
|
|
234
|
+
*
|
|
235
|
+
* @param tokens - The OAuth tokens to check
|
|
236
|
+
* @param bufferMs - Buffer time in milliseconds before actual expiration (default: 5 minutes)
|
|
237
|
+
* @returns true if token is expired or will expire within buffer time
|
|
238
|
+
*/
|
|
239
|
+
isTokenExpired(tokens, bufferMs = TokenStore.DEFAULT_EXPIRY_BUFFER_MS) {
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
return tokens.expiresAt - bufferMs <= now;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Gets a valid access token for a provider, refreshing if needed
|
|
245
|
+
*
|
|
246
|
+
* If the stored token is expired or about to expire, and a refresher
|
|
247
|
+
* function has been set, it will automatically refresh the token.
|
|
248
|
+
*
|
|
249
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
250
|
+
* @returns The valid access token, or null if not available
|
|
251
|
+
* @throws TokenStoreError if refresh fails
|
|
252
|
+
*/
|
|
253
|
+
async getValidToken(provider) {
|
|
254
|
+
const tokens = await this.loadTokens(provider);
|
|
255
|
+
if (!tokens) {
|
|
256
|
+
logger.debug("No tokens found for provider", { provider });
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
// Check if token is expired or about to expire
|
|
260
|
+
if (this.isTokenExpired(tokens)) {
|
|
261
|
+
logger.debug("Token expired or expiring soon", {
|
|
262
|
+
provider,
|
|
263
|
+
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
264
|
+
});
|
|
265
|
+
// Try to refresh if a refresher is configured
|
|
266
|
+
const refresher = this.tokenRefreshers.get(provider);
|
|
267
|
+
if (refresher && tokens.refreshToken) {
|
|
268
|
+
try {
|
|
269
|
+
logger.info("Refreshing expired token", { provider });
|
|
270
|
+
const newTokens = await refresher(tokens.refreshToken);
|
|
271
|
+
// Save the new tokens
|
|
272
|
+
await this.saveTokens(provider, newTokens);
|
|
273
|
+
logger.info("Token refreshed successfully", { provider });
|
|
274
|
+
return newTokens.accessToken;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
278
|
+
logger.error("Failed to refresh token", {
|
|
279
|
+
provider,
|
|
280
|
+
error: errorMessage,
|
|
281
|
+
});
|
|
282
|
+
throw new TokenStoreError(`Failed to refresh token for ${provider}: ${errorMessage}`, "REFRESH_ERROR");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
logger.debug("No refresher configured or no refresh token available", {
|
|
287
|
+
provider,
|
|
288
|
+
hasRefresher: !!refresher,
|
|
289
|
+
hasRefreshToken: !!tokens.refreshToken,
|
|
290
|
+
});
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return tokens.accessToken;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Sets the token refresher function for a provider
|
|
298
|
+
*
|
|
299
|
+
* The refresher function will be called automatically when getValidToken
|
|
300
|
+
* detects that the stored token is expired or about to expire.
|
|
301
|
+
*
|
|
302
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
303
|
+
* @param refresher - Function that takes a refresh token and returns new tokens
|
|
304
|
+
*/
|
|
305
|
+
setTokenRefresher(provider, refresher) {
|
|
306
|
+
this.tokenRefreshers.set(provider, refresher);
|
|
307
|
+
logger.debug("Token refresher set for provider", { provider });
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Removes the token refresher function for a provider
|
|
311
|
+
*
|
|
312
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
313
|
+
*/
|
|
314
|
+
clearTokenRefresher(provider) {
|
|
315
|
+
this.tokenRefreshers.delete(provider);
|
|
316
|
+
logger.debug("Token refresher cleared for provider", { provider });
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Checks if tokens exist for a specific provider
|
|
320
|
+
*
|
|
321
|
+
* @param provider - The provider name (e.g., "anthropic", "openai")
|
|
322
|
+
* @returns true if tokens are stored for the provider
|
|
323
|
+
*/
|
|
324
|
+
async hasTokens(provider) {
|
|
325
|
+
try {
|
|
326
|
+
const tokens = await this.loadTokens(provider);
|
|
327
|
+
return tokens !== null;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Lists all providers that have stored tokens
|
|
335
|
+
*
|
|
336
|
+
* @returns Array of provider names
|
|
337
|
+
*/
|
|
338
|
+
async listProviders() {
|
|
339
|
+
try {
|
|
340
|
+
const storageData = await this.loadStorageData();
|
|
341
|
+
return Object.keys(storageData.providers);
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
if (error instanceof TokenStoreError && error.code === "NOT_FOUND") {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Clears all stored tokens for all providers
|
|
352
|
+
*
|
|
353
|
+
* @throws TokenStoreError if deletion fails
|
|
354
|
+
*/
|
|
355
|
+
async clearAllTokens() {
|
|
356
|
+
await this.deleteStorageFile();
|
|
357
|
+
logger.info("All OAuth tokens cleared");
|
|
358
|
+
}
|
|
359
|
+
// ===========================================================================
|
|
360
|
+
// PRIVATE HELPER METHODS
|
|
361
|
+
// ===========================================================================
|
|
362
|
+
/**
|
|
363
|
+
* Loads the storage data from file
|
|
364
|
+
*/
|
|
365
|
+
async loadStorageData() {
|
|
366
|
+
try {
|
|
367
|
+
await access(this.storagePath);
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
// File doesn't exist, return empty storage
|
|
371
|
+
throw new TokenStoreError("Token storage file not found", "NOT_FOUND");
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
const content = await readFile(this.storagePath, "utf-8");
|
|
375
|
+
let storageData;
|
|
376
|
+
if (this.encryptionEnabled) {
|
|
377
|
+
const decrypted = this.deobfuscate(content);
|
|
378
|
+
storageData = JSON.parse(decrypted);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
storageData = JSON.parse(content);
|
|
382
|
+
}
|
|
383
|
+
// Validate storage data structure
|
|
384
|
+
if (!storageData.providers || !storageData.version) {
|
|
385
|
+
throw new TokenStoreError("Invalid token storage format", "VALIDATION_ERROR");
|
|
386
|
+
}
|
|
387
|
+
return storageData;
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (error instanceof TokenStoreError) {
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
394
|
+
logger.error("Failed to read token storage", { error: errorMessage });
|
|
395
|
+
throw new TokenStoreError(`Failed to read token storage: ${errorMessage}`, "STORAGE_ERROR");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Saves storage data to file
|
|
400
|
+
*/
|
|
401
|
+
async saveStorageData(data) {
|
|
402
|
+
try {
|
|
403
|
+
const content = this.encryptionEnabled
|
|
404
|
+
? this.obfuscate(JSON.stringify(data))
|
|
405
|
+
: JSON.stringify(data, null, 2);
|
|
406
|
+
const tmpPath = `${this.storagePath}.tmp`;
|
|
407
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
408
|
+
await chmod(tmpPath, TokenStore.FILE_PERMISSIONS);
|
|
409
|
+
await rename(tmpPath, this.storagePath);
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
413
|
+
throw new TokenStoreError(`Failed to save token storage: ${errorMessage}`, "STORAGE_ERROR");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Deletes the storage file
|
|
418
|
+
*/
|
|
419
|
+
async deleteStorageFile() {
|
|
420
|
+
try {
|
|
421
|
+
await access(this.storagePath);
|
|
422
|
+
await unlink(this.storagePath);
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
if (error.code === "ENOENT") {
|
|
426
|
+
// File doesn't exist, nothing to delete
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Validates token structure
|
|
434
|
+
*/
|
|
435
|
+
validateTokens(tokens) {
|
|
436
|
+
if (!tokens.accessToken || typeof tokens.accessToken !== "string") {
|
|
437
|
+
throw new TokenStoreError("Invalid access token: must be a non-empty string", "VALIDATION_ERROR");
|
|
438
|
+
}
|
|
439
|
+
if (tokens.refreshToken !== undefined &&
|
|
440
|
+
typeof tokens.refreshToken !== "string") {
|
|
441
|
+
throw new TokenStoreError("Invalid refresh token: must be a string when provided", "VALIDATION_ERROR");
|
|
442
|
+
}
|
|
443
|
+
if (typeof tokens.expiresAt !== "number" || tokens.expiresAt <= 0) {
|
|
444
|
+
throw new TokenStoreError("Invalid expiresAt: must be a positive number", "VALIDATION_ERROR");
|
|
445
|
+
}
|
|
446
|
+
if (!tokens.tokenType || typeof tokens.tokenType !== "string") {
|
|
447
|
+
throw new TokenStoreError("Invalid token type: must be a non-empty string", "VALIDATION_ERROR");
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Ensures the storage directory exists with proper permissions
|
|
452
|
+
*/
|
|
453
|
+
async ensureDirectory(dirPath) {
|
|
454
|
+
try {
|
|
455
|
+
await mkdir(dirPath, {
|
|
456
|
+
recursive: true,
|
|
457
|
+
mode: TokenStore.DIR_PERMISSIONS,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
// Directory might already exist, try to set permissions
|
|
462
|
+
try {
|
|
463
|
+
await chmod(dirPath, TokenStore.DIR_PERMISSIONS);
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
// Ignore permission errors on existing directories
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Derives an encoding key from machine-specific information
|
|
472
|
+
* This provides basic obfuscation - for production use, consider
|
|
473
|
+
* using proper encryption with a user-provided key or system keychain
|
|
474
|
+
*/
|
|
475
|
+
deriveEncryptionKey() {
|
|
476
|
+
const machineInfo = `${homedir()}-neurolink-token-store`;
|
|
477
|
+
return createHash("sha256").update(machineInfo).digest("hex");
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Simple XOR-based obfuscation
|
|
481
|
+
* Note: This is NOT cryptographically secure, just basic obfuscation
|
|
482
|
+
* For production use, consider using node:crypto with proper encryption
|
|
483
|
+
*/
|
|
484
|
+
obfuscate(data) {
|
|
485
|
+
const key = this.encryptionKey;
|
|
486
|
+
let result = "";
|
|
487
|
+
for (let i = 0; i < data.length; i++) {
|
|
488
|
+
const charCode = data.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
|
489
|
+
result += String.fromCharCode(charCode);
|
|
490
|
+
}
|
|
491
|
+
// Encode as base64 for safe storage
|
|
492
|
+
return Buffer.from(result, "binary").toString("base64");
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Reverses the XOR obfuscation
|
|
496
|
+
*/
|
|
497
|
+
deobfuscate(data) {
|
|
498
|
+
const key = this.encryptionKey;
|
|
499
|
+
// Decode from base64
|
|
500
|
+
const decoded = Buffer.from(data, "base64").toString("binary");
|
|
501
|
+
let result = "";
|
|
502
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
503
|
+
const charCode = decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
|
504
|
+
result += String.fromCharCode(charCode);
|
|
505
|
+
}
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// =============================================================================
|
|
510
|
+
// SINGLETON INSTANCE
|
|
511
|
+
// =============================================================================
|
|
512
|
+
/**
|
|
513
|
+
* Default token store singleton instance
|
|
514
|
+
* Uses default configuration with encryption enabled
|
|
515
|
+
*/
|
|
516
|
+
export const tokenStore = new TokenStore();
|
|
517
|
+
/**
|
|
518
|
+
* Alias for backward compatibility
|
|
519
|
+
* @deprecated Use tokenStore instead
|
|
520
|
+
*/
|
|
521
|
+
export const defaultTokenStore = tokenStore;
|
|
522
|
+
//# sourceMappingURL=tokenStore.js.map
|
|
@@ -486,3 +486,23 @@ export declare enum ErrorSeverity {
|
|
|
486
486
|
HIGH = "high",
|
|
487
487
|
CRITICAL = "critical"
|
|
488
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Beta features available for Anthropic API
|
|
491
|
+
*
|
|
492
|
+
* @description Beta feature flags that can be enabled for enhanced functionality:
|
|
493
|
+
* - CLAUDE_CODE: Claude Code beta features for development workflows
|
|
494
|
+
* - INTERLEAVED_THINKING: Enables interleaved thinking in responses
|
|
495
|
+
* - FINE_GRAINED_STREAMING: Fine-grained tool streaming for better UX
|
|
496
|
+
*/
|
|
497
|
+
export declare enum AnthropicBetaFeature {
|
|
498
|
+
CLAUDE_CODE = "claude-code-20250219",
|
|
499
|
+
INTERLEAVED_THINKING = "interleaved-thinking-2025-05-14",
|
|
500
|
+
FINE_GRAINED_STREAMING = "fine-grained-tool-streaming-2025-05-14"
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Buffer time in milliseconds before token expiry to trigger refresh
|
|
504
|
+
*
|
|
505
|
+
* @description Tokens are refreshed 5 minutes before expiry to prevent
|
|
506
|
+
* authentication failures during ongoing operations
|
|
507
|
+
*/
|
|
508
|
+
export declare const TOKEN_EXPIRY_BUFFER_MS: number;
|
|
@@ -656,4 +656,34 @@ export var ErrorSeverity;
|
|
|
656
656
|
ErrorSeverity["HIGH"] = "high";
|
|
657
657
|
ErrorSeverity["CRITICAL"] = "critical";
|
|
658
658
|
})(ErrorSeverity || (ErrorSeverity = {}));
|
|
659
|
+
// ============================================================================
|
|
660
|
+
// CLAUDE SUBSCRIPTION ENUMS
|
|
661
|
+
// ============================================================================
|
|
662
|
+
// Note: ClaudeSubscriptionTier and AnthropicAuthMethod are defined as type
|
|
663
|
+
// aliases in types/subscriptionTypes.ts (canonical definitions).
|
|
664
|
+
// The type aliases support all 6 tier values: free, pro, max, max_5, max_20, api.
|
|
665
|
+
/**
|
|
666
|
+
* Beta features available for Anthropic API
|
|
667
|
+
*
|
|
668
|
+
* @description Beta feature flags that can be enabled for enhanced functionality:
|
|
669
|
+
* - CLAUDE_CODE: Claude Code beta features for development workflows
|
|
670
|
+
* - INTERLEAVED_THINKING: Enables interleaved thinking in responses
|
|
671
|
+
* - FINE_GRAINED_STREAMING: Fine-grained tool streaming for better UX
|
|
672
|
+
*/
|
|
673
|
+
export var AnthropicBetaFeature;
|
|
674
|
+
(function (AnthropicBetaFeature) {
|
|
675
|
+
AnthropicBetaFeature["CLAUDE_CODE"] = "claude-code-20250219";
|
|
676
|
+
AnthropicBetaFeature["INTERLEAVED_THINKING"] = "interleaved-thinking-2025-05-14";
|
|
677
|
+
AnthropicBetaFeature["FINE_GRAINED_STREAMING"] = "fine-grained-tool-streaming-2025-05-14";
|
|
678
|
+
})(AnthropicBetaFeature || (AnthropicBetaFeature = {}));
|
|
679
|
+
// ============================================================================
|
|
680
|
+
// ANTHROPIC OAUTH CONSTANTS
|
|
681
|
+
// ============================================================================
|
|
682
|
+
/**
|
|
683
|
+
* Buffer time in milliseconds before token expiry to trigger refresh
|
|
684
|
+
*
|
|
685
|
+
* @description Tokens are refreshed 5 minutes before expiry to prevent
|
|
686
|
+
* authentication failures during ongoing operations
|
|
687
|
+
*/
|
|
688
|
+
export const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
|
|
659
689
|
//# sourceMappingURL=enums.js.map
|
|
@@ -187,6 +187,8 @@ export declare const CONSTANTS_METADATA: {
|
|
|
187
187
|
readonly VERSION: "1.0.0";
|
|
188
188
|
readonly LAST_UPDATED: "2025-01-27";
|
|
189
189
|
readonly TOTAL_CONSTANTS: 300;
|
|
190
|
-
readonly CATEGORIES: readonly ["timeouts", "retry", "performance", "tokens"];
|
|
190
|
+
readonly CATEGORIES: readonly ["timeouts", "retry", "performance", "tokens", "enums"];
|
|
191
191
|
readonly COMPATIBILITY: "backward_compatible";
|
|
192
192
|
};
|
|
193
|
+
export { AIProviderName, OpenRouterModels, BedrockModels, OpenAIModels, AzureOpenAIModels, VertexModels, GoogleAIModels, AnthropicModels, MistralModels, OllamaModels, LiteLLMModels, HuggingFaceModels, SageMakerModels, APIVersions, ErrorCategory, ErrorSeverity, AnthropicBetaFeature, TOKEN_EXPIRY_BUFFER_MS, } from "./enums.js";
|
|
194
|
+
export type { ClaudeSubscriptionTier, AnthropicAuthMethod, } from "../types/subscriptionTypes.js";
|
|
@@ -190,7 +190,17 @@ export const CONSTANTS_METADATA = {
|
|
|
190
190
|
VERSION: "1.0.0",
|
|
191
191
|
LAST_UPDATED: "2025-01-27",
|
|
192
192
|
TOTAL_CONSTANTS: 300,
|
|
193
|
-
CATEGORIES: ["timeouts", "retry", "performance", "tokens"],
|
|
193
|
+
CATEGORIES: ["timeouts", "retry", "performance", "tokens", "enums"],
|
|
194
194
|
COMPATIBILITY: "backward_compatible",
|
|
195
195
|
};
|
|
196
|
+
// ===== ENUMS =====
|
|
197
|
+
export {
|
|
198
|
+
// Provider and Model Enums
|
|
199
|
+
AIProviderName, OpenRouterModels, BedrockModels, OpenAIModels, AzureOpenAIModels, VertexModels, GoogleAIModels, AnthropicModels, MistralModels, OllamaModels, LiteLLMModels, HuggingFaceModels, SageMakerModels, APIVersions,
|
|
200
|
+
// Error Enums
|
|
201
|
+
ErrorCategory, ErrorSeverity,
|
|
202
|
+
// Claude Subscription Enums
|
|
203
|
+
AnthropicBetaFeature,
|
|
204
|
+
// OAuth Constants
|
|
205
|
+
TOKEN_EXPIRY_BUFFER_MS, } from "./enums.js";
|
|
196
206
|
//# sourceMappingURL=index.js.map
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -192,7 +192,7 @@ export declare function createBestAIProvider(requestedProvider?: string, modelNa
|
|
|
192
192
|
* ```
|
|
193
193
|
*/
|
|
194
194
|
export { CircuitBreakerManager, calculateExpiresAt, createMCPServer, createOAuthProviderFromConfig, DEFAULT_HTTP_RETRY_CONFIG, DEFAULT_RATE_LIMIT_CONFIG, executeMCP, FileTokenStorage, getMCPStats, getServerInfo, globalCircuitBreakerManager, globalRateLimiterManager, HTTPRateLimiter, InMemoryTokenStorage, initializeMCPEcosystem, isRetryableHTTPError, isRetryableStatusCode, isTokenExpired, listMCPs, MCPCircuitBreaker, mcpLogger, NeuroLinkOAuthProvider, RateLimiterManager, validateServerTools, validateTool as validateMCPTool, withHTTPRetry, } from "./mcp/index.js";
|
|
195
|
-
export type { AuthorizationUrlResult, DiscoveredMcp, HTTPRetryConfig, MCPOAuthConfig, McpMetadata, OAuthClientInformation, OAuthTokens, RateLimitConfig, TokenExchangeRequest, TokenStorage, } from "./types/mcpTypes.js";
|
|
195
|
+
export type { AuthorizationUrlResult, DiscoveredMcp, HTTPRetryConfig, MCPOAuthConfig, McpMetadata, OAuthClientInformation, OAuthTokens as McpOAuthTokens, RateLimitConfig, TokenExchangeRequest, TokenStorage, } from "./types/mcpTypes.js";
|
|
196
196
|
export type { ExecutionContext, ToolExecutionResult, ToolInfo, } from "./types/tools.js";
|
|
197
197
|
export type { LogLevel } from "./types/utilities.js";
|
|
198
198
|
export { logger } from "./utils/logger.js";
|