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