@juspay/neurolink 4.1.0 → 4.2.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 (75) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +105 -116
  3. package/dist/cli/commands/mcp.d.ts +11 -0
  4. package/dist/cli/commands/mcp.js +332 -223
  5. package/dist/cli/index.js +69 -8
  6. package/dist/core/factory.js +2 -2
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/lib/core/factory.js +2 -2
  10. package/dist/lib/index.d.ts +1 -1
  11. package/dist/lib/index.js +1 -1
  12. package/dist/lib/mcp/context-manager.d.ts +6 -0
  13. package/dist/lib/mcp/context-manager.js +8 -0
  14. package/dist/lib/mcp/contracts/mcpContract.d.ts +1 -0
  15. package/dist/lib/mcp/external-client.js +6 -2
  16. package/dist/lib/mcp/initialize.d.ts +2 -1
  17. package/dist/lib/mcp/initialize.js +8 -7
  18. package/dist/lib/mcp/orchestrator.js +9 -0
  19. package/dist/lib/mcp/registry.d.ts +1 -1
  20. package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.js +1 -1
  21. package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +3 -3
  22. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  23. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +1 -1
  24. package/dist/lib/mcp/session-manager.js +1 -1
  25. package/dist/lib/mcp/session-persistence.js +1 -1
  26. package/dist/lib/mcp/tool-registry.d.ts +31 -11
  27. package/dist/lib/mcp/tool-registry.js +226 -38
  28. package/dist/lib/mcp/unified-mcp.d.ts +12 -2
  29. package/dist/lib/mcp/unified-registry.d.ts +21 -7
  30. package/dist/lib/mcp/unified-registry.js +179 -17
  31. package/dist/lib/neurolink.js +17 -25
  32. package/dist/lib/providers/googleVertexAI.js +19 -1
  33. package/dist/lib/providers/openAI.js +18 -1
  34. package/dist/lib/utils/provider-setup-messages.d.ts +8 -0
  35. package/dist/lib/utils/provider-setup-messages.js +120 -0
  36. package/dist/lib/utils/provider-validation.d.ts +35 -0
  37. package/dist/lib/utils/provider-validation.js +625 -0
  38. package/dist/lib/utils/providerUtils-fixed.js +20 -1
  39. package/dist/lib/utils/providerUtils.d.ts +2 -2
  40. package/dist/lib/utils/providerUtils.js +38 -7
  41. package/dist/lib/utils/timeout-manager.d.ts +75 -0
  42. package/dist/lib/utils/timeout-manager.js +244 -0
  43. package/dist/mcp/context-manager.d.ts +6 -0
  44. package/dist/mcp/context-manager.js +8 -0
  45. package/dist/mcp/contracts/mcpContract.d.ts +1 -0
  46. package/dist/mcp/external-client.js +6 -2
  47. package/dist/mcp/initialize.d.ts +2 -1
  48. package/dist/mcp/initialize.js +8 -7
  49. package/dist/mcp/orchestrator.js +9 -0
  50. package/dist/mcp/plugins/core/neurolink-mcp.json +15 -15
  51. package/dist/mcp/registry.d.ts +1 -1
  52. package/dist/mcp/servers/ai-providers/ai-analysis-tools.js +1 -1
  53. package/dist/mcp/servers/ai-providers/ai-core-server.js +3 -3
  54. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  55. package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +1 -1
  56. package/dist/mcp/session-manager.js +1 -1
  57. package/dist/mcp/session-persistence.js +1 -1
  58. package/dist/mcp/tool-registry.d.ts +31 -11
  59. package/dist/mcp/tool-registry.js +226 -38
  60. package/dist/mcp/unified-mcp.d.ts +12 -2
  61. package/dist/mcp/unified-registry.d.ts +21 -7
  62. package/dist/mcp/unified-registry.js +179 -17
  63. package/dist/neurolink.js +17 -25
  64. package/dist/providers/googleVertexAI.js +19 -1
  65. package/dist/providers/openAI.js +18 -1
  66. package/dist/utils/provider-setup-messages.d.ts +8 -0
  67. package/dist/utils/provider-setup-messages.js +120 -0
  68. package/dist/utils/provider-validation.d.ts +35 -0
  69. package/dist/utils/provider-validation.js +625 -0
  70. package/dist/utils/providerUtils-fixed.js +20 -1
  71. package/dist/utils/providerUtils.d.ts +2 -2
  72. package/dist/utils/providerUtils.js +38 -7
  73. package/dist/utils/timeout-manager.d.ts +75 -0
  74. package/dist/utils/timeout-manager.js +244 -0
  75. package/package.json +245 -245
