@lanonasis/cli 3.6.3 → 3.6.5
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/dist/commands/api-keys.d.ts +1 -2
- package/dist/commands/api-keys.js +78 -73
- package/dist/commands/auth.js +167 -160
- package/dist/commands/completion.js +39 -31
- package/dist/commands/config.js +201 -162
- package/dist/commands/enhanced-memory.js +17 -11
- package/dist/commands/guide.js +88 -79
- package/dist/commands/init.js +20 -14
- package/dist/commands/mcp.js +173 -142
- package/dist/commands/memory.js +83 -77
- package/dist/commands/organization.js +21 -15
- package/dist/commands/topics.js +58 -52
- package/dist/core/achievements.js +26 -19
- package/dist/core/architecture.js +59 -42
- package/dist/core/dashboard.js +81 -71
- package/dist/core/error-handler.js +39 -30
- package/dist/core/power-mode.js +53 -46
- package/dist/core/progress.js +44 -35
- package/dist/core/welcome.js +64 -56
- package/dist/enhanced-cli.js +58 -49
- package/dist/index-simple.js +112 -74
- package/dist/index.js +68 -63
- package/dist/mcp/access-control.js +17 -13
- package/dist/mcp/client/enhanced-client.js +23 -16
- package/dist/mcp/enhanced-server.js +14 -10
- package/dist/mcp/logger.js +6 -2
- package/dist/mcp/memory-state.js +17 -13
- package/dist/mcp/schemas/tool-schemas.d.ts +28 -28
- package/dist/mcp/schemas/tool-schemas.js +126 -122
- package/dist/mcp/server/lanonasis-server.js +51 -44
- package/dist/mcp/transports/transport-manager.js +25 -18
- package/dist/mcp/vector-store.js +10 -6
- package/dist/mcp-server.js +21 -17
- package/dist/utils/api.js +30 -21
- package/dist/utils/config.js +61 -15
- package/dist/utils/formatting.js +14 -6
- package/dist/utils/mcp-client.js +132 -77
- package/package.json +17 -92
- package/dist/completions/bash-completion.sh +0 -88
- package/dist/completions/fish-completion.fish +0 -132
- package/dist/completions/zsh-completion.zsh +0 -196
package/dist/utils/config.js
CHANGED
|
@@ -1,9 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CLIConfig = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const jwt_decode_1 = require("jwt-decode");
|
|
41
|
+
const crypto_1 = require("crypto");
|
|
42
|
+
class CLIConfig {
|
|
7
43
|
configDir;
|
|
8
44
|
configPath;
|
|
9
45
|
config = {};
|
|
@@ -83,7 +119,7 @@ export class CLIConfig {
|
|
|
83
119
|
this.config.version = CLIConfig.CONFIG_VERSION;
|
|
84
120
|
this.config.lastUpdated = new Date().toISOString();
|
|
85
121
|
// Create temporary file with unique name
|
|
86
|
-
const tempPath = `${this.configPath}.tmp.${randomUUID()}`;
|
|
122
|
+
const tempPath = `${this.configPath}.tmp.${(0, crypto_1.randomUUID)()}`;
|
|
87
123
|
// Write to temporary file first
|
|
88
124
|
await fs.writeFile(tempPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
|
89
125
|
// Atomic rename - this is the critical atomic operation
|
|
@@ -178,14 +214,22 @@ export class CLIConfig {
|
|
|
178
214
|
}
|
|
179
215
|
const response = await axios.get(discoveryUrl, {
|
|
180
216
|
timeout: 10000,
|
|
217
|
+
maxRedirects: 5,
|
|
218
|
+
proxy: false, // Bypass proxy to avoid redirect loops
|
|
181
219
|
headers: {
|
|
182
220
|
'User-Agent': 'Lanonasis-CLI/3.0.13'
|
|
183
221
|
}
|
|
184
222
|
});
|
|
185
223
|
// Map discovery response to our config format
|
|
186
224
|
const discovered = response.data;
|
|
225
|
+
// Extract auth base, but filter out localhost URLs
|
|
226
|
+
let authBase = discovered.auth?.base || discovered.auth?.login?.replace('/auth/login', '') || '';
|
|
227
|
+
// Override localhost with production auth endpoint
|
|
228
|
+
if (authBase.includes('localhost') || authBase.includes('127.0.0.1')) {
|
|
229
|
+
authBase = 'https://auth.lanonasis.com';
|
|
230
|
+
}
|
|
187
231
|
this.config.discoveredServices = {
|
|
188
|
-
auth_base:
|
|
232
|
+
auth_base: authBase || 'https://auth.lanonasis.com',
|
|
189
233
|
memory_base: 'https://api.lanonasis.com/api/v1',
|
|
190
234
|
mcp_base: discovered.endpoints?.http || 'https://mcp.lanonasis.com/api/v1',
|
|
191
235
|
mcp_ws_base: discovered.endpoints?.websocket || 'wss://mcp.lanonasis.com/ws',
|
|
@@ -381,15 +425,16 @@ export class CLIConfig {
|
|
|
381
425
|
const axios = (await import('axios')).default;
|
|
382
426
|
// Ensure service discovery is done
|
|
383
427
|
await this.discoverServices();
|
|
384
|
-
const authBase = this.config.discoveredServices?.auth_base || 'https://
|
|
428
|
+
const authBase = this.config.discoveredServices?.auth_base || 'https://auth.lanonasis.com';
|
|
385
429
|
// Test vendor key with health endpoint
|
|
386
|
-
await axios.get(`${authBase}/
|
|
430
|
+
await axios.get(`${authBase}/health`, {
|
|
387
431
|
headers: {
|
|
388
432
|
'X-API-Key': vendorKey,
|
|
389
433
|
'X-Auth-Method': 'vendor_key',
|
|
390
434
|
'X-Project-Scope': 'lanonasis-maas'
|
|
391
435
|
},
|
|
392
|
-
timeout: 10000
|
|
436
|
+
timeout: 10000,
|
|
437
|
+
proxy: false // Bypass proxy to avoid redirect loops
|
|
393
438
|
});
|
|
394
439
|
}
|
|
395
440
|
catch (error) {
|
|
@@ -452,7 +497,7 @@ export class CLIConfig {
|
|
|
452
497
|
await this.resetFailureCount(); // Reset failure count on successful auth
|
|
453
498
|
// Decode token to get user info and expiry
|
|
454
499
|
try {
|
|
455
|
-
const decoded = jwtDecode(token);
|
|
500
|
+
const decoded = (0, jwt_decode_1.jwtDecode)(token);
|
|
456
501
|
// Store token expiry
|
|
457
502
|
if (typeof decoded.exp === 'number') {
|
|
458
503
|
this.config.tokenExpiry = decoded.exp;
|
|
@@ -509,7 +554,7 @@ export class CLIConfig {
|
|
|
509
554
|
else {
|
|
510
555
|
// Handle JWT tokens
|
|
511
556
|
try {
|
|
512
|
-
const decoded = jwtDecode(token);
|
|
557
|
+
const decoded = (0, jwt_decode_1.jwtDecode)(token);
|
|
513
558
|
const now = Date.now() / 1000;
|
|
514
559
|
locallyValid = typeof decoded.exp === 'number' && decoded.exp > now;
|
|
515
560
|
}
|
|
@@ -676,7 +721,7 @@ export class CLIConfig {
|
|
|
676
721
|
// CLI tokens don't need refresh, they're long-lived
|
|
677
722
|
return;
|
|
678
723
|
}
|
|
679
|
-
const decoded = jwtDecode(token);
|
|
724
|
+
const decoded = (0, jwt_decode_1.jwtDecode)(token);
|
|
680
725
|
const now = Date.now() / 1000;
|
|
681
726
|
const exp = typeof decoded.exp === 'number' ? decoded.exp : 0;
|
|
682
727
|
// Refresh if token expires within 5 minutes
|
|
@@ -750,7 +795,7 @@ export class CLIConfig {
|
|
|
750
795
|
async getDeviceId() {
|
|
751
796
|
if (!this.config.deviceId) {
|
|
752
797
|
// Generate a new device ID
|
|
753
|
-
this.config.deviceId = randomUUID();
|
|
798
|
+
this.config.deviceId = (0, crypto_1.randomUUID)();
|
|
754
799
|
await this.save();
|
|
755
800
|
}
|
|
756
801
|
return this.config.deviceId;
|
|
@@ -799,3 +844,4 @@ export class CLIConfig {
|
|
|
799
844
|
}
|
|
800
845
|
}
|
|
801
846
|
}
|
|
847
|
+
exports.CLIConfig = CLIConfig;
|
package/dist/utils/formatting.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatOutput = formatOutput;
|
|
4
|
+
exports.formatBytes = formatBytes;
|
|
5
|
+
exports.truncateText = truncateText;
|
|
6
|
+
exports.formatDuration = formatDuration;
|
|
7
|
+
exports.formatDate = formatDate;
|
|
8
|
+
exports.formatTableData = formatTableData;
|
|
9
|
+
function formatOutput(data, format = 'table') {
|
|
2
10
|
switch (format) {
|
|
3
11
|
case 'json':
|
|
4
12
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -12,7 +20,7 @@ export function formatOutput(data, format = 'table') {
|
|
|
12
20
|
break;
|
|
13
21
|
}
|
|
14
22
|
}
|
|
15
|
-
|
|
23
|
+
function formatBytes(bytes) {
|
|
16
24
|
if (bytes === 0)
|
|
17
25
|
return '0 Bytes';
|
|
18
26
|
const k = 1024;
|
|
@@ -20,23 +28,23 @@ export function formatBytes(bytes) {
|
|
|
20
28
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
21
29
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
22
30
|
}
|
|
23
|
-
|
|
31
|
+
function truncateText(text, maxLength) {
|
|
24
32
|
if (text.length <= maxLength)
|
|
25
33
|
return text;
|
|
26
34
|
return text.substring(0, maxLength - 3) + '...';
|
|
27
35
|
}
|
|
28
|
-
|
|
36
|
+
function formatDuration(ms) {
|
|
29
37
|
if (ms < 1000)
|
|
30
38
|
return `${ms}ms`;
|
|
31
39
|
if (ms < 60000)
|
|
32
40
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
33
41
|
return `${(ms / 60000).toFixed(1)}m`;
|
|
34
42
|
}
|
|
35
|
-
|
|
43
|
+
function formatDate(date) {
|
|
36
44
|
const d = new Date(date);
|
|
37
45
|
return d.toLocaleString();
|
|
38
46
|
}
|
|
39
|
-
|
|
47
|
+
function formatTableData(data) {
|
|
40
48
|
return data.map(item => {
|
|
41
49
|
if (Array.isArray(item))
|
|
42
50
|
return item.map(String);
|
package/dist/utils/mcp-client.js
CHANGED
|
@@ -1,11 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.MCPClient = void 0;
|
|
40
|
+
exports.getMCPClient = getMCPClient;
|
|
41
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
42
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const config_js_1 = require("./config.js");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const eventsource_1 = require("eventsource");
|
|
47
|
+
const ws_1 = __importDefault(require("ws"));
|
|
48
|
+
class MCPClient {
|
|
9
49
|
client = null;
|
|
10
50
|
config;
|
|
11
51
|
isConnected = false;
|
|
@@ -18,7 +58,7 @@ export class MCPClient {
|
|
|
18
58
|
lastHealthCheck = null;
|
|
19
59
|
activeConnectionMode = 'local'; // Track actual connection mode
|
|
20
60
|
constructor() {
|
|
21
|
-
this.config = new CLIConfig();
|
|
61
|
+
this.config = new config_js_1.CLIConfig();
|
|
22
62
|
}
|
|
23
63
|
/**
|
|
24
64
|
* Overrides the configuration directory used by the underlying CLI config.
|
|
@@ -94,10 +134,10 @@ export class MCPClient {
|
|
|
94
134
|
'wss://mcp.lanonasis.com/ws';
|
|
95
135
|
wsUrl = wsUrlValue;
|
|
96
136
|
if (this.retryAttempts === 0) {
|
|
97
|
-
console.log(
|
|
137
|
+
console.log(chalk_1.default.cyan(`Connecting to WebSocket MCP server at ${wsUrl}...`));
|
|
98
138
|
}
|
|
99
139
|
else {
|
|
100
|
-
console.log(
|
|
140
|
+
console.log(chalk_1.default.yellow(`Retry ${this.retryAttempts}/${this.maxRetries}: Connecting to WebSocket MCP server...`));
|
|
101
141
|
}
|
|
102
142
|
// Initialize WebSocket connection
|
|
103
143
|
await this.initializeWebSocket(wsUrl);
|
|
@@ -115,10 +155,10 @@ export class MCPClient {
|
|
|
115
155
|
'https://mcp.lanonasis.com/api/v1';
|
|
116
156
|
serverUrl = serverUrlValue;
|
|
117
157
|
if (this.retryAttempts === 0) {
|
|
118
|
-
console.log(
|
|
158
|
+
console.log(chalk_1.default.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
|
|
119
159
|
}
|
|
120
160
|
else {
|
|
121
|
-
console.log(
|
|
161
|
+
console.log(chalk_1.default.yellow(`Retry ${this.retryAttempts}/${this.maxRetries}: Connecting to remote MCP server...`));
|
|
122
162
|
}
|
|
123
163
|
// Initialize SSE connection for real-time updates
|
|
124
164
|
await this.initializeSSE(serverUrl);
|
|
@@ -132,22 +172,22 @@ export class MCPClient {
|
|
|
132
172
|
// Local MCP server connection requires explicit path via option or config
|
|
133
173
|
serverPath = options.serverPath ?? this.config.get('mcpServerPath');
|
|
134
174
|
if (!serverPath) {
|
|
135
|
-
console.log(
|
|
136
|
-
console.log(
|
|
137
|
-
console.log(
|
|
175
|
+
console.log(chalk_1.default.yellow('⚠️ No local MCP server path configured.'));
|
|
176
|
+
console.log(chalk_1.default.cyan('💡 Prefer using WebSocket mode (default). Or configure a local path via:'));
|
|
177
|
+
console.log(chalk_1.default.cyan(' lanonasis config set mcpServerPath /absolute/path/to/server.js'));
|
|
138
178
|
throw new Error('Local MCP server path not provided');
|
|
139
179
|
}
|
|
140
180
|
// Check if the server file exists
|
|
141
181
|
if (!fs.existsSync(serverPath)) {
|
|
142
|
-
console.log(
|
|
143
|
-
console.log(
|
|
182
|
+
console.log(chalk_1.default.yellow(`⚠️ Local MCP server not found at ${serverPath}`));
|
|
183
|
+
console.log(chalk_1.default.cyan('💡 For remote use WebSocket: lanonasis mcp connect --mode websocket --url wss://mcp.lanonasis.com/ws'));
|
|
144
184
|
throw new Error(`MCP server not found at ${serverPath}`);
|
|
145
185
|
}
|
|
146
186
|
if (this.retryAttempts === 0) {
|
|
147
|
-
console.log(
|
|
187
|
+
console.log(chalk_1.default.cyan(`Connecting to local MCP server at ${serverPath}...`));
|
|
148
188
|
}
|
|
149
189
|
else {
|
|
150
|
-
console.log(
|
|
190
|
+
console.log(chalk_1.default.yellow(`Retry ${this.retryAttempts}/${this.maxRetries}: Connecting to local MCP server...`));
|
|
151
191
|
}
|
|
152
192
|
// Allow passing extra args to local server (e.g., --stdio) via options or env/config
|
|
153
193
|
// Precedence: options.localArgs -> env.MCP_LOCAL_SERVER_ARGS -> config.mcpLocalArgs -> none
|
|
@@ -160,11 +200,11 @@ export class MCPClient {
|
|
|
160
200
|
? options.localArgs
|
|
161
201
|
: (envArgs.length > 0 ? envArgs : configArgs);
|
|
162
202
|
const args = [serverPath, ...extraArgs];
|
|
163
|
-
const localTransport = new StdioClientTransport({
|
|
203
|
+
const localTransport = new stdio_js_1.StdioClientTransport({
|
|
164
204
|
command: 'node',
|
|
165
205
|
args
|
|
166
206
|
});
|
|
167
|
-
this.client = new Client({
|
|
207
|
+
this.client = new index_js_1.Client({
|
|
168
208
|
name: '@lanonasis/cli',
|
|
169
209
|
version: '3.0.1'
|
|
170
210
|
});
|
|
@@ -172,7 +212,7 @@ export class MCPClient {
|
|
|
172
212
|
this.isConnected = true;
|
|
173
213
|
this.activeConnectionMode = 'local';
|
|
174
214
|
this.retryAttempts = 0;
|
|
175
|
-
console.log(
|
|
215
|
+
console.log(chalk_1.default.green('✓ Connected to MCP server'));
|
|
176
216
|
this.startHealthMonitoring();
|
|
177
217
|
return true;
|
|
178
218
|
}
|
|
@@ -183,7 +223,7 @@ export class MCPClient {
|
|
|
183
223
|
?? this.config.getMCPRestUrl()
|
|
184
224
|
?? 'https://mcp.lanonasis.com/api/v1';
|
|
185
225
|
serverUrl = serverUrlValue;
|
|
186
|
-
console.log(
|
|
226
|
+
console.log(chalk_1.default.yellow(`Unknown connection mode '${String(connectionMode)}', falling back to remote at ${serverUrl}`));
|
|
187
227
|
await this.initializeSSE(serverUrl);
|
|
188
228
|
this.isConnected = true;
|
|
189
229
|
this.activeConnectionMode = 'remote';
|
|
@@ -204,23 +244,23 @@ export class MCPClient {
|
|
|
204
244
|
// Check if this is an authentication error (don't retry these)
|
|
205
245
|
if (this.isAuthenticationError(error)) {
|
|
206
246
|
const authMsg = error?.message ?? '';
|
|
207
|
-
console.error(
|
|
247
|
+
console.error(chalk_1.default.red('Authentication failed:'), authMsg);
|
|
208
248
|
this.provideAuthenticationGuidance(error);
|
|
209
249
|
this.isConnected = false;
|
|
210
250
|
return false;
|
|
211
251
|
}
|
|
212
252
|
this.retryAttempts++;
|
|
213
253
|
if (this.retryAttempts >= this.maxRetries) {
|
|
214
|
-
console.error(
|
|
254
|
+
console.error(chalk_1.default.red(`Failed to connect after ${this.maxRetries} attempts`));
|
|
215
255
|
this.provideNetworkTroubleshootingGuidance(error);
|
|
216
256
|
this.isConnected = false;
|
|
217
257
|
return false;
|
|
218
258
|
}
|
|
219
259
|
// For network errors, retry with exponential backoff
|
|
220
260
|
const delay = await this.exponentialBackoff(this.retryAttempts);
|
|
221
|
-
console.log(
|
|
261
|
+
console.log(chalk_1.default.yellow(`Network error, retrying in ${delay}ms... (${this.retryAttempts}/${this.maxRetries})`));
|
|
222
262
|
const message = error?.message ?? String(error);
|
|
223
|
-
console.log(
|
|
263
|
+
console.log(chalk_1.default.gray(`Error: ${message}`));
|
|
224
264
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
225
265
|
return this.connectWithRetry(options);
|
|
226
266
|
}
|
|
@@ -243,61 +283,61 @@ export class MCPClient {
|
|
|
243
283
|
* Provide authentication-specific guidance
|
|
244
284
|
*/
|
|
245
285
|
provideAuthenticationGuidance(error) {
|
|
246
|
-
console.log(
|
|
286
|
+
console.log(chalk_1.default.yellow('\n🔐 Authentication Issue Detected:'));
|
|
247
287
|
const msg = error?.message ?? '';
|
|
248
288
|
if (msg.includes('AUTHENTICATION_REQUIRED')) {
|
|
249
|
-
console.log(
|
|
250
|
-
console.log(
|
|
289
|
+
console.log(chalk_1.default.cyan('• No credentials found. Run: lanonasis auth login'));
|
|
290
|
+
console.log(chalk_1.default.cyan('• Or set a vendor key: lanonasis auth login --vendor-key <your-key>'));
|
|
251
291
|
}
|
|
252
292
|
else if (msg.includes('AUTHENTICATION_INVALID')) {
|
|
253
|
-
console.log(
|
|
254
|
-
console.log(
|
|
293
|
+
console.log(chalk_1.default.cyan('• Invalid credentials. Confirm the vendor key matches your dashboard value'));
|
|
294
|
+
console.log(chalk_1.default.cyan('• Try: lanonasis auth logout && lanonasis auth login'));
|
|
255
295
|
}
|
|
256
296
|
else if (msg.includes('expired')) {
|
|
257
|
-
console.log(
|
|
258
|
-
console.log(
|
|
297
|
+
console.log(chalk_1.default.cyan('• Token expired. Re-authenticate: lanonasis auth login'));
|
|
298
|
+
console.log(chalk_1.default.cyan('• Or refresh: lanonasis auth refresh (if available)'));
|
|
259
299
|
}
|
|
260
300
|
else {
|
|
261
|
-
console.log(
|
|
262
|
-
console.log(
|
|
263
|
-
console.log(
|
|
301
|
+
console.log(chalk_1.default.cyan('• Check authentication status: lanonasis auth status'));
|
|
302
|
+
console.log(chalk_1.default.cyan('• Re-authenticate: lanonasis auth login'));
|
|
303
|
+
console.log(chalk_1.default.cyan('• Verify vendor key: lanonasis auth login --vendor-key <your-key>'));
|
|
264
304
|
}
|
|
265
305
|
}
|
|
266
306
|
/**
|
|
267
307
|
* Provide network troubleshooting guidance
|
|
268
308
|
*/
|
|
269
309
|
provideNetworkTroubleshootingGuidance(_error) {
|
|
270
|
-
console.log(
|
|
310
|
+
console.log(chalk_1.default.yellow('\n🌐 Network Issue Detected:'));
|
|
271
311
|
const msg = _error?.message ?? '';
|
|
272
312
|
if (msg.includes('ECONNREFUSED') || msg.includes('connect ECONNREFUSED')) {
|
|
273
|
-
console.log(
|
|
274
|
-
console.log(
|
|
275
|
-
console.log(
|
|
276
|
-
console.log(
|
|
313
|
+
console.log(chalk_1.default.cyan('• Connection refused. Service may be down:'));
|
|
314
|
+
console.log(chalk_1.default.cyan(' - For remote: Check https://mcp.lanonasis.com/health'));
|
|
315
|
+
console.log(chalk_1.default.cyan(' - For WebSocket: Check wss://mcp.lanonasis.com/ws'));
|
|
316
|
+
console.log(chalk_1.default.cyan(' - For local: Install local MCP server'));
|
|
277
317
|
}
|
|
278
318
|
else if (msg.includes('timeout') || msg.includes('ETIMEDOUT')) {
|
|
279
|
-
console.log(
|
|
280
|
-
console.log(
|
|
281
|
-
console.log(
|
|
282
|
-
console.log(
|
|
319
|
+
console.log(chalk_1.default.cyan('• Connection timeout. Check network:'));
|
|
320
|
+
console.log(chalk_1.default.cyan(' - Verify internet connectivity'));
|
|
321
|
+
console.log(chalk_1.default.cyan(' - Check firewall settings'));
|
|
322
|
+
console.log(chalk_1.default.cyan(' - Try different connection mode: --mode remote'));
|
|
283
323
|
}
|
|
284
324
|
else if (msg.includes('ENOTFOUND') || msg.includes('getaddrinfo')) {
|
|
285
|
-
console.log(
|
|
286
|
-
console.log(
|
|
287
|
-
console.log(
|
|
288
|
-
console.log(
|
|
325
|
+
console.log(chalk_1.default.cyan('• DNS resolution failed:'));
|
|
326
|
+
console.log(chalk_1.default.cyan(' - Check DNS settings'));
|
|
327
|
+
console.log(chalk_1.default.cyan(' - Verify server URL is correct'));
|
|
328
|
+
console.log(chalk_1.default.cyan(' - Try using IP address instead of hostname'));
|
|
289
329
|
}
|
|
290
330
|
else if (msg.includes('certificate') || msg.includes('SSL') || msg.includes('TLS')) {
|
|
291
|
-
console.log(
|
|
292
|
-
console.log(
|
|
293
|
-
console.log(
|
|
294
|
-
console.log(
|
|
331
|
+
console.log(chalk_1.default.cyan('• SSL/TLS certificate issue:'));
|
|
332
|
+
console.log(chalk_1.default.cyan(' - Check system time and date'));
|
|
333
|
+
console.log(chalk_1.default.cyan(' - Update CA certificates'));
|
|
334
|
+
console.log(chalk_1.default.cyan(' - Try different connection mode'));
|
|
295
335
|
}
|
|
296
336
|
else {
|
|
297
|
-
console.log(
|
|
298
|
-
console.log(
|
|
299
|
-
console.log(
|
|
300
|
-
console.log(
|
|
337
|
+
console.log(chalk_1.default.cyan('• General network error:'));
|
|
338
|
+
console.log(chalk_1.default.cyan(' - Check server status'));
|
|
339
|
+
console.log(chalk_1.default.cyan(' - Verify network connectivity'));
|
|
340
|
+
console.log(chalk_1.default.cyan(' - Try: lanonasis mcp diagnose (when available)'));
|
|
301
341
|
}
|
|
302
342
|
}
|
|
303
343
|
/**
|
|
@@ -352,7 +392,7 @@ export class MCPClient {
|
|
|
352
392
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
353
393
|
// Check if token is expired or expires within 5 minutes
|
|
354
394
|
if (payload.exp && payload.exp < currentTime + 300) {
|
|
355
|
-
console.log(
|
|
395
|
+
console.log(chalk_1.default.yellow('Token is expired or expiring soon, attempting refresh...'));
|
|
356
396
|
await this.refreshTokenIfNeeded();
|
|
357
397
|
}
|
|
358
398
|
}
|
|
@@ -380,7 +420,7 @@ export class MCPClient {
|
|
|
380
420
|
});
|
|
381
421
|
if (response.data.access_token) {
|
|
382
422
|
await this.config.setAndSave('token', response.data.access_token);
|
|
383
|
-
console.log(
|
|
423
|
+
console.log(chalk_1.default.green('✓ Token refreshed successfully'));
|
|
384
424
|
}
|
|
385
425
|
}
|
|
386
426
|
catch {
|
|
@@ -420,18 +460,18 @@ export class MCPClient {
|
|
|
420
460
|
const token = this.config.get('token');
|
|
421
461
|
if (token) {
|
|
422
462
|
// EventSource doesn't support headers directly, append token to URL
|
|
423
|
-
this.sseConnection = new EventSource(`${sseUrl}?token=${encodeURIComponent(token)}`);
|
|
463
|
+
this.sseConnection = new eventsource_1.EventSource(`${sseUrl}?token=${encodeURIComponent(token)}`);
|
|
424
464
|
this.sseConnection.onmessage = (event) => {
|
|
425
465
|
try {
|
|
426
466
|
const data = JSON.parse(event.data);
|
|
427
|
-
console.log(
|
|
467
|
+
console.log(chalk_1.default.blue('📡 Real-time update:'), data.type);
|
|
428
468
|
}
|
|
429
469
|
catch {
|
|
430
470
|
// Ignore parse errors
|
|
431
471
|
}
|
|
432
472
|
};
|
|
433
473
|
this.sseConnection.onerror = () => {
|
|
434
|
-
console.error(
|
|
474
|
+
console.error(chalk_1.default.yellow('⚠️ SSE connection error (will retry)'));
|
|
435
475
|
};
|
|
436
476
|
}
|
|
437
477
|
}
|
|
@@ -451,14 +491,14 @@ export class MCPClient {
|
|
|
451
491
|
this.wsConnection = null;
|
|
452
492
|
}
|
|
453
493
|
// Create new WebSocket connection with authentication
|
|
454
|
-
this.wsConnection = new
|
|
494
|
+
this.wsConnection = new ws_1.default(wsUrl, [], {
|
|
455
495
|
headers: {
|
|
456
496
|
'Authorization': `Bearer ${token}`,
|
|
457
497
|
'X-API-Key': token
|
|
458
498
|
}
|
|
459
499
|
});
|
|
460
500
|
this.wsConnection.on('open', () => {
|
|
461
|
-
console.log(
|
|
501
|
+
console.log(chalk_1.default.green('✅ Connected to MCP WebSocket server'));
|
|
462
502
|
// Send initialization message
|
|
463
503
|
this.sendWebSocketMessage({
|
|
464
504
|
id: 1,
|
|
@@ -479,22 +519,22 @@ export class MCPClient {
|
|
|
479
519
|
this.wsConnection.on('message', (data) => {
|
|
480
520
|
try {
|
|
481
521
|
const message = JSON.parse(data.toString());
|
|
482
|
-
console.log(
|
|
522
|
+
console.log(chalk_1.default.blue('📡 MCP message:'), message.id, message.method || 'response');
|
|
483
523
|
}
|
|
484
524
|
catch (error) {
|
|
485
525
|
console.error('Failed to parse WebSocket message:', error);
|
|
486
526
|
}
|
|
487
527
|
});
|
|
488
528
|
this.wsConnection.on('error', (error) => {
|
|
489
|
-
console.error(
|
|
529
|
+
console.error(chalk_1.default.red('WebSocket error:'), error);
|
|
490
530
|
reject(error);
|
|
491
531
|
});
|
|
492
532
|
this.wsConnection.on('close', (code, reason) => {
|
|
493
|
-
console.log(
|
|
533
|
+
console.log(chalk_1.default.yellow(`WebSocket connection closed (${code}): ${reason}`));
|
|
494
534
|
// Auto-reconnect after delay
|
|
495
535
|
setTimeout(() => {
|
|
496
536
|
if (this.isConnected) {
|
|
497
|
-
console.log(
|
|
537
|
+
console.log(chalk_1.default.blue('🔄 Attempting to reconnect to WebSocket...'));
|
|
498
538
|
this.initializeWebSocket(wsUrl).catch(err => {
|
|
499
539
|
console.error('Failed to reconnect:', err);
|
|
500
540
|
});
|
|
@@ -562,7 +602,7 @@ export class MCPClient {
|
|
|
562
602
|
}
|
|
563
603
|
catch {
|
|
564
604
|
const connectionMode = this.activeConnectionMode || 'remote';
|
|
565
|
-
console.log(
|
|
605
|
+
console.log(chalk_1.default.yellow(`⚠️ ${connectionMode} connection health check failed, attempting reconnection...`));
|
|
566
606
|
await this.handleHealthCheckFailure();
|
|
567
607
|
}
|
|
568
608
|
}
|
|
@@ -570,7 +610,7 @@ export class MCPClient {
|
|
|
570
610
|
* Check WebSocket connection health
|
|
571
611
|
*/
|
|
572
612
|
async checkWebSocketHealth() {
|
|
573
|
-
if (!this.wsConnection || this.wsConnection.readyState !==
|
|
613
|
+
if (!this.wsConnection || this.wsConnection.readyState !== ws_1.default.OPEN) {
|
|
574
614
|
throw new Error('WebSocket connection not open');
|
|
575
615
|
}
|
|
576
616
|
// Send a ping message to check connectivity
|
|
@@ -631,7 +671,7 @@ export class MCPClient {
|
|
|
631
671
|
const options = {
|
|
632
672
|
connectionMode
|
|
633
673
|
};
|
|
634
|
-
console.log(
|
|
674
|
+
console.log(chalk_1.default.yellow(`↻ Attempting reconnection using ${connectionMode} mode...`));
|
|
635
675
|
// Add specific URLs if available
|
|
636
676
|
if (connectionMode === 'websocket') {
|
|
637
677
|
options.serverUrl = this.config.get('mcpWebSocketUrl');
|
|
@@ -645,10 +685,10 @@ export class MCPClient {
|
|
|
645
685
|
// Attempt reconnection
|
|
646
686
|
const reconnected = await this.connect(options);
|
|
647
687
|
if (reconnected) {
|
|
648
|
-
console.log(
|
|
688
|
+
console.log(chalk_1.default.green('✓ Reconnected to MCP server'));
|
|
649
689
|
}
|
|
650
690
|
else {
|
|
651
|
-
console.log(
|
|
691
|
+
console.log(chalk_1.default.red('✗ Failed to reconnect to MCP server'));
|
|
652
692
|
}
|
|
653
693
|
}
|
|
654
694
|
/**
|
|
@@ -823,7 +863,21 @@ export class MCPClient {
|
|
|
823
863
|
* Get connection status details with health information
|
|
824
864
|
*/
|
|
825
865
|
getConnectionStatus() {
|
|
826
|
-
|
|
866
|
+
// When disconnected, show the configured preference instead of the stale activeConnectionMode
|
|
867
|
+
let connectionMode = this.activeConnectionMode;
|
|
868
|
+
if (!this.isConnected) {
|
|
869
|
+
// Check configured preference
|
|
870
|
+
const mcpPreference = this.config.get('mcpPreference');
|
|
871
|
+
const mcpConnectionMode = this.config.get('mcpConnectionMode');
|
|
872
|
+
const preferRemote = this.config.get('mcpUseRemote');
|
|
873
|
+
connectionMode = mcpConnectionMode
|
|
874
|
+
?? mcpPreference
|
|
875
|
+
?? (preferRemote ? 'remote' : 'websocket');
|
|
876
|
+
// If preference is 'auto', resolve to default (websocket)
|
|
877
|
+
if (connectionMode === 'auto') {
|
|
878
|
+
connectionMode = 'websocket';
|
|
879
|
+
}
|
|
880
|
+
}
|
|
827
881
|
let server;
|
|
828
882
|
switch (connectionMode) {
|
|
829
883
|
case 'websocket':
|
|
@@ -849,9 +903,10 @@ export class MCPClient {
|
|
|
849
903
|
};
|
|
850
904
|
}
|
|
851
905
|
}
|
|
906
|
+
exports.MCPClient = MCPClient;
|
|
852
907
|
// Singleton instance
|
|
853
908
|
let mcpClientInstance = null;
|
|
854
|
-
|
|
909
|
+
function getMCPClient() {
|
|
855
910
|
if (!mcpClientInstance) {
|
|
856
911
|
mcpClientInstance = new MCPClient();
|
|
857
912
|
}
|