@juspay/neurolink 4.0.0 → 4.1.1

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 (56) hide show
  1. package/CHANGELOG.md +20 -5
  2. package/README.md +59 -1
  3. package/dist/lib/mcp/dynamic-chain-executor.d.ts +201 -0
  4. package/dist/lib/mcp/dynamic-chain-executor.js +489 -0
  5. package/dist/lib/mcp/dynamic-orchestrator.d.ts +109 -0
  6. package/dist/lib/mcp/dynamic-orchestrator.js +351 -0
  7. package/dist/lib/mcp/error-manager.d.ts +254 -0
  8. package/dist/lib/mcp/error-manager.js +501 -0
  9. package/dist/lib/mcp/error-recovery.d.ts +158 -0
  10. package/dist/lib/mcp/error-recovery.js +405 -0
  11. package/dist/lib/mcp/health-monitor.d.ts +256 -0
  12. package/dist/lib/mcp/health-monitor.js +621 -0
  13. package/dist/lib/mcp/orchestrator.d.ts +136 -5
  14. package/dist/lib/mcp/orchestrator.js +316 -9
  15. package/dist/lib/mcp/registry.d.ts +22 -0
  16. package/dist/lib/mcp/registry.js +24 -0
  17. package/dist/lib/mcp/semaphore-manager.d.ts +137 -0
  18. package/dist/lib/mcp/semaphore-manager.js +329 -0
  19. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  20. package/dist/lib/mcp/session-manager.d.ts +186 -0
  21. package/dist/lib/mcp/session-manager.js +400 -0
  22. package/dist/lib/mcp/session-persistence.d.ts +93 -0
  23. package/dist/lib/mcp/session-persistence.js +298 -0
  24. package/dist/lib/mcp/transport-manager.d.ts +153 -0
  25. package/dist/lib/mcp/transport-manager.js +330 -0
  26. package/dist/lib/mcp/unified-registry.d.ts +42 -1
  27. package/dist/lib/mcp/unified-registry.js +122 -2
  28. package/dist/lib/neurolink.d.ts +75 -0
  29. package/dist/lib/neurolink.js +104 -0
  30. package/dist/mcp/dynamic-chain-executor.d.ts +201 -0
  31. package/dist/mcp/dynamic-chain-executor.js +489 -0
  32. package/dist/mcp/dynamic-orchestrator.d.ts +109 -0
  33. package/dist/mcp/dynamic-orchestrator.js +351 -0
  34. package/dist/mcp/error-manager.d.ts +254 -0
  35. package/dist/mcp/error-manager.js +501 -0
  36. package/dist/mcp/error-recovery.d.ts +158 -0
  37. package/dist/mcp/error-recovery.js +405 -0
  38. package/dist/mcp/health-monitor.d.ts +256 -0
  39. package/dist/mcp/health-monitor.js +621 -0
  40. package/dist/mcp/orchestrator.d.ts +136 -5
  41. package/dist/mcp/orchestrator.js +316 -9
  42. package/dist/mcp/registry.d.ts +22 -0
  43. package/dist/mcp/registry.js +24 -0
  44. package/dist/mcp/semaphore-manager.d.ts +137 -0
  45. package/dist/mcp/semaphore-manager.js +329 -0
  46. package/dist/mcp/session-manager.d.ts +186 -0
  47. package/dist/mcp/session-manager.js +400 -0
  48. package/dist/mcp/session-persistence.d.ts +93 -0
  49. package/dist/mcp/session-persistence.js +299 -0
  50. package/dist/mcp/transport-manager.d.ts +153 -0
  51. package/dist/mcp/transport-manager.js +331 -0
  52. package/dist/mcp/unified-registry.d.ts +42 -1
  53. package/dist/mcp/unified-registry.js +122 -2
  54. package/dist/neurolink.d.ts +75 -0
  55. package/dist/neurolink.js +104 -0
  56. package/package.json +2 -1