@@ -8,6 +8,8 @@ import { MCPToolRegistry, } from "./tool-registry.js";
8
8
  import { TransportManager, TransportConfigSchema, } from "./transport-manager.js";
9
9
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
10
10
  import { ErrorManager } from "./error-manager.js";
11
+ import * as fs from "fs";
12
+ import * as path from "path";
11
13
  /**
12
14
  * Unified registry combining multiple sources
13
15
  */
@@ -25,10 +27,12 @@ export class UnifiedMCPRegistry extends MCPToolRegistry {
25
27
  this.transportManager = new TransportManager(this.errorManager);
26
28
  }
27
29
  /**
28
- * Initialize with auto-discovery
30
+ * Initialize with auto-discovery and manual config
29
31
  */
30
32
  async initialize(options = {}) {
31
33
  unifiedRegistryLogger.info("Initializing unified MCP registry...");
34
+ // Load manual configuration first
35
+ await this.loadManualConfig();
32
36
  if (this.autoDiscoveryEnabled) {
33
37
  const result = await autoRegisterMCPServers(options);
34
38
  unifiedRegistryLogger.info(`Auto-discovery complete: ${result.registered} registered, ${result.failed} failed`);
@@ -40,6 +44,58 @@ export class UnifiedMCPRegistry extends MCPToolRegistry {
40
44
  }
41
45
  }
42
46
  }
47
+ /**
48
+ * Load servers from .mcp-config.json
49
+ */
50
+ async loadManualConfig() {
51
+ const configPath = path.join(process.cwd(), ".mcp-config.json");
52
+ try {
53
+ await fs.promises.access(configPath, fs.constants.F_OK);
54
+ }
55
+ catch {
56
+ unifiedRegistryLogger.debug("No .mcp-config.json found");
57
+ return;
58
+ }
59
+ try {
60
+ const configContent = await fs.promises.readFile(configPath, "utf-8");
61
+ const config = JSON.parse(configContent);
62
+ if (!config.mcpServers) {
63
+ unifiedRegistryLogger.debug("No mcpServers section in config");
64
+ return;
65
+ }
66
+ unifiedRegistryLogger.info(`Loading ${Object.keys(config.mcpServers).length} servers from .mcp-config.json`);
67
+ for (const [serverId, serverConfig] of Object.entries(config.mcpServers)) {
68
+ try {
69
+ // Convert server config to DiscoveredMCP format
70
+ const discoveredMcp = {
71
+ metadata: {
72
+ name: serverId,
73
+ version: "1.0.0",
74
+ main: "index.js",
75
+ engine: { neurolink: ">=4.0.0" },
76
+ description: `MCP server: ${serverId}`,
77
+ permissions: ["filesystem", "network"],
78
+ },
79
+ entryPath: serverConfig.command || "npx",
80
+ source: "project",
81
+ constructor: undefined,
82
+ };
83
+ // Register the server
84
+ this.register(discoveredMcp);
85
+ this.manualServers.set(serverId, serverConfig);
86
+ this.availableServers.add(serverId);
87
+ unifiedRegistryLogger.debug(`Registered manual server: ${serverId}`);
88
+ }
89
+ catch (error) {
90
+ unifiedRegistryLogger.error(`Failed to register server ${serverId}:`, error instanceof Error ? error.message : String(error));
91
+ }
92
+ }
93
+ unifiedRegistryLogger.info(`Manual config loaded: ${this.manualServers.size} servers registered`);
94
+ }
95
+ catch (error) {
96
+ unifiedRegistryLogger.error("Failed to load manual config:", error instanceof Error ? error.message : String(error));
97
+ }
98
+ }
43
99
  /**
44
100
  * Enable or disable auto-discovery
45
101
  */
@@ -85,30 +141,136 @@ export class UnifiedMCPRegistry extends MCPToolRegistry {
85
141
  */
86
142
  async listAllTools() {
87
143
  const allTools = [];
88
- const plugins = this.list();
144
+ try {
145
+ // FIXED: Get built-in tools from base registry
146
+ const builtInTools = await super.listTools();
147
+ allTools.push(...builtInTools.map((tool) => ({
148
+ ...tool,
149
+ id: tool.name,
150
+ serverId: tool.serverId || "built-in",
151
+ source: "built-in",
152
+ isExternal: false,
153
+ })));
154
+ unifiedRegistryLogger.debug(`Found ${builtInTools.length} built-in tools`);
155
+ }
156
+ catch (error) {
157
+ unifiedRegistryLogger.warn("Failed to get built-in tools:", error);
158
+ }
159
+ // FIXED: Get tools from external servers with proper error handling
160
+ // Use the internal plugin registry for accurate server listing
161
+ const plugins = Array.from(this.plugins.values());
162
+ const externalToolPromises = [];
89
163
  for (const plugin of plugins) {
90
- try {
91
- // Get tools from plugin metadata if available
92
- const tools = await this.listTools();
93
- allTools.push(...tools.map((tool) => ({
94
- ...tool,
95
- id: tool.name,
96
- serverId: tool.serverId || plugin.metadata.name,
97
- source: "unified",
98
- })));
99
- }
100
- catch (error) {
101
- unifiedRegistryLogger.warn(`Failed to get tools from ${plugin.metadata.name}:`, error);
164
+ if (plugin.metadata?.name &&
165
+ this.availableServers.has(plugin.metadata.name)) {
166
+ const connection = this.activeConnections.get(plugin.metadata.name);
167
+ if (connection) {
168
+ externalToolPromises.push(connection
169
+ .listTools()
170
+ .then((response) => {
171
+ const serverTools = response.tools || [];
172
+ allTools.push(...serverTools.map((tool) => ({
173
+ name: tool.name,
174
+ description: tool.description,
175
+ inputSchema: tool.inputSchema,
176
+ serverId: plugin.metadata.name,
177
+ id: `${plugin.metadata.name}.${tool.name}`,
178
+ source: "external",
179
+ isExternal: true,
180
+ })));
181
+ unifiedRegistryLogger.debug(`Found ${serverTools.length} tools from ${plugin.metadata.name}`);
182
+ })
183
+ .catch((error) => {
184
+ unifiedRegistryLogger.warn(`Failed to get tools from ${plugin.metadata.name}:`, error);
185
+ }));
186
+ }
102
187
  }
103
188
  }
189
+ await Promise.all(externalToolPromises);
190
+ unifiedRegistryLogger.info(`Total tools available: ${allTools.length}`);
104
191
  return allTools;
105
192
  }
106
193
  /**
107
- * Execute a tool through the registry
194
+ * Execute a tool through the registry with fallback to direct MCP execution
108
195
  */
109
196
  async executeTool(toolName, args, context) {
110
197
  unifiedRegistryLogger.info(`Executing tool: ${toolName}`);
111
- return super.executeTool(toolName, args, context);
198
+ // STEP 1: Try built-in tools first
199
+ try {
200
+ const result = await super.executeTool(toolName, args, context);
201
+ unifiedRegistryLogger.info(`Tool ${toolName} executed successfully via built-in registry`);
202
+ return result;
203
+ }
204
+ catch (builtInError) {
205
+ unifiedRegistryLogger.debug(`Built-in tool execution failed: ${builtInError.message}`);
206
+ }
207
+ // STEP 2: Try external MCP servers
208
+ try {
209
+ const result = await this.executeToolViaMCPServer(toolName, args, context);
210
+ unifiedRegistryLogger.info(`Tool ${toolName} executed successfully via external MCP server`);
211
+ return result;
212
+ }
213
+ catch (externalError) {
214
+ unifiedRegistryLogger.debug(`External MCP execution failed: ${externalError.message}`);
215
+ }
216
+ // STEP 3: Comprehensive error with available tools
217
+ const availableTools = await this.listAllTools();
218
+ const toolNames = availableTools.map((t) => t.name).join(", ");
219
+ const builtInTools = availableTools
220
+ .filter((t) => !t.isExternal)
221
+ .map((t) => t.name)
222
+ .join(", ");
223
+ const externalTools = availableTools
224
+ .filter((t) => t.isExternal)
225
+ .map((t) => `${t.serverId}.${t.name}`)
226
+ .join(", ");
227
+ const errorMessage = [
228
+ `Tool '${toolName}' not found in any registry.`,
229
+ `Available built-in tools: ${builtInTools || "none"}`,
230
+ `Available external tools: ${externalTools || "none"}`,
231
+ `Connected servers: ${Array.from(this.availableServers).join(", ") || "none"}`,
232
+ ].join("\n");
233
+ throw new Error(errorMessage);
234
+ }
235
+ /**
236
+ * Execute tool via direct MCP server connection (fallback)
237
+ */
238
+ async executeToolViaMCPServer(toolName, args, context) {
239
+ const configPath = path.join(process.cwd(), ".mcp-config.json");
240
+ if (!fs.existsSync(configPath)) {
241
+ throw new Error(`Tool '${toolName}' not found and no .mcp-config.json for fallback`);
242
+ }
243
+ const configContent = fs.readFileSync(configPath, "utf-8");
244
+ const config = JSON.parse(configContent);
245
+ if (!config.mcpServers) {
246
+ throw new Error(`Tool '${toolName}' not found and no servers configured`);
247
+ }
248
+ // Try each configured server
249
+ const errors = [];
250
+ for (const [serverId, serverConfig] of Object.entries(config.mcpServers)) {
251
+ try {
252
+ unifiedRegistryLogger.debug(`Trying tool ${toolName} on server ${serverId}`);
253
+ // Import the executeMCPTool function
254
+ const { executeMCPTool } = await import("../../cli/commands/mcp.js");
255
+ const result = await executeMCPTool(serverConfig, toolName, args || {});
256
+ // Convert to ToolResult format
257
+ const toolResult = {
258
+ success: true,
259
+ data: result,
260
+ metadata: { toolName, serverId, sessionId: context?.sessionId },
261
+ };
262
+ unifiedRegistryLogger.info(`Tool ${toolName} executed successfully via server ${serverId}`);
263
+ return toolResult;
264
+ }
265
+ catch (serverError) {
266
+ const errorMsg = serverError instanceof Error
267
+ ? serverError.message
268
+ : String(serverError);
269
+ errors.push(`${serverId}: ${errorMsg}`);
270
+ unifiedRegistryLogger.debug(`Tool ${toolName} failed on server ${serverId}: ${errorMsg}`);
271
+ }
272
+ }
273
+ throw new Error(`Tool '${toolName}' not found on any configured MCP server. Errors: ${errors.join("; ")}`);
112
274
  }
113
275
  /**
114
276
  * Lazily activate a server by ID
@@ -144,7 +306,7 @@ export class UnifiedMCPRegistry extends MCPToolRegistry {
144
306
  * Get registry statistics (override parent method)
145
307
  */
146
308
  getStats() {
147
- // Return execution stats in the expected format
309
+ // Return full stats interface as expected by MCPOrchestrator
148
310
  return super.getStats();
149
311
  }
150
312
  /**
package/dist/neurolink.js CHANGED
@@ -73,6 +73,12 @@ export class NeuroLink {
73
73
  * Tools are ENABLED BY DEFAULT for natural AI behavior
74
74
  */
75
75
  async generateText(options) {
76
+ // 🔧 FIX: Add input validation
77
+ if (!options ||
78
+ typeof options.prompt !== "string" ||
79
+ options.prompt.trim() === "") {
80
+ throw new Error("options.prompt is required and must be a non-empty string");
81
+ }
76
82
  // Tools are DEFAULT behavior unless explicitly disabled
77
83
  if (options.disableTools === true) {
78
84
  return this.generateTextRegular(options);
@@ -101,7 +107,7 @@ export class NeuroLink {
101
107
  try {
102
108
  mcpLogger.debug(`[${functionTag}] Starting MCP-enabled generation`, {
103
109
  provider: providerName,
104
- prompt: options.prompt.substring(0, 100) + "...",
110
+ prompt: (options.prompt?.substring(0, 100) || "No prompt") + "...",
105
111
  contextId: context.sessionId,
106
112
  });
107
113
  // Get available tools from tool registry (simplified approach)
@@ -205,22 +211,15 @@ export class NeuroLink {
205
211
  "ollama",
206
212
  ];
207
213
  const requestedProvider = options.provider === "auto" ? undefined : options.provider;
208
- // Local providers that should not fall back when explicitly requested
209
- const localProviders = ["ollama"];
210
- // If specific provider requested, check if we should allow fallback
214
+ // If specific provider requested, only use that provider (no fallback)
211
215
  const tryProviders = requestedProvider
212
- ? localProviders.includes(requestedProvider)
213
- ? [requestedProvider] // No fallback for local providers
214
- : [
215
- requestedProvider,
216
- ...providerPriority.filter((p) => p !== requestedProvider),
217
- ]
216
+ ? [requestedProvider] // Only use the requested provider, no fallback
218
217
  : providerPriority;
219
218
  logger.debug(`[${functionTag}] Starting text generation`, {
220
219
  requestedProvider: requestedProvider || "auto",
221
220
  tryProviders,
222
- allowFallback: !requestedProvider || !localProviders.includes(requestedProvider),
223
- promptLength: options.prompt.length,
221
+ allowFallback: !requestedProvider,
222
+ promptLength: options.prompt?.length || 0,
224
223
  });
225
224
  let lastError = null;
226
225
  for (const providerName of tryProviders) {
@@ -228,7 +227,7 @@ export class NeuroLink {
228
227
  logger.debug(`[${functionTag}] Attempting provider`, {
229
228
  provider: providerName,
230
229
  });
231
- const provider = await AIProviderFactory.createProvider(providerName, options.model);
230
+ const provider = await AIProviderFactory.createProvider(providerName, options.model, false);
232
231
  const result = await provider.generateText({
233
232
  prompt: options.prompt,
234
233
  model: options.model,
@@ -343,21 +342,14 @@ Note: Tool integration is currently in development. Please provide helpful respo
343
342
  "ollama",
344
343
  ];
345
344
  const requestedProvider = options.provider === "auto" ? undefined : options.provider;
346
- // Local providers that should not fall back when explicitly requested
347
- const localProviders = ["ollama"];
348
- // If specific provider requested, check if we should allow fallback
345
+ // If specific provider requested, only use that provider (no fallback)
349
346
  const tryProviders = requestedProvider
350
- ? localProviders.includes(requestedProvider)
351
- ? [requestedProvider] // No fallback for local providers
352
- : [
353
- requestedProvider,
354
- ...providerPriority.filter((p) => p !== requestedProvider),
355
- ]
347
+ ? [requestedProvider] // Only use the requested provider, no fallback
356
348
  : providerPriority;
357
349
  logger.debug(`[${functionTag}] Starting stream generation`, {
358
350
  requestedProvider: requestedProvider || "auto",
359
351
  tryProviders,
360
- allowFallback: !requestedProvider || !localProviders.includes(requestedProvider),
352
+ allowFallback: !requestedProvider,
361
353
  promptLength: options.prompt.length,
362
354
  });
363
355
  let lastError = null;
@@ -366,7 +358,7 @@ Note: Tool integration is currently in development. Please provide helpful respo
366
358
  logger.debug(`[${functionTag}] Attempting provider`, {
367
359
  provider: providerName,
368
360
  });
369
- const provider = await AIProviderFactory.createProvider(providerName, options.model);
361
+ const provider = await AIProviderFactory.createProvider(providerName, options.model, false);
370
362
  const result = await provider.streamText({
371
363
  prompt: options.prompt,
372
364
  model: options.model,
@@ -430,7 +422,7 @@ Note: Tool integration is currently in development. Please provide helpful respo
430
422
  */
431
423
  async testProvider(providerName, testPrompt = "test") {
432
424
  try {
433
- const provider = await AIProviderFactory.createProvider(providerName);
425
+ const provider = await AIProviderFactory.createProvider(providerName, null, false); // Disable MCP for simple testing
434
426
  await provider.generateText({
435
427
  prompt: testPrompt,
436
428
  enableAnalytics: false,
@@ -35,7 +35,25 @@ const DEFAULT_SYSTEM_CONTEXT = {
35
35
  const getGCPVertexBreezeProjectId = () => {
36
36
  const projectId = process.env.GOOGLE_VERTEX_PROJECT;
37
37
  if (!projectId) {
38
- throw new Error("GOOGLE_VERTEX_PROJECT environment variable is not set");
38
+ // 🔧 FIX: Enhanced error message with setup instructions
39
+ throw new Error(`❌ VERTEX Provider Configuration Error
40
+
41
+ Missing required environment variables: GOOGLE_VERTEX_PROJECT
42
+
43
+ 🔧 Step 1: Get Credentials
44
+ Set up Google Cloud project and download service account JSON
45
+
46
+ 💡 Step 2: Add to your .env file (or export in CLI):
47
+ GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
48
+ GOOGLE_VERTEX_PROJECT="your-gcp-project-id"
49
+ GOOGLE_VERTEX_LOCATION="us-central1"
50
+ # Optional:
51
+ VERTEX_MODEL="gemini-2.5-pro"
52
+
53
+ 🚀 Step 3: Test the setup:
54
+ npx neurolink generate "Hello" --provider vertex
55
+
56
+ 📖 Full setup guide: https://docs.neurolink.ai/providers/vertex`);
39
57
  }
40
58
  return projectId;
41
59
  };
@@ -13,7 +13,24 @@ const DEFAULT_SYSTEM_CONTEXT = {
13
13
  const getOpenAIApiKey = () => {
14
14
  const apiKey = process.env.OPENAI_API_KEY;
15
15
  if (!apiKey) {
16
- throw new Error("OPENAI_API_KEY environment variable is not set");
16
+ // 🔧 FIX: Enhanced error message with setup instructions
17
+ throw new Error(`❌ OPENAI Provider Configuration Error
18
+
19
+ Missing required environment variables: OPENAI_API_KEY
20
+
21
+ 🔧 Step 1: Get Credentials
22
+ Get your API key from https://platform.openai.com/api-keys
23
+
24
+ 💡 Step 2: Add to your .env file (or export in CLI):
25
+ OPENAI_API_KEY="sk-proj-your-openai-api-key"
26
+ # Optional:
27
+ OPENAI_MODEL="gpt-4o"
28
+ OPENAI_BASE_URL="https://api.openai.com"
29
+
30
+ 🚀 Step 3: Test the setup:
31
+ npx neurolink generate "Hello" --provider openai
32
+
33
+ 📖 Full setup guide: https://docs.neurolink.ai/providers/openai`);
17
34
  }
18
35
  return apiKey;
19
36
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Enhanced Provider Setup Messages
3
+ * Provides detailed setup instructions for AI providers
4
+ */
5
+ /**
6
+ * Generate enhanced error message with setup instructions
7
+ */
8
+ export declare function getProviderSetupMessage(provider: string, missingVars: string[]): string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Enhanced Provider Setup Messages
3
+ * Provides detailed setup instructions for AI providers
4
+ */
5
+ /**
6
+ * Generate enhanced error message with setup instructions
7
+ */
8
+ export function getProviderSetupMessage(provider, missingVars) {
9
+ const providerSetup = {
10
+ openai: {
11
+ guide: "Get your API key from https://platform.openai.com/api-keys",
12
+ envVars: [
13
+ 'OPENAI_API_KEY="sk-proj-your-openai-api-key"',
14
+ "# Optional:",
15
+ 'OPENAI_MODEL="gpt-4o"',
16
+ 'OPENAI_BASE_URL="https://api.openai.com"',
17
+ ],
18
+ },
19
+ anthropic: {
20
+ guide: "Get your API key from https://console.anthropic.com/",
21
+ envVars: [
22
+ 'ANTHROPIC_API_KEY="sk-ant-api03-your-anthropic-key"',
23
+ "# Optional:",
24
+ 'ANTHROPIC_MODEL="claude-3-5-sonnet-20241022"',
25
+ ],
26
+ },
27
+ "google-ai": {
28
+ guide: "Get your API key from https://aistudio.google.com/app/apikey",
29
+ envVars: [
30
+ 'GOOGLE_AI_API_KEY="AIza-your-google-ai-api-key"',
31
+ "# Optional:",
32
+ 'GOOGLE_AI_MODEL="gemini-2.5-pro"',
33
+ ],
34
+ },
35
+ vertex: {
36
+ guide: "Set up Google Cloud project and download service account JSON",
37
+ envVars: [
38
+ 'GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"',
39
+ 'GOOGLE_VERTEX_PROJECT="your-gcp-project-id"',
40
+ 'GOOGLE_VERTEX_LOCATION="us-central1"',
41
+ "# Optional:",
42
+ 'VERTEX_MODEL="gemini-2.5-pro"',
43
+ ],
44
+ },
45
+ bedrock: {
46
+ guide: "Set up AWS credentials and request model access in Bedrock console",
47
+ envVars: [
48
+ 'AWS_ACCESS_KEY_ID="AKIA..."',
49
+ 'AWS_SECRET_ACCESS_KEY="your-aws-secret-key"',
50
+ 'AWS_REGION="us-east-1"',
51
+ "# Use full inference profile ARN for Anthropic models:",
52
+ 'BEDROCK_MODEL="arn:aws:bedrock:us-east-1:123456789:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0"',
53
+ "# Or simple name for Amazon models:",
54
+ '# BEDROCK_MODEL="amazon.titan-text-express-v1"',
55
+ ],
56
+ },
57
+ azure: {
58
+ guide: "Set up Azure OpenAI resource and create deployment",
59
+ envVars: [
60
+ 'AZURE_OPENAI_API_KEY="your-azure-openai-key"',
61
+ 'AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"',
62
+ 'AZURE_OPENAI_DEPLOYMENT_ID="your-deployment-name"',
63
+ "# Optional:",
64
+ 'AZURE_MODEL="gpt-4o"',
65
+ 'AZURE_API_VERSION="2024-02-15-preview"',
66
+ ],
67
+ },
68
+ huggingface: {
69
+ guide: "Get your API token from https://huggingface.co/settings/tokens",
70
+ envVars: [
71
+ 'HUGGINGFACE_API_KEY="hf_your_huggingface_token"',
72
+ "# Optional:",
73
+ 'HUGGINGFACE_MODEL="microsoft/DialoGPT-medium"',
74
+ 'HUGGINGFACE_ENDPOINT="https://api-inference.huggingface.co"',
75
+ ],
76
+ },
77
+ mistral: {
78
+ guide: "Get your API key from https://mistral.ai/platform",
79
+ envVars: [
80
+ 'MISTRAL_API_KEY="your_mistral_api_key"',
81
+ "# Optional:",
82
+ 'MISTRAL_MODEL="mistral-small"',
83
+ 'MISTRAL_ENDPOINT="https://api.mistral.ai"',
84
+ ],
85
+ },
86
+ ollama: {
87
+ guide: "Install Ollama and pull models locally",
88
+ envVars: [
89
+ "# Ollama runs locally - no API key needed",
90
+ 'OLLAMA_BASE_URL="http://localhost:11434"',
91
+ 'OLLAMA_MODEL="llama2"',
92
+ "",
93
+ "# First install and start Ollama:",
94
+ "# macOS: brew install ollama",
95
+ "# Linux: curl -fsSL https://ollama.ai/install.sh | sh",
96
+ "# Then pull a model: ollama pull llama2",
97
+ ],
98
+ },
99
+ };
100
+ const setup = providerSetup[provider];
101
+ if (!setup) {
102
+ return `❌ ${provider.toUpperCase()} Provider Configuration Error\nMissing variables: ${missingVars.join(", ")}\nCheck provider documentation for setup instructions.`;
103
+ }
104
+ return `
105
+ ❌ ${provider.toUpperCase()} Provider Configuration Error
106
+
107
+ Missing required environment variables: ${missingVars.join(", ")}
108
+
109
+ 🔧 Step 1: Get Credentials
110
+ ${setup.guide}
111
+
112
+ 💡 Step 2: Add to your .env file (or export in CLI):
113
+ ${setup.envVars.join("\n")}
114
+
115
+ 🚀 Step 3: Test the setup:
116
+ npx neurolink generate "Hello" --provider ${provider}
117
+
118
+ 📖 Full setup guide: https://docs.neurolink.ai/providers/${provider}
119
+ `;
120
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Enhanced Provider Validation Utilities
3
+ *
4
+ * Fixes false positives in provider status checking by implementing:
5
+ * - API key format validation
6
+ * - Lightweight authentication checks
7
+ * - Proper error classification
8
+ * - Rate-limit friendly validation
9
+ */
10
+ export interface ProviderValidationResult {
11
+ configured: boolean;
12
+ formatValid: boolean;
13
+ authenticated: boolean;
14
+ available: boolean;
15
+ error?: string;
16
+ errorType?: "config" | "format" | "auth" | "network" | "quota" | "unknown";
17
+ responseTime?: number;
18
+ details?: Record<string, any>;
19
+ }
20
+ /**
21
+ * Validate API key format for a specific provider
22
+ */
23
+ export declare function validateApiKeyFormat(provider: string, apiKey: string): boolean;
24
+ /**
25
+ * Comprehensive provider validation that prevents false positives
26
+ */
27
+ export declare function validateProvider(provider: string): Promise<ProviderValidationResult>;
28
+ /**
29
+ * Batch validate multiple providers efficiently
30
+ */
31
+ export declare function validateProviders(providers: string[]): Promise<Record<string, ProviderValidationResult>>;
32
+ /**
33
+ * Check if provider validation should be cached (to avoid rate limits)
34
+ */
35
+ export declare function shouldCacheValidation(result: ProviderValidationResult): boolean;