@lanonasis/cli 1.5.0 → 1.5.1

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.
@@ -2,27 +2,70 @@ interface MCPConnectionOptions {
2
2
  serverPath?: string;
3
3
  serverUrl?: string;
4
4
  useRemote?: boolean;
5
- mode?: 'local' | 'remote' | 'websocket';
5
+ useWebSocket?: boolean;
6
+ connectionMode?: 'local' | 'remote' | 'websocket';
7
+ }
8
+ /**
9
+ * Interface for MCP tool arguments
10
+ */
11
+ interface MCPToolArgs {
12
+ [key: string]: unknown;
13
+ }
14
+ /**
15
+ * Interface for MCP tool response
16
+ */
17
+ export interface MCPToolResponse {
18
+ result?: unknown;
19
+ error?: {
20
+ code: number;
21
+ message: string;
22
+ };
23
+ id?: string;
24
+ title?: string;
25
+ memory_type?: string;
26
+ length?: number;
27
+ forEach?: (callback: (item: any, index: number) => void) => void;
28
+ code?: number;
29
+ message?: string;
30
+ response?: any;
31
+ }
32
+ /**
33
+ * Interface for MCP WebSocket messages
34
+ */
35
+ export interface MCPWebSocketMessage {
36
+ id: number;
37
+ method?: string;
38
+ params?: Record<string, unknown>;
39
+ result?: Record<string, unknown>;
40
+ error?: {
41
+ code: number;
42
+ message: string;
43
+ data?: unknown;
44
+ };
6
45
  }