@@ -0,0 +1,298 @@
1
+ /**
2
+ * NeuroLink Session Persistence Layer
3
+ * Provides file-based persistence for sessions with atomic writes and recovery
4
+ */
5
+ import fs from "fs/promises";
6
+ import path from "path";
7
+ import crypto from "crypto";
8
+ /**
9
+ * Session Persistence Manager
10
+ * Handles saving and loading sessions to/from disk with recovery mechanisms
11
+ */
12
+ export class SessionPersistence {
13
+ directory;
14
+ snapshotInterval;
15
+ retentionPeriod;
16
+ enableChecksum;
17
+ maxRetries;
18
+ snapshotTimer = null;
19
+ sessionMap;
20
+ constructor(sessionMap, options = {}) {
21
+ this.sessionMap = sessionMap;
22
+ this.directory = options.directory || ".neurolink/sessions";
23
+ this.snapshotInterval = options.snapshotInterval || 30000; // 30 seconds
24
+ this.retentionPeriod = options.retentionPeriod || 86400000; // 24 hours
25
+ this.enableChecksum = options.enableChecksum ?? true;
26
+ this.maxRetries = options.maxRetries || 3;
27
+ }
28
+ /**
29
+ * Initialize persistence layer
30
+ */
31
+ async initialize() {
32
+ // Create sessions directory if it doesn't exist
33
+ await this.ensureDirectory();
34
+ // Load existing sessions
35
+ await this.loadSessions();
36
+ // Start snapshot timer
37
+ this.startSnapshotTimer();
38
+ if (process.env.NEUROLINK_DEBUG === "true") {
39
+ console.log(`[SessionPersistence] Initialized with directory: ${this.directory}`);
40
+ }
41
+ }
42
+ /**
43
+ * Ensure sessions directory exists
44
+ */
45
+ async ensureDirectory() {
46
+ await fs.mkdir(this.directory, { recursive: true });
47
+ }
48
+ /**
49
+ * Calculate checksum for data integrity
50
+ */
51
+ calculateChecksum(data) {
52
+ return crypto.createHash("sha256").update(data).digest("hex");
53
+ }
54
+ /**
55
+ * Serialize session to JSON with metadata
56
+ */
57
+ serializeSession(session) {
58
+ // Convert Map to array for JSON serialization
59
+ const sessionData = {
60
+ ...session,
61
+ state: Array.from(session.state.entries()),
62
+ };
63
+ let fileData = {
64
+ session: sessionData,
65
+ checksum: "",
66
+ version: "1.0",
67
+ lastSaved: Date.now(),
68
+ };
69
+ if (this.enableChecksum) {
70
+ // First serialization without checksum
71
+ const tempJsonData = JSON.stringify(fileData, null, 2);
72
+ // Calculate checksum based on the first serialization
73
+ fileData.checksum = this.calculateChecksum(tempJsonData);
74
+ }
75
+ // Final serialization with checksum included
76
+ return JSON.stringify(fileData, null, 2);
77
+ }
78
+ /**
79
+ * Deserialize session from JSON
80
+ */
81
+ deserializeSession(data) {
82
+ try {
83
+ const fileData = JSON.parse(data);
84
+ // Verify checksum if enabled
85
+ if (this.enableChecksum && fileData.checksum) {
86
+ const tempData = { ...fileData, checksum: "" };
87
+ const expectedChecksum = this.calculateChecksum(JSON.stringify(tempData, null, 2));
88
+ if (fileData.checksum !== expectedChecksum) {
89
+ console.error("[SessionPersistence] Checksum mismatch, session corrupted");
90
+ return null;
91
+ }
92
+ }
93
+ // Convert state array back to Map
94
+ const session = fileData.session;
95
+ session.state = new Map(session.state);
96
+ return session;
97
+ }
98
+ catch (error) {
99
+ console.error("[SessionPersistence] Failed to deserialize session:", error);
100
+ return null;
101
+ }
102
+ }
103
+ /**
104
+ * Save single session with atomic write
105
+ */
106
+ async saveSession(sessionId, session) {
107
+ const filename = `${sessionId}.json`;
108
+ const filepath = path.join(this.directory, filename);
109
+ const tempPath = `${filepath}.tmp`;
110
+ let retries = 0;
111
+ while (retries < this.maxRetries) {
112
+ try {
113
+ // Serialize session
114
+ const data = this.serializeSession(session);
115
+ // Write to temporary file first (atomic write pattern)
116
+ await fs.writeFile(tempPath, data, "utf8");
117
+ // Rename temp file to final location (atomic operation)
118
+ await fs.rename(tempPath, filepath);
119
+ if (process.env.NEUROLINK_DEBUG === "true") {
120
+ console.log(`[SessionPersistence] Saved session ${sessionId}`);
121
+ }
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ retries++;
126
+ console.error(`[SessionPersistence] Failed to save session (attempt ${retries}):`, error);
127
+ // Clean up temp file if it exists
128
+ try {
129
+ await fs.unlink(tempPath);
130
+ }
131
+ catch {
132
+ // Ignore errors when cleaning up temp file
133
+ }
134
+ if (retries < this.maxRetries) {
135
+ // Exponential backoff
136
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, retries) * 100));
137
+ }
138
+ }
139
+ }
140
+ return false;
141
+ }
142
+ /**
143
+ * Load single session from disk
144
+ */
145
+ async loadSession(sessionId) {
146
+ const filename = `${sessionId}.json`;
147
+ const filepath = path.join(this.directory, filename);
148
+ try {
149
+ const data = await fs.readFile(filepath, "utf8");
150
+ return this.deserializeSession(data);
151
+ }
152
+ catch (error) {
153
+ if (error.code !== "ENOENT") {
154
+ console.error(`[SessionPersistence] Failed to load session ${sessionId}:`, error);
155
+ }
156
+ return null;
157
+ }
158
+ }
159
+ /**
160
+ * Load all sessions from disk on startup
161
+ */
162
+ async loadSessions() {
163
+ try {
164
+ const files = await fs.readdir(this.directory);
165
+ const sessionFiles = files.filter((f) => f.endsWith(".json"));
166
+ let loaded = 0;
167
+ let expired = 0;
168
+ for (const file of sessionFiles) {
169
+ const sessionId = file.replace(".json", "");
170
+ const session = await this.loadSession(sessionId);
171
+ if (session) {
172
+ // Check if session is expired
173
+ if (session.expiresAt < Date.now()) {
174
+ expired++;
175
+ // Clean up expired session file
176
+ await this.deleteSession(sessionId);
177
+ }
178
+ else {
179
+ // Restore to memory
180
+ this.sessionMap.set(sessionId, session);
181
+ loaded++;
182
+ }
183
+ }
184
+ }
185
+ console.log(`[SessionPersistence] Loaded ${loaded} sessions, cleaned ${expired} expired sessions`);
186
+ }
187
+ catch (error) {
188
+ console.error("[SessionPersistence] Failed to load sessions:", error);
189
+ }
190
+ }
191
+ /**
192
+ * Save all active sessions (snapshot)
193
+ */
194
+ async saveAllSessions() {
195
+ const sessions = Array.from(this.sessionMap.entries());
196
+ // Filter out expired sessions and create save promises
197
+ const savePromises = sessions
198
+ .filter(([_, session]) => session.expiresAt >= Date.now())
199
+ .map(async ([sessionId, session]) => {
200
+ return (await this.saveSession(sessionId, session)) ? 1 : 0;
201
+ });
202
+ // Execute all save operations concurrently
203
+ const results = await Promise.all(savePromises);
204
+ const saved = results.reduce((sum, result) => sum + result, 0);
205
+ if (process.env.NEUROLINK_DEBUG === "true") {
206
+ console.log(`[SessionPersistence] Snapshot: saved ${saved}/${sessions.length} sessions`);
207
+ }
208
+ }
209
+ /**
210
+ * Delete session file
211
+ */
212
+ async deleteSession(sessionId) {
213
+ const filename = `${sessionId}.json`;
214
+ const filepath = path.join(this.directory, filename);
215
+ try {
216
+ await fs.unlink(filepath);
217
+ // Keep memory and disk state in sync
218
+ this.sessionMap.delete(sessionId);
219
+ }
220
+ catch (error) {
221
+ if (error.code !== "ENOENT") {
222
+ console.error(`[SessionPersistence] Failed to delete session ${sessionId}:`, error);
223
+ }
224
+ }
225
+ }
226
+ /**
227
+ * Clean up old session files
228
+ */
229
+ async cleanupOldSessions() {
230
+ try {
231
+ const files = await fs.readdir(this.directory);
232
+ const now = Date.now();
233
+ let cleaned = 0;
234
+ for (const file of files) {
235
+ if (!file.endsWith(".json")) {
236
+ continue;
237
+ }
238
+ const filepath = path.join(this.directory, file);
239
+ const stats = await fs.stat(filepath);
240
+ // Remove files older than retention period
241
+ if (now - stats.mtimeMs > this.retentionPeriod) {
242
+ await fs.unlink(filepath);
243
+ cleaned++;
244
+ }
245
+ }
246
+ if (cleaned > 0) {
247
+ console.log(`[SessionPersistence] Cleaned up ${cleaned} old session files`);
248
+ }
249
+ }
250
+ catch (error) {
251
+ console.error("[SessionPersistence] Cleanup failed:", error);
252
+ }
253
+ }
254
+ /**
255
+ * Start periodic snapshot timer
256
+ */
257
+ startSnapshotTimer() {
258
+ if (this.snapshotTimer) {
259
+ clearInterval(this.snapshotTimer);
260
+ }
261
+ this.snapshotTimer = setInterval(async () => {
262
+ await this.saveAllSessions();
263
+ await this.cleanupOldSessions();
264
+ }, this.snapshotInterval);
265
+ }
266
+ /**
267
+ * Stop snapshot timer
268
+ */
269
+ stopSnapshotTimer() {
270
+ if (this.snapshotTimer) {
271
+ clearInterval(this.snapshotTimer);
272
+ this.snapshotTimer = null;
273
+ }
274
+ }
275
+ /**
276
+ * Shutdown persistence layer
277
+ */
278
+ async shutdown() {
279
+ this.stopSnapshotTimer();
280
+ // Final snapshot before shutdown
281
+ await this.saveAllSessions();
282
+ console.log("[SessionPersistence] Shutdown complete");
283
+ }
284
+ }
285
+ /**
286
+ * Default session persistence instance
287
+ */
288
+ export let defaultSessionPersistence = null;
289
+ /**
290
+ * Initialize default session persistence
291
+ */
292
+ export async function initializeSessionPersistence(sessionMap, options) {
293
+ if (!defaultSessionPersistence) {
294
+ defaultSessionPersistence = new SessionPersistence(sessionMap, options);
295
+ await defaultSessionPersistence.initialize();
296
+ }
297
+ return defaultSessionPersistence;
298
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Transport Manager for MCP connections
3
+ * Supports stdio, SSE, and HTTP transports with reconnection logic
4
+ */
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { z } from "zod";
7
+ import { ErrorManager } from "./error-manager.js";
8
+ export declare const TransportConfigSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
9
+ type: z.ZodLiteral<"stdio">;
10
+ command: z.ZodString;
11
+ args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ cwd: z.ZodOptional<z.ZodString>;
13
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ type: "stdio";
16
+ command: string;
17
+ args?: string[] | undefined;
18
+ cwd?: string | undefined;
19
+ env?: Record<string, string> | undefined;
20
+ }, {
21
+ type: "stdio";
22
+ command: string;
23
+ args?: string[] | undefined;
24
+ cwd?: string | undefined;
25
+ env?: Record<string, string> | undefined;
26
+ }>, z.ZodObject<{
27
+ type: z.ZodLiteral<"sse">;
28
+ url: z.ZodString;
29
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
30
+ timeout: z.ZodDefault<z.ZodNumber>;
31
+ maxRetryTime: z.ZodDefault<z.ZodNumber>;
32
+ withCredentials: z.ZodDefault<z.ZodBoolean>;
33
+ }, "strip", z.ZodTypeAny, {
34
+ type: "sse";
35
+ timeout: number;
36
+ url: string;
37
+ maxRetryTime: number;
38
+ withCredentials: boolean;
39
+ headers?: Record<string, string> | undefined;
40
+ }, {
41
+ type: "sse";
42
+ url: string;
43
+ timeout?: number | undefined;
44
+ headers?: Record<string, string> | undefined;
45
+ maxRetryTime?: number | undefined;
46
+ withCredentials?: boolean | undefined;
47
+ }>, z.ZodObject<{
48
+ type: z.ZodLiteral<"http">;
49
+ url: z.ZodString;
50
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
51
+ timeout: z.ZodDefault<z.ZodNumber>;
52
+ }, "strip", z.ZodTypeAny, {
53
+ type: "http";
54
+ timeout: number;
55
+ url: string;
56
+ headers?: Record<string, string> | undefined;
57
+ }, {
58
+ type: "http";
59
+ url: string;
60
+ timeout?: number | undefined;
61
+ headers?: Record<string, string> | undefined;
62
+ }>]>;
63
+ export type TransportConfig = z.infer<typeof TransportConfigSchema>;
64
+ export interface TransportStatus {
65
+ connected: boolean;
66
+ type: "stdio" | "sse" | "http";
67
+ lastConnected?: Date;
68
+ lastError?: Error;
69
+ reconnectAttempts: number;
70
+ }
71
+ export interface TransportManagerOptions {
72
+ autoReconnect?: boolean;
73
+ maxReconnectAttempts?: number;
74
+ reconnectDelay?: number;
75
+ healthCheckInterval?: number;
76
+ jitterEnabled?: boolean;
77
+ maxJitter?: number;
78
+ }
79
+ /**
80
+ * Manages MCP transport connections with automatic reconnection and health monitoring
81
+ */
82
+ export declare class TransportManager {
83
+ private errorManager;
84
+ private options;
85
+ private client?;
86
+ private config?;
87
+ private status;
88
+ private reconnectTimer?;
89
+ private healthCheckTimer?;
90
+ private reconnectPromise?;
91
+ constructor(errorManager: ErrorManager, options?: TransportManagerOptions);
92
+ /**
93
+ * Connect to MCP server using specified transport
94
+ */
95
+ connect(config: TransportConfig): Promise<Client>;
96
+ /**
97
+ * Create transport based on configuration
98
+ */
99
+ createTransport(config: TransportConfig): Promise<any>;
100
+ /**
101
+ * Create stdio transport
102
+ */
103
+ private createStdioTransport;
104
+ /**
105
+ * Create SSE transport with reconnection support
106
+ */
107
+ private createSSETransport;
108
+ /**
109
+ * Create HTTP transport
110
+ */
111
+ private createHTTPTransport;
112
+ /**
113
+ * Set up health monitoring
114
+ */
115
+ private setupHealthMonitoring;
116
+ /**
117
+ * Handle connection errors
118
+ */
119
+ private handleConnectionError;
120
+ /**
121
+ * Schedule reconnection attempt
122
+ */
123
+ private scheduleReconnect;
124
+ /**
125
+ * Calculate reconnect delay with exponential backoff
126
+ */
127
+ private calculateReconnectDelay;
128
+ /**
129
+ * Reconnect to server
130
+ */
131
+ reconnect(): Promise<void>;
132
+ private _reconnect;
133
+ /**
134
+ * Disconnect from server
135
+ */
136
+ disconnect(): Promise<void>;
137
+ /**
138
+ * Get current transport status
139
+ */
140
+ getStatus(): TransportStatus;
141
+ /**
142
+ * Get connected client
143
+ */
144
+ getClient(): Client | undefined;
145
+ /**
146
+ * Check if connected
147
+ */
148
+ isConnected(): boolean;
149
+ /**
150
+ * Reset reconnection attempts
151
+ */
152
+ resetReconnectAttempts(): void;
153
+ }