@lanonasis/cli 3.4.15 → 3.5.15
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/auth.js +8 -2
- package/dist/core/welcome.js +0 -1
- package/dist/mcp/client/enhanced-client.js +1 -2
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +40 -6
- package/dist/utils/config.d.ts +10 -1
- package/dist/utils/config.js +64 -12
- package/dist/utils/mcp-client.d.ts +17 -0
- package/dist/utils/mcp-client.js +32 -5
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -231,8 +231,11 @@ export async function diagnoseCommand() {
|
|
|
231
231
|
console.log(chalk.green(' ✓ Token is not expired'));
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
-
catch {
|
|
234
|
+
catch (error) {
|
|
235
235
|
console.log(chalk.yellow(' ⚠ Could not validate token expiry'));
|
|
236
|
+
if (process.env.CLI_VERBOSE === 'true' && error instanceof Error) {
|
|
237
|
+
console.log(chalk.gray(` ${error.message}`));
|
|
238
|
+
}
|
|
236
239
|
}
|
|
237
240
|
}
|
|
238
241
|
else {
|
|
@@ -315,8 +318,11 @@ export async function diagnoseCommand() {
|
|
|
315
318
|
diagnostics.deviceId = deviceId;
|
|
316
319
|
console.log(chalk.green(' ✓ Device ID:'), chalk.gray(deviceId));
|
|
317
320
|
}
|
|
318
|
-
catch {
|
|
321
|
+
catch (error) {
|
|
319
322
|
console.log(chalk.yellow(' ⚠ Could not get device ID'));
|
|
323
|
+
if (process.env.CLI_VERBOSE === 'true' && error instanceof Error) {
|
|
324
|
+
console.log(chalk.gray(` ${error.message}`));
|
|
325
|
+
}
|
|
320
326
|
}
|
|
321
327
|
// Summary and recommendations
|
|
322
328
|
console.log(chalk.blue.bold('\n📋 Diagnostic Summary'));
|
package/dist/core/welcome.js
CHANGED
|
@@ -59,7 +59,7 @@ export class EnhancedMCPClient extends EventEmitter {
|
|
|
59
59
|
const maxRetries = config.maxRetries || 3;
|
|
60
60
|
const timeout = config.timeout || 30000;
|
|
61
61
|
let attempts = 0;
|
|
62
|
-
while (
|
|
62
|
+
while (true) {
|
|
63
63
|
try {
|
|
64
64
|
this.updateConnectionStatus(config.name, 'connecting');
|
|
65
65
|
const client = await this.createClientWithTimeout(config, timeout);
|
|
@@ -82,7 +82,6 @@ export class EnhancedMCPClient extends EventEmitter {
|
|
|
82
82
|
await this.delay(delay);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
return false;
|
|
86
85
|
}
|
|
87
86
|
/**
|
|
88
87
|
* Create client with timeout
|
package/dist/mcp-server.d.ts
CHANGED
package/dist/mcp-server.js
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { dirname, join } from 'path';
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import { createRequire } from 'module';
|
|
9
11
|
import { spawn } from 'child_process';
|
|
10
12
|
import { CLIConfig } from './utils/config.js';
|
|
11
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
14
|
const __dirname = dirname(__filename);
|
|
15
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
13
16
|
export class CLIMCPServer {
|
|
14
17
|
config;
|
|
15
18
|
constructor() {
|
|
@@ -28,6 +31,33 @@ export class CLIMCPServer {
|
|
|
28
31
|
await this.startLocalMCP(options);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
34
|
+
resolveMCPServerPath() {
|
|
35
|
+
const candidates = new Set();
|
|
36
|
+
if (process.env.MCP_SERVER_PATH) {
|
|
37
|
+
candidates.add(process.env.MCP_SERVER_PATH);
|
|
38
|
+
}
|
|
39
|
+
const packageRequests = [
|
|
40
|
+
'@lanonasis/mcp-server/dist/cli-aligned-mcp-server.js',
|
|
41
|
+
'lanonasis-mcp-server/dist/cli-aligned-mcp-server.js'
|
|
42
|
+
];
|
|
43
|
+
for (const request of packageRequests) {
|
|
44
|
+
try {
|
|
45
|
+
const resolved = nodeRequire.resolve(request);
|
|
46
|
+
candidates.add(resolved);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore resolution failures and continue through fallbacks
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
candidates.add(join(process.cwd(), 'mcp-server/dist/cli-aligned-mcp-server.js'));
|
|
53
|
+
candidates.add(join(__dirname, '../../../mcp-server/dist/cli-aligned-mcp-server.js'));
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
if (candidate && existsSync(candidate)) {
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw new Error('Unable to locate the CLI-aligned MCP server. Set MCP_SERVER_PATH or install @lanonasis/mcp-server.');
|
|
60
|
+
}
|
|
31
61
|
/**
|
|
32
62
|
* Start local MCP server using CLI auth config
|
|
33
63
|
*/
|
|
@@ -42,10 +72,11 @@ export class CLIMCPServer {
|
|
|
42
72
|
console.error(`Config: ~/.maas/config.json`);
|
|
43
73
|
console.error(`Auth: ${this.config.hasVendorKey() ? 'Vendor Key' : 'JWT Token'}`);
|
|
44
74
|
}
|
|
75
|
+
const resolvedPort = typeof port === 'number' && !Number.isNaN(port) ? port : 3001;
|
|
45
76
|
// Set environment variables from CLI config
|
|
46
77
|
const env = {
|
|
47
78
|
...process.env,
|
|
48
|
-
PORT:
|
|
79
|
+
PORT: resolvedPort.toString(),
|
|
49
80
|
MEMORY_API_URL: this.config.getApiUrl(),
|
|
50
81
|
LANONASIS_VENDOR_KEY: this.config.getVendorKey(),
|
|
51
82
|
LANONASIS_TOKEN: this.config.getToken(),
|
|
@@ -84,14 +115,13 @@ export class CLIMCPServer {
|
|
|
84
115
|
*/
|
|
85
116
|
async startRemoteMCP(options) {
|
|
86
117
|
const { verbose } = options;
|
|
118
|
+
const message = 'Remote MCP not implemented; remove --remote or use local mode.';
|
|
87
119
|
if (verbose) {
|
|
88
120
|
console.error('🌐 Connecting to remote MCP server...');
|
|
89
121
|
console.error(`URL: ${this.config.getMCPServerUrl()}`);
|
|
90
122
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.error('⚠️ Remote MCP not yet implemented, falling back to local mode');
|
|
94
|
-
await this.startLocalMCP({ ...options, useRemote: false });
|
|
123
|
+
console.error(`❌ ${message}`);
|
|
124
|
+
throw new Error(message);
|
|
95
125
|
}
|
|
96
126
|
/**
|
|
97
127
|
* Check if MCP server is available and configured
|
|
@@ -149,6 +179,10 @@ Examples:
|
|
|
149
179
|
await server.start(options);
|
|
150
180
|
}
|
|
151
181
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
152
|
-
main().catch(
|
|
182
|
+
main().catch(error => {
|
|
183
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
184
|
+
console.error(message);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
153
187
|
}
|
|
154
188
|
export default CLIMCPServer;
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ export declare class CLIConfig {
|
|
|
43
43
|
private authCheckCache;
|
|
44
44
|
private readonly AUTH_CACHE_TTL;
|
|
45
45
|
constructor();
|
|
46
|
+
/**
|
|
47
|
+
* Overrides the configuration storage directory. Primarily used for tests.
|
|
48
|
+
*/
|
|
49
|
+
setConfigDirectory(configDir: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Exposes the current config path for tests and diagnostics.
|
|
52
|
+
*/
|
|
53
|
+
getConfigPath(): string;
|
|
46
54
|
init(): Promise<void>;
|
|
47
55
|
load(): Promise<void>;
|
|
48
56
|
private migrateConfigIfNeeded;
|
|
@@ -55,6 +63,8 @@ export declare class CLIConfig {
|
|
|
55
63
|
discoverServices(verbose?: boolean): Promise<void>;
|
|
56
64
|
private handleServiceDiscoveryFailure;
|
|
57
65
|
private categorizeServiceDiscoveryError;
|
|
66
|
+
private resolveFallbackEndpoints;
|
|
67
|
+
private logFallbackUsage;
|
|
58
68
|
setManualEndpoints(endpoints: Partial<CLIConfigData['discoveredServices']>): Promise<void>;
|
|
59
69
|
hasManualEndpointOverrides(): boolean;
|
|
60
70
|
clearManualEndpointOverrides(): Promise<void>;
|
|
@@ -71,7 +81,6 @@ export declare class CLIConfig {
|
|
|
71
81
|
isAuthenticated(): Promise<boolean>;
|
|
72
82
|
logout(): Promise<void>;
|
|
73
83
|
clear(): Promise<void>;
|
|
74
|
-
getConfigPath(): string;
|
|
75
84
|
exists(): Promise<boolean>;
|
|
76
85
|
validateStoredCredentials(): Promise<boolean>;
|
|
77
86
|
refreshTokenIfNeeded(): Promise<void>;
|
package/dist/utils/config.js
CHANGED
|
@@ -16,6 +16,20 @@ export class CLIConfig {
|
|
|
16
16
|
this.configPath = path.join(this.configDir, 'config.json');
|
|
17
17
|
this.lockFile = path.join(this.configDir, 'config.lock');
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the configuration storage directory. Primarily used for tests.
|
|
21
|
+
*/
|
|
22
|
+
setConfigDirectory(configDir) {
|
|
23
|
+
this.configDir = configDir;
|
|
24
|
+
this.configPath = path.join(configDir, 'config.json');
|
|
25
|
+
this.lockFile = path.join(configDir, 'config.lock');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Exposes the current config path for tests and diagnostics.
|
|
29
|
+
*/
|
|
30
|
+
getConfigPath() {
|
|
31
|
+
return this.configPath;
|
|
32
|
+
}
|
|
19
33
|
async init() {
|
|
20
34
|
try {
|
|
21
35
|
await fs.mkdir(this.configDir, { recursive: true });
|
|
@@ -229,20 +243,17 @@ export class CLIConfig {
|
|
|
229
243
|
return;
|
|
230
244
|
}
|
|
231
245
|
}
|
|
232
|
-
|
|
246
|
+
const fallback = this.resolveFallbackEndpoints();
|
|
233
247
|
this.config.discoveredServices = {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
mcp_base: 'https://mcp.lanonasis.com/api/v1', // MCP HTTP/REST
|
|
237
|
-
mcp_ws_base: 'wss://mcp.lanonasis.com/ws', // MCP WebSocket
|
|
238
|
-
mcp_sse_base: 'https://mcp.lanonasis.com/api/v1/events', // MCP SSE
|
|
239
|
-
project_scope: 'lanonasis-maas' // Correct project scope
|
|
248
|
+
...fallback.endpoints,
|
|
249
|
+
project_scope: 'lanonasis-maas'
|
|
240
250
|
};
|
|
241
251
|
// Mark as fallback (don't set lastServiceDiscovery)
|
|
242
252
|
await this.save();
|
|
253
|
+
this.logFallbackUsage(fallback.source, this.config.discoveredServices);
|
|
243
254
|
if (verbose) {
|
|
244
255
|
console.log('✓ Using fallback service endpoints');
|
|
245
|
-
console.log(
|
|
256
|
+
console.log(` Source: ${fallback.source === 'environment' ? 'environment overrides' : 'built-in defaults'}`);
|
|
246
257
|
}
|
|
247
258
|
}
|
|
248
259
|
categorizeServiceDiscoveryError(error) {
|
|
@@ -272,6 +283,47 @@ export class CLIConfig {
|
|
|
272
283
|
}
|
|
273
284
|
return 'unknown';
|
|
274
285
|
}
|
|
286
|
+
resolveFallbackEndpoints() {
|
|
287
|
+
const envAuthBase = process.env.LANONASIS_FALLBACK_AUTH_BASE ?? process.env.AUTH_BASE;
|
|
288
|
+
const envMemoryBase = process.env.LANONASIS_FALLBACK_MEMORY_BASE ?? process.env.MEMORY_BASE;
|
|
289
|
+
const envMcpBase = process.env.LANONASIS_FALLBACK_MCP_BASE ?? process.env.MCP_BASE;
|
|
290
|
+
const envMcpWsBase = process.env.LANONASIS_FALLBACK_MCP_WS_BASE ?? process.env.MCP_WS_BASE;
|
|
291
|
+
const envMcpSseBase = process.env.LANONASIS_FALLBACK_MCP_SSE_BASE ?? process.env.MCP_SSE_BASE;
|
|
292
|
+
const hasEnvOverrides = Boolean(envAuthBase || envMemoryBase || envMcpBase || envMcpWsBase || envMcpSseBase);
|
|
293
|
+
const nodeEnv = (process.env.NODE_ENV ?? '').toLowerCase();
|
|
294
|
+
const isDevEnvironment = nodeEnv === 'development' || nodeEnv === 'test';
|
|
295
|
+
const defaultAuthBase = isDevEnvironment ? 'http://localhost:4000' : 'https://api.lanonasis.com';
|
|
296
|
+
const defaultMemoryBase = isDevEnvironment ? 'http://localhost:4000/api/v1' : 'https://api.lanonasis.com/api/v1';
|
|
297
|
+
const defaultMcpBase = isDevEnvironment ? 'http://localhost:4100/api/v1' : 'https://mcp.lanonasis.com/api/v1';
|
|
298
|
+
const defaultMcpWsBase = isDevEnvironment ? 'ws://localhost:4100/ws' : 'wss://mcp.lanonasis.com/ws';
|
|
299
|
+
const defaultMcpSseBase = isDevEnvironment ? 'http://localhost:4100/api/v1/events' : 'https://mcp.lanonasis.com/api/v1/events';
|
|
300
|
+
const endpoints = {
|
|
301
|
+
auth_base: envAuthBase ?? defaultAuthBase,
|
|
302
|
+
memory_base: envMemoryBase ?? defaultMemoryBase,
|
|
303
|
+
mcp_base: envMcpBase ?? defaultMcpBase,
|
|
304
|
+
mcp_ws_base: envMcpWsBase ?? defaultMcpWsBase,
|
|
305
|
+
mcp_sse_base: envMcpSseBase ?? defaultMcpSseBase
|
|
306
|
+
};
|
|
307
|
+
return {
|
|
308
|
+
endpoints,
|
|
309
|
+
source: hasEnvOverrides ? 'environment' : 'default'
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
logFallbackUsage(source, endpoints) {
|
|
313
|
+
const summary = {
|
|
314
|
+
auth: endpoints.auth_base,
|
|
315
|
+
mcp: endpoints.mcp_base,
|
|
316
|
+
websocket: endpoints.mcp_ws_base,
|
|
317
|
+
sse: endpoints.mcp_sse_base,
|
|
318
|
+
source
|
|
319
|
+
};
|
|
320
|
+
const message = `Service discovery fallback activated using ${source === 'environment' ? 'environment overrides' : 'built-in defaults'}`;
|
|
321
|
+
console.warn(`⚠️ ${message}`);
|
|
322
|
+
console.info('📊 service_discovery_fallback', summary);
|
|
323
|
+
if (typeof process.emitWarning === 'function') {
|
|
324
|
+
process.emitWarning(message, 'ServiceDiscoveryFallback');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
275
327
|
// Manual endpoint override functionality
|
|
276
328
|
async setManualEndpoints(endpoints) {
|
|
277
329
|
if (!this.config.discoveredServices) {
|
|
@@ -570,9 +622,6 @@ export class CLIConfig {
|
|
|
570
622
|
this.config = {};
|
|
571
623
|
await this.save();
|
|
572
624
|
}
|
|
573
|
-
getConfigPath() {
|
|
574
|
-
return this.configPath;
|
|
575
|
-
}
|
|
576
625
|
async exists() {
|
|
577
626
|
try {
|
|
578
627
|
await fs.access(this.configPath);
|
|
@@ -656,9 +705,12 @@ export class CLIConfig {
|
|
|
656
705
|
}
|
|
657
706
|
}
|
|
658
707
|
}
|
|
659
|
-
catch {
|
|
708
|
+
catch (err) {
|
|
660
709
|
// If refresh fails, mark credentials as potentially invalid
|
|
661
710
|
await this.incrementFailureCount();
|
|
711
|
+
if (process.env.CLI_VERBOSE === 'true' || process.env.NODE_ENV !== 'production') {
|
|
712
|
+
console.debug('Token refresh failed:', err.message);
|
|
713
|
+
}
|
|
662
714
|
}
|
|
663
715
|
}
|
|
664
716
|
async clearInvalidCredentials() {
|
|
@@ -57,6 +57,23 @@ export declare class MCPClient {
|
|
|
57
57
|
private lastHealthCheck;
|
|
58
58
|
private activeConnectionMode;
|
|
59
59
|
constructor();
|
|
60
|
+
/**
|
|
61
|
+
* Overrides the configuration directory used by the underlying CLI config.
|
|
62
|
+
* Useful for tests that need isolated config state.
|
|
63
|
+
*/
|
|
64
|
+
setConfigDirectory(configDir: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Returns the current config file path. Primarily used for test introspection.
|
|
67
|
+
*/
|
|
68
|
+
getConfigPath(): string;
|
|
69
|
+
/**
|
|
70
|
+
* Helper for tests to seed authentication tokens without accessing internals.
|
|
71
|
+
*/
|
|
72
|
+
setTokenForTesting(token: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Helper for tests to seed vendor keys without accessing internals.
|
|
75
|
+
*/
|
|
76
|
+
setVendorKeyForTesting(vendorKey: string): Promise<void>;
|
|
60
77
|
/**
|
|
61
78
|
* Initialize the MCP client configuration
|
|
62
79
|
*/
|
package/dist/utils/mcp-client.js
CHANGED
|
@@ -20,6 +20,31 @@ export class MCPClient {
|
|
|
20
20
|
constructor() {
|
|
21
21
|
this.config = new CLIConfig();
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Overrides the configuration directory used by the underlying CLI config.
|
|
25
|
+
* Useful for tests that need isolated config state.
|
|
26
|
+
*/
|
|
27
|
+
setConfigDirectory(configDir) {
|
|
28
|
+
this.config.setConfigDirectory(configDir);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns the current config file path. Primarily used for test introspection.
|
|
32
|
+
*/
|
|
33
|
+
getConfigPath() {
|
|
34
|
+
return this.config.getConfigPath();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Helper for tests to seed authentication tokens without accessing internals.
|
|
38
|
+
*/
|
|
39
|
+
async setTokenForTesting(token) {
|
|
40
|
+
await this.config.setToken(token);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Helper for tests to seed vendor keys without accessing internals.
|
|
44
|
+
*/
|
|
45
|
+
async setVendorKeyForTesting(vendorKey) {
|
|
46
|
+
await this.config.setVendorKey(vendorKey);
|
|
47
|
+
}
|
|
23
48
|
/**
|
|
24
49
|
* Initialize the MCP client configuration
|
|
25
50
|
*/
|
|
@@ -528,7 +553,7 @@ export class MCPClient {
|
|
|
528
553
|
}
|
|
529
554
|
try {
|
|
530
555
|
this.lastHealthCheck = new Date();
|
|
531
|
-
const connectionMode = this.
|
|
556
|
+
const connectionMode = this.activeConnectionMode || 'remote';
|
|
532
557
|
switch (connectionMode) {
|
|
533
558
|
case 'websocket':
|
|
534
559
|
await this.checkWebSocketHealth();
|
|
@@ -542,7 +567,8 @@ export class MCPClient {
|
|
|
542
567
|
}
|
|
543
568
|
}
|
|
544
569
|
catch {
|
|
545
|
-
|
|
570
|
+
const connectionMode = this.activeConnectionMode || 'remote';
|
|
571
|
+
console.log(chalk.yellow(`⚠️ ${connectionMode} connection health check failed, attempting reconnection...`));
|
|
546
572
|
await this.handleHealthCheckFailure();
|
|
547
573
|
}
|
|
548
574
|
}
|
|
@@ -607,10 +633,11 @@ export class MCPClient {
|
|
|
607
633
|
this.isConnected = false;
|
|
608
634
|
this.stopHealthMonitoring();
|
|
609
635
|
// Attempt to reconnect with current configuration
|
|
610
|
-
const connectionMode = this.
|
|
636
|
+
const connectionMode = (this.activeConnectionMode || 'remote');
|
|
611
637
|
const options = {
|
|
612
|
-
connectionMode
|
|
638
|
+
connectionMode
|
|
613
639
|
};
|
|
640
|
+
console.log(chalk.yellow(`↻ Attempting reconnection using ${connectionMode} mode...`));
|
|
614
641
|
// Add specific URLs if available
|
|
615
642
|
if (connectionMode === 'websocket') {
|
|
616
643
|
options.serverUrl = this.config.get('mcpWebSocketUrl');
|
|
@@ -648,7 +675,7 @@ export class MCPClient {
|
|
|
648
675
|
this.wsConnection = null;
|
|
649
676
|
}
|
|
650
677
|
this.isConnected = false;
|
|
651
|
-
this.activeConnectionMode = '
|
|
678
|
+
this.activeConnectionMode = 'websocket'; // Reset to default
|
|
652
679
|
}
|
|
653
680
|
/**
|
|
654
681
|
* Call an MCP tool
|
package/package.json
CHANGED