@lanonasis/cli 3.9.7 → 3.9.9

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.
@@ -356,31 +356,69 @@ export class MCPClient {
356
356
  const jitter = cappedDelay * 0.25 * (Math.random() - 0.5);
357
357
  return Math.round(cappedDelay + jitter);
358
358
  }
359
+ async resolveAuthCredential() {
360
+ const authMethod = String(this.config.get('authMethod') || '').toLowerCase();
361
+ const token = this.config.get('token');
362
+ const vendorKey = await this.config.getVendorKeyAsync();
363
+ if (authMethod === 'vendor_key' && typeof vendorKey === 'string' && vendorKey.trim().length > 0) {
364
+ return { value: vendorKey.trim(), source: 'vendor_key' };
365
+ }
366
+ if ((authMethod === 'oauth' || authMethod === 'oauth2' || authMethod === 'jwt') &&
367
+ typeof token === 'string' &&
368
+ token.trim().length > 0) {
369
+ return { value: token.trim(), source: 'token' };
370
+ }
371
+ if (typeof token === 'string' && token.trim().length > 0) {
372
+ return { value: token.trim(), source: 'token' };
373
+ }
374
+ if (typeof vendorKey === 'string' && vendorKey.trim().length > 0) {
375
+ return { value: vendorKey.trim(), source: 'vendor_key' };
376
+ }
377
+ const envKey = process.env.LANONASIS_API_KEY;
378
+ if (typeof envKey === 'string' && envKey.trim().length > 0) {
379
+ return { value: envKey.trim(), source: 'env' };
380
+ }
381
+ return null;
382
+ }
383
+ buildAuthHeaders(auth) {
384
+ const headers = {};
385
+ const value = auth.value.trim();
386
+ if (!value) {
387
+ return headers;
388
+ }
389
+ if (auth.source === 'vendor_key' || value.startsWith('lano_') || value.startsWith('lms_')) {
390
+ headers['X-API-Key'] = value;
391
+ headers['X-Auth-Method'] = 'vendor_key';
392
+ headers['X-Project-Scope'] = 'lanonasis-maas';
393
+ return headers;
394
+ }
395
+ if (value.toLowerCase().startsWith('bearer ')) {
396
+ headers['Authorization'] = value;
397
+ headers['X-Auth-Method'] = 'jwt';
398
+ headers['X-Project-Scope'] = 'lanonasis-maas';
399
+ return headers;
400
+ }
401
+ headers['Authorization'] = `Bearer ${value}`;
402
+ headers['X-Auth-Method'] = 'jwt';
403
+ headers['X-Project-Scope'] = 'lanonasis-maas';
404
+ return headers;
405
+ }
359
406
  /**
360
407
  * Validate authentication credentials before attempting MCP connection
361
408
  */
362
409
  async validateAuthBeforeConnect() {
363
- const token = this.config.get('token');
364
- const vendorKey = await this.config.getVendorKeyAsync();
365
- // Check if we have any authentication credentials
366
- if (!token && !vendorKey) {
410
+ const auth = await this.resolveAuthCredential();
411
+ if (!auth) {
367
412
  throw new Error('AUTHENTICATION_REQUIRED: No authentication credentials found. Run "lanonasis auth login" first.');
368
413
  }
369
- // If we have a token, check if it's expired or needs refresh
370
- if (token) {
371
- try {
372
- await this.validateAndRefreshToken(token);
373
- }
374
- catch (error) {
375
- throw new Error(`AUTHENTICATION_INVALID: ${error instanceof Error ? error.message : 'Token validation failed'}`);
376
- }
377
- }
378
- // If we have a vendor key, ensure it is valid (non-empty)
379
- if (vendorKey && !token) {
380
- const validationResult = this.config.validateVendorKeyFormat(vendorKey);
381
- if (validationResult !== true) {
382
- throw new Error(`AUTHENTICATION_INVALID: ${typeof validationResult === 'string' ? validationResult : 'Vendor key is invalid'}`);
414
+ await this.config.refreshTokenIfNeeded();
415
+ const verification = await this.config.verifyCurrentCredentialsWithServer();
416
+ if (!verification.valid) {
417
+ const reason = verification.reason || 'Credential verification failed';
418
+ if (verification.method === 'none') {
419
+ throw new Error(`AUTHENTICATION_REQUIRED: ${reason}`);
383
420
  }
421
+ throw new Error(`AUTHENTICATION_INVALID: ${reason}`);
384
422
  }
385
423
  }
386
424
  /**
@@ -460,12 +498,10 @@ export class MCPClient {
460
498
  async initializeSSE(serverUrl) {
461
499
  // Use the proper SSE endpoint from config
462
500
  const sseUrl = this.config.getMCPSSEUrl() ?? `${serverUrl}/events`;
463
- const token = this.config.get('token');
464
- const vendorKey = await this.config.getVendorKeyAsync();
465
- const authKey = token || vendorKey || process.env.LANONASIS_API_KEY;
466
- if (authKey) {
501
+ const auth = await this.resolveAuthCredential();
502
+ if (auth) {
467
503
  // EventSource doesn't support headers directly, append token to URL
468
- this.sseConnection = new EventSource(`${sseUrl}?token=${encodeURIComponent(authKey)}`);
504
+ this.sseConnection = new EventSource(`${sseUrl}?token=${encodeURIComponent(auth.value)}`);
469
505
  this.sseConnection.onmessage = (event) => {
470
506
  try {
471
507
  const data = JSON.parse(event.data);
@@ -479,7 +515,11 @@ export class MCPClient {
479
515
  }
480
516
  };
481
517
  this.sseConnection.onerror = () => {
482
- console.error(chalk.yellow('⚠️ SSE connection error (will retry)'));
518
+ if (process.env.CLI_VERBOSE === 'true') {
519
+ console.error(chalk.yellow('⚠️ SSE connection error (stream disabled for this session)'));
520
+ }
521
+ this.sseConnection?.close();
522
+ this.sseConnection = null;
483
523
  };
484
524
  }
485
525
  }
@@ -487,12 +527,11 @@ export class MCPClient {
487
527
  * Initialize WebSocket connection for enterprise MCP server
488
528
  */
489
529
  async initializeWebSocket(wsUrl) {
490
- const token = this.config.get('token');
491
- const vendorKey = await this.config.getVendorKeyAsync();
492
- const authKey = token || vendorKey || process.env.LANONASIS_API_KEY;
493
- if (!authKey) {
530
+ const auth = await this.resolveAuthCredential();
531
+ if (!auth) {
494
532
  throw new Error('API key required for WebSocket mode. Set LANONASIS_API_KEY or login first.');
495
533
  }
534
+ const wsHeaders = this.buildAuthHeaders(auth);
496
535
  return new Promise((resolve, reject) => {
497
536
  try {
498
537
  // Close existing connection if any
@@ -502,10 +541,7 @@ export class MCPClient {
502
541
  }
503
542
  // Create new WebSocket connection with authentication
504
543
  this.wsConnection = new WebSocket(wsUrl, [], {
505
- headers: {
506
- 'Authorization': `Bearer ${authKey}`,
507
- 'X-API-Key': authKey
508
- }
544
+ headers: wsHeaders
509
545
  });
510
546
  this.wsConnection.on('open', () => {
511
547
  console.log(chalk.green('✅ Connected to MCP WebSocket server'));
@@ -653,19 +689,14 @@ export class MCPClient {
653
689
  */
654
690
  async checkRemoteHealth(serverUrl) {
655
691
  const apiUrl = serverUrl ?? this.config.getMCPRestUrl() ?? 'https://mcp.lanonasis.com/api/v1';
656
- const token = this.config.get('token');
657
- const vendorKey = await this.config.getVendorKeyAsync();
658
- const authKey = token || vendorKey || process.env.LANONASIS_API_KEY;
659
- if (!authKey) {
692
+ const auth = await this.resolveAuthCredential();
693
+ if (!auth) {
660
694
  throw new Error('No authentication token available');
661
695
  }
662
696
  try {
663
697
  const axios = (await import('axios')).default;
664
698
  await axios.get(`${apiUrl}/health`, {
665
- headers: {
666
- 'Authorization': `Bearer ${authKey}`,
667
- 'X-API-Key': authKey
668
- },
699
+ headers: this.buildAuthHeaders(auth),
669
700
  timeout: 5000
670
701
  });
671
702
  }
@@ -786,10 +817,8 @@ export class MCPClient {
786
817
  */
787
818
  async callRemoteTool(toolName, args) {
788
819
  const apiUrl = this.config.getMCPRestUrl() ?? 'https://mcp.lanonasis.com/api/v1';
789
- const token = this.config.get('token');
790
- const vendorKey = await this.config.getVendorKeyAsync();
791
- const authKey = token || vendorKey || process.env.LANONASIS_API_KEY;
792
- if (!authKey) {
820
+ const auth = await this.resolveAuthCredential();
821
+ if (!auth) {
793
822
  throw new Error('Authentication required. Run "lanonasis auth login" first.');
794
823
  }
795
824
  // Map MCP tool names to REST API endpoints
@@ -845,8 +874,7 @@ export class MCPClient {
845
874
  method: mapping.method,
846
875
  url: `${apiUrl}${endpoint}`,
847
876
  headers: {
848
- 'Authorization': `Bearer ${authKey}`,
849
- 'X-API-Key': authKey,
877
+ ...this.buildAuthHeaders(auth),
850
878
  'Content-Type': 'application/json'
851
879
  },
852
880
  data: mapping.transform ? mapping.transform(args) : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lanonasis/cli",
3
- "version": "3.9.7",
3
+ "version": "3.9.9",
4
4
  "description": "Professional CLI for LanOnasis Memory as a Service (MaaS) with MCP support, seamless inline editing, and enterprise-grade security",
5
5
  "keywords": [
6
6
  "lanonasis",
@@ -52,6 +52,7 @@
52
52
  "CHANGELOG.md"
53
53
  ],
54
54
  "dependencies": {
55
+ "@lanonasis/mem-intel-sdk": "2.0.3",
55
56
  "@lanonasis/oauth-client": "2.0.0",
56
57
  "@lanonasis/security-sdk": "1.0.5",
57
58
  "@modelcontextprotocol/sdk": "^1.26.0",
@@ -85,7 +86,7 @@
85
86
  "typescript": "^5.9.3"
86
87
  },
87
88
  "scripts": {
88
- "build": "rimraf dist && tsc -p tsconfig.json",
89
+ "build": "rimraf dist && tsc -p tsconfig.json && chmod +x dist/index.js dist/mcp-server-entry.js 2>/dev/null || true",
89
90
  "prepublishOnly": "npm run build",
90
91
  "postinstall": "node scripts/postinstall.js",
91
92
  "test": "[ -f node_modules/jest/bin/jest.js ] || bun install --no-save; node --experimental-vm-modules node_modules/jest/bin/jest.js",