7
46
  export declare class MCPClient {
8
47
  private client;
9
48
  private config;
10
49
  private isConnected;
11
50
  private sseConnection;
12
- private wsClient;
51
+ private wsConnection;
13
52
  constructor();
14
53
  /**
15
54
  * Connect to MCP server (local or remote)
16
55
  */
17
56
  connect(options?: MCPConnectionOptions): Promise<boolean>;
18
57
  /**
19
- * Initialize WebSocket MCP connection for enterprise users
58
+ * Initialize SSE connection for real-time updates
59
+ */
60
+ private initializeSSE;
61
+ /**
62
+ * Initialize WebSocket connection for enterprise MCP server
20
63
  */
21
64
  private initializeWebSocket;
22
65
  /**
23
- * Initialize SSE connection for real-time updates
66
+ * Send a message over the WebSocket connection
24
67
  */
25
- private initializeSSE;
68
+ private sendWebSocketMessage;
26
69
  /**
27
70
  * Disconnect from MCP server
28
71
  */
@@ -30,7 +73,7 @@ export declare class MCPClient {
30
73
  /**
31
74
  * Call an MCP tool
32
75
  */
33
- callTool(toolName: string, args: any): Promise<any>;
76
+ callTool(toolName: string, args: MCPToolArgs): Promise<MCPToolResponse>;
34
77
  /**
35
78
  * Call remote tool via REST API with MCP interface
36
79
  */
@@ -5,7 +5,7 @@ import { CLIConfig } from './config.js';
5
5
  import * as path from 'path';
6
6
  import { EventSource } from 'eventsource';
7
7
  import { fileURLToPath } from 'url';
8
- import { output } from './output.js';
8
+ import WebSocket from 'ws';
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
11
  export class MCPClient {
@@ -13,7 +13,7 @@ export class MCPClient {
13
13
  config;
14
14
  isConnected = false;
15
15
  sseConnection = null;
16
- wsClient = null;
16
+ wsConnection = null;
17
17
  constructor() {
18
18
  this.config = new CLIConfig();
19
19
  }
@@ -22,81 +22,67 @@ export class MCPClient {
22
22
  */
23
23
  async connect(options = {}) {
24
24
  try {
25
- // Support new mode parameter or fallback to legacy useRemote
26
- const mode = options.mode ?? (options.useRemote ? 'remote' : 'local');
27
- const useRemote = mode !== 'local';
28
- if (useRemote) {
29
- if (mode === 'websocket') {
30
- // WebSocket MCP connection for enterprise users
31
- const wsUrl = options.serverUrl ?? 'wss://mcp.lanonasis.com/mcp';
32
- if (!output.isSilent()) {
33
- output.log(chalk.cyan(`Connecting to WebSocket MCP server at ${wsUrl}...`));
34
- }
35
- // Initialize WebSocket MCP connection
25
+ // Determine connection mode with priority to explicit mode option
26
+ const connectionMode = options.connectionMode ??
27
+ (options.useWebSocket ? 'websocket' :
28
+ options.useRemote ? 'remote' :
29
+ this.config.get('mcpConnectionMode') ??
30
+ this.config.get('mcpUseRemote') ? 'remote' : 'local');
31
+ let wsUrl;
32
+ let serverUrl;
33
+ let serverPath;
34
+ switch (connectionMode) {
35
+ case 'websocket':
36
+ // WebSocket connection mode for enterprise users
37
+ wsUrl = options.serverUrl ??
38
+ this.config.get('mcpWebSocketUrl') ??
39
+ 'ws://localhost:8081/mcp/ws';
40
+ console.log(chalk.cyan(`Connecting to WebSocket MCP server at ${wsUrl}...`));
41
+ // Initialize WebSocket connection
36
42
  await this.initializeWebSocket(wsUrl);
37
- }
38
- else {
39
- // SSE MCP connection for regular users
40
- const serverUrl = options.serverUrl ?? this.config.get('mcpServerUrl') ?? 'https://api.lanonasis.com';
41
- if (!output.isSilent()) {
42
- output.log(chalk.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
43
- }
43
+ this.isConnected = true;
44
+ return true;
45
+ case 'remote':
46
+ // For remote MCP, we'll use the REST API with MCP-style interface
47
+ serverUrl = options.serverUrl ??
48
+ this.config.get('mcpServerUrl') ??
49
+ 'https://api.lanonasis.com';
50
+ console.log(chalk.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
44
51
  // Initialize SSE connection for real-time updates
45
52
  await this.initializeSSE(serverUrl);
46
- }
47
- this.isConnected = true;
48
- return true;
49
- }
50
- else {
51
- // Local MCP server connection
52
- const serverPath = options.serverPath ?? this.config.get('mcpServerPath') ?? path.join(__dirname, '../../../../onasis-gateway/mcp-server/server.js');
53
- if (!output.isSilent()) {
54
- output.log(chalk.cyan(`Connecting to local MCP server at ${serverPath}...`));
55
- }
56
- const transport = new StdioClientTransport({
57
- command: 'node',
58
- args: [serverPath]
59
- });
60
- this.client = new Client({
61
- name: '@lanonasis/cli',
62
- version: '1.0.0'
63
- }, {
64
- capabilities: {}
65
- });
66
- await this.client.connect(transport);
67
- this.isConnected = true;
68
- if (!output.isSilent()) {
69
- output.log(chalk.green('✓ Connected to MCP server'));
70
- }
71
- return true;
53
+ this.isConnected = true;
54
+ return true;
55
+ case 'local':
56
+ default:
57
+ {
58
+ // Local MCP server connection
59
+ serverPath = options.serverPath ??
60
+ this.config.get('mcpServerPath') ??
61
+ path.join(__dirname, '../../../../onasis-gateway/mcp-server/server.js');
62
+ console.log(chalk.cyan(`Connecting to local MCP server at ${serverPath}...`));
63
+ const localTransport = new StdioClientTransport({
64
+ command: 'node',
65
+ args: [serverPath]
66
+ });
67
+ this.client = new Client({
68
+ name: '@lanonasis/cli',
69
+ version: '1.0.0'
70
+ }, {
71
+ capabilities: {}
72
+ });
73
+ await this.client.connect(localTransport);
74
+ }
75
+ this.isConnected = true;
76
+ console.log(chalk.green('✓ Connected to MCP server'));
77
+ return true;
72
78
  }
73
79
  }
74
80
  catch (error) {
75
- if (!output.isSilent()) {
76
- console.error(chalk.red('Failed to connect to MCP server:'), error);
77
- }
81
+ console.error(chalk.red('Failed to connect to MCP server:'), error);
78
82
  this.isConnected = false;
79
83
  return false;
80
84
  }
81
85
  }
82
- /**
83
- * Initialize WebSocket MCP connection for enterprise users
84
- */
85
- async initializeWebSocket(wsUrl) {
86
- const { WebSocketMCPClient } = await import('./websocket-mcp-client.js');
87
- const apiKey = this.config.get('token');
88
- if (!apiKey) {
89
- throw new Error('API key required for WebSocket MCP connection');
90
- }
91
- this.wsClient = new WebSocketMCPClient({
92
- url: wsUrl,
93
- apiKey: apiKey
94
- });
95
- await this.wsClient.connect();
96
- if (!output.isSilent()) {
97
- output.log(chalk.green('✅ WebSocket MCP connection established'));
98
- }
99
- }
100
86
  /**
101
87
  * Initialize SSE connection for real-time updates
102
88
  */
@@ -109,21 +95,98 @@ export class MCPClient {
109
95
  this.sseConnection.onmessage = (event) => {
110
96
  try {
111
97
  const data = JSON.parse(event.data);
112
- if (!output.isSilent() && process.env.CLI_VERBOSE === 'true') {
113
- output.log(chalk.blue('📡 Real-time update:'), data.type);
114
- }
98
+ console.log(chalk.blue('📡 Real-time update:'), data.type);
115
99
  }
116
- catch (error) {
100
+ catch {
117
101
  // Ignore parse errors
118
102
  }
119
103
  };
120
- this.sseConnection.onerror = (error) => {
121
- if (!output.isSilent() && process.env.CLI_VERBOSE === 'true') {
122
- console.error(chalk.yellow('⚠️ SSE connection error (will retry)'));
123
- }
104
+ this.sseConnection.onerror = () => {
105
+ console.error(chalk.yellow('⚠️ SSE connection error (will retry)'));
124
106
  };
125
107
  }
126
108
  }
109
+ /**
110
+ * Initialize WebSocket connection for enterprise MCP server
111
+ */
112
+ async initializeWebSocket(wsUrl) {
113
+ const token = this.config.get('token');
114
+ if (!token) {
115
+ throw new Error('API key required for WebSocket mode. Set LANONASIS_API_KEY or login first.');
116
+ }
117
+ return new Promise((resolve, reject) => {
118
+ try {
119
+ // Close existing connection if any
120
+ if (this.wsConnection) {
121
+ this.wsConnection.close();
122
+ this.wsConnection = null;
123
+ }
124
+ // Create new WebSocket connection with authentication
125
+ this.wsConnection = new WebSocket(wsUrl, {
126
+ headers: {
127
+ 'Authorization': `Bearer ${token}`,
128
+ 'X-API-Key': token
129
+ }
130
+ });
131
+ this.wsConnection.on('open', () => {
132
+ console.log(chalk.green('✅ Connected to MCP WebSocket server'));
133
+ // Send initialization message
134
+ this.sendWebSocketMessage({
135
+ id: 1,
136
+ method: 'initialize',
137
+ params: {
138
+ protocolVersion: '2024-11-05',
139
+ capabilities: {
140
+ tools: ['memory_management', 'workflow_orchestration']
141
+ },
142
+ clientInfo: {
143
+ name: '@lanonasis/cli',
144
+ version: '1.1.0'
145
+ }
146
+ }
147
+ });
148
+ resolve();
149
+ });
150
+ this.wsConnection.on('message', (data) => {
151
+ try {
152
+ const message = JSON.parse(data.toString());
153
+ console.log(chalk.blue('📡 MCP message:'), message.id, message.method || 'response');
154
+ }
155
+ catch (error) {
156
+ console.error('Failed to parse WebSocket message:', error);
157
+ }
158
+ });
159
+ this.wsConnection.on('error', (error) => {
160
+ console.error(chalk.red('WebSocket error:'), error);
161
+ reject(error);
162
+ });
163
+ this.wsConnection.on('close', (code, reason) => {
164
+ console.log(chalk.yellow(`WebSocket connection closed (${code}): ${reason}`));
165
+ // Auto-reconnect after delay
166
+ setTimeout(() => {
167
+ if (this.isConnected) {
168
+ console.log(chalk.blue('🔄 Attempting to reconnect to WebSocket...'));
169
+ this.initializeWebSocket(wsUrl).catch(err => {
170
+ console.error('Failed to reconnect:', err);
171
+ });
172
+ }
173
+ }, 5000);
174
+ });
175
+ }
176
+ catch (error) {
177
+ reject(error);
178
+ }
179
+ });
180
+ }
181
+ /**
182
+ * Send a message over the WebSocket connection
183
+ */
184
+ sendWebSocketMessage(message) {
185
+ if (!this.wsConnection) {
186
+ throw new Error('WebSocket not connected');
187
+ }
188
+ this.wsConnection.send(JSON.stringify(message));
189
+ }
127
190
  /**
128
191
  * Disconnect from MCP server
129
192
  */
@@ -160,7 +223,12 @@ export class MCPClient {
160
223
  name: toolName,
161
224
  arguments: args
162
225
  });
163
- return result;
226
+ // Convert the SDK result to our expected MCPToolResponse format
227
+ return {
228
+ result: result,
229
+ code: 200,
230
+ message: 'Success'
231
+ };
164
232
  }
165
233
  catch (error) {
166
234
  throw new Error(`MCP tool call failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -190,20 +258,21 @@ export class MCPClient {
190
258
  },
191
259
  'memory_get_memory': {
192
260
  method: 'GET',
193
- endpoint: `/api/v1/memory/${args.memory_id}`,
261
+ endpoint: '/api/v1/memory/{id}',
194
262
  transform: () => undefined
195
263
  },
196
264
  'memory_update_memory': {
197
265
  method: 'PUT',
198
- endpoint: `/api/v1/memory/${args.memory_id}`,
266
+ endpoint: '/api/v1/memory/{id}',
199
267
  transform: (args) => {
200
- const { memory_id, ...data } = args;
268
+ const data = { ...args };
269
+ delete data.memory_id;
201
270
  return data;
202
271
  }
203
272
  },
204
273
  'memory_delete_memory': {
205
274
  method: 'DELETE',
206
- endpoint: `/api/v1/memory/${args.memory_id}`,
275
+ endpoint: '/api/v1/memory/{id}',
207
276
  transform: () => undefined
208
277
  },
209
278
  'memory_list_memories': {
@@ -218,9 +287,15 @@ export class MCPClient {
218
287
  }
219
288
  try {
220
289
  const axios = (await import('axios')).default;
290
+ // Handle dynamic endpoint for memory operations that need ID
291
+ let endpoint = mapping.endpoint;
292
+ if (endpoint.includes('{id}') && args.memory_id) {
293
+ // Ensure memory_id is treated as a string for replacement
294
+ endpoint = endpoint.replace('{id}', String(args.memory_id));
295
+ }
221
296
  const response = await axios({
222
297
  method: mapping.method,
223
- url: `${apiUrl}${mapping.endpoint}`,
298
+ url: `${apiUrl}${endpoint}`,
224
299
  headers: {
225
300
  'Authorization': `Bearer ${token}`,
226
301
  'Content-Type': 'application/json'
@@ -231,7 +306,11 @@ export class MCPClient {
231
306
  return response.data;
232
307
  }
233
308
  catch (error) {
234
- throw new Error(`Remote tool call failed: ${error.response?.data?.error || error.message}`);
309
+ // Safely handle errors with type checking
310
+ const errorObj = error;
311
+ const errorMsg = errorObj.response?.data?.error ||
312
+ (errorObj.message ? errorObj.message : 'Unknown error');
313
+ throw new Error(`Remote tool call failed: ${errorMsg}`);
235
314
  }
236
315
  }
237
316
  /**
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@lanonasis/cli",
3
- "version": "1.5.0",
4
- "description": "Lanonasis Unified CLI - Enterprise AI Infrastructure & Memory as a Service",
5
- "main": "dist/index.js",
3
+ "version": "1.5.1",
4
+ "description": "LanOnasis Enterprise CLI - Memory as a Service, API Key Management, and Infrastructure Orchestration",
5
+ "main": "dist/index-simple.js",
6
6
  "bin": {
7
- "lanonasis": "dist/index.js",
8
- "onasis": "dist/index.js",
9
- "memory": "dist/index.js",
10
- "maas": "dist/index.js"
7
+ "lanonasis": "dist/index-simple.js",
8
+ "lanonasis-mcp-server": "dist/mcp-server.js",
9
+ "memory": "dist/index-simple.js",
10
+ "maas": "dist/index-simple.js"
11
11
  },
12
12
  "type": "module",
13
13
  "scripts": {
14
14
  "dev": "tsx src/index.ts",
15
- "build": "tsc && chmod +x dist/index.js",
15
+ "build": "tsc && chmod +x dist/index-simple.js",
16
16
  "start": "node dist/index.js",
17
17
  "test": "jest",
18
18
  "lint": "eslint src/**/*.ts",
@@ -26,7 +26,11 @@
26
26
  "maas",
27
27
  "enterprise",
28
28
  "infrastructure",
29
- "orchestrator"
29
+ "orchestrator",
30
+ "api-keys",
31
+ "secrets",
32
+ "mcp",
33
+ "security"
30
34
  ],
31
35
  "author": "Lanonasis (Seye Derick)",
32
36
  "license": "MIT",
@@ -37,8 +41,10 @@
37
41
  "dependencies": {
38
42
  "@modelcontextprotocol/sdk": "^1.17.0",
39
43
  "@types/eventsource": "^1.1.15",
44
+ "@types/ws": "^8.18.1",
40
45
  "axios": "^1.11.0",
41
46
  "chalk": "^5.4.1",
47
+ "cli-table3": "^0.6.5",
42
48
  "commander": "^12.1.0",
43
49
  "date-fns": "^4.1.0",
44
50
  "dotenv": "^17.2.1",
@@ -48,7 +54,8 @@
48
54
  "open": "^10.2.0",
49
55
  "ora": "^8.2.0",
50
56
  "table": "^6.9.0",
51
- "word-wrap": "^1.2.5"
57
+ "word-wrap": "^1.2.5",
58
+ "ws": "^8.18.3"
52
59
  },
53
60
  "devDependencies": {
54
61
  "@types/inquirer": "^9.0.7",
@@ -1,28 +0,0 @@
1
- export interface CompletionData {
2
- commands: string[];
3
- memory: {
4
- types: string[];
5
- tags: string[];
6
- recentIds: string[];
7
- };
8
- topics: string[];
9
- organizations: string[];
10
- }
11
- export declare class TabCompletions {
12
- private config;
13
- private completionData;
14
- constructor();
15
- init(): Promise<void>;
16
- private loadDynamicCompletions;
17
- private loadRecentMemories;
18
- private loadTopics;
19
- private loadTags;
20
- getCompletions(line: string, position: number): string[];
21
- private getMemoryCompletions;
22
- private getCreateCompletions;
23
- private getSearchCompletions;
24
- private getListCompletions;
25
- private getTopicCompletions;
26
- generateBashCompletion(): string;
27
- generateZshCompletion(): string;
28
- }