@lanonasis/cli 1.5.0 → 1.5.2

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 (37) hide show
  1. package/README.md +284 -586
  2. package/dist/commands/api-keys.d.ts +3 -0
  3. package/dist/commands/api-keys.js +812 -0
  4. package/dist/commands/auth.d.ts +2 -0
  5. package/dist/commands/auth.js +127 -138
  6. package/dist/commands/completion.d.ts +33 -0
  7. package/dist/commands/completion.js +378 -0
  8. package/dist/commands/guide.d.ts +19 -0
  9. package/dist/commands/guide.js +446 -0
  10. package/dist/commands/mcp.js +30 -37
  11. package/dist/commands/memory.js +53 -78
  12. package/dist/completions/bash-completion.sh +88 -0
  13. package/dist/completions/fish-completion.fish +132 -0
  14. package/dist/completions/zsh-completion.zsh +196 -0
  15. package/dist/index-simple.js +633 -183
  16. package/dist/index.js +327 -221
  17. package/dist/mcp-server.d.ts +38 -0
  18. package/dist/mcp-server.js +154 -0
  19. package/dist/utils/api.d.ts +12 -2
  20. package/dist/utils/api.js +38 -4
  21. package/dist/utils/config.d.ts +5 -2
  22. package/dist/utils/config.js +39 -15
  23. package/dist/utils/formatting.d.ts +2 -0
  24. package/dist/utils/formatting.js +13 -0
  25. package/dist/utils/mcp-client.d.ts +49 -6
  26. package/dist/utils/mcp-client.js +159 -82
  27. package/package.json +22 -12
  28. package/dist/utils/completions.d.ts +0 -28
  29. package/dist/utils/completions.js +0 -276
  30. package/dist/utils/mcp-client.test.d.ts +0 -1
  31. package/dist/utils/mcp-client.test.js +0 -125
  32. package/dist/utils/output.d.ts +0 -23
  33. package/dist/utils/output.js +0 -97
  34. package/dist/utils/websocket-mcp-client.d.ts +0 -60
  35. package/dist/utils/websocket-mcp-client.js +0 -182
  36. package/dist/utils/websocket-mcp-client.test.d.ts +0 -1
  37. package/dist/utils/websocket-mcp-client.test.js +0 -126
@@ -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,65 @@ 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.5.2'
70
+ });
71
+ await this.client.connect(localTransport);
72
+ }
73
+ this.isConnected = true;
74
+ console.log(chalk.green('✓ Connected to MCP server'));
75
+ return true;
72
76
  }
73
77
  }
74
78
  catch (error) {
75
- if (!output.isSilent()) {
76
- console.error(chalk.red('Failed to connect to MCP server:'), error);
77
- }
79
+ console.error(chalk.red('Failed to connect to MCP server:'), error);
78
80
  this.isConnected = false;
79
81
  return false;
80
82
  }
81
83
  }
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
84
  /**
101
85
  * Initialize SSE connection for real-time updates
102
86
  */
@@ -109,21 +93,98 @@ export class MCPClient {
109
93
  this.sseConnection.onmessage = (event) => {
110
94
  try {
111
95
  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
- }
96
+ console.log(chalk.blue('📡 Real-time update:'), data.type);
115
97
  }
116
- catch (error) {
98
+ catch {
117
99
  // Ignore parse errors
118
100
  }
119
101
  };
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
- }
102
+ this.sseConnection.onerror = () => {
103
+ console.error(chalk.yellow('⚠️ SSE connection error (will retry)'));
124
104
  };
125
105
  }
126
106
  }
107
+ /**
108
+ * Initialize WebSocket connection for enterprise MCP server
109
+ */
110
+ async initializeWebSocket(wsUrl) {
111
+ const token = this.config.get('token');
112
+ if (!token) {
113
+ throw new Error('API key required for WebSocket mode. Set LANONASIS_API_KEY or login first.');
114
+ }
115
+ return new Promise((resolve, reject) => {
116
+ try {
117
+ // Close existing connection if any
118
+ if (this.wsConnection) {
119
+ this.wsConnection.close();
120
+ this.wsConnection = null;
121
+ }
122
+ // Create new WebSocket connection with authentication
123
+ this.wsConnection = new WebSocket(wsUrl, {
124
+ headers: {
125
+ 'Authorization': `Bearer ${token}`,
126
+ 'X-API-Key': token
127
+ }
128
+ });
129
+ this.wsConnection.on('open', () => {
130
+ console.log(chalk.green('✅ Connected to MCP WebSocket server'));
131
+ // Send initialization message
132
+ this.sendWebSocketMessage({
133
+ id: 1,
134
+ method: 'initialize',
135
+ params: {
136
+ protocolVersion: '2024-11-05',
137
+ capabilities: {
138
+ tools: ['memory_management', 'workflow_orchestration']
139
+ },
140
+ clientInfo: {
141
+ name: '@lanonasis/cli',
142
+ version: '1.1.0'
143
+ }
144
+ }
145
+ });
146
+ resolve();
147
+ });
148
+ this.wsConnection.on('message', (data) => {
149
+ try {
150
+ const message = JSON.parse(data.toString());
151
+ console.log(chalk.blue('📡 MCP message:'), message.id, message.method || 'response');
152
+ }
153
+ catch (error) {
154
+ console.error('Failed to parse WebSocket message:', error);
155
+ }
156
+ });
157
+ this.wsConnection.on('error', (error) => {
158
+ console.error(chalk.red('WebSocket error:'), error);
159
+ reject(error);
160
+ });
161
+ this.wsConnection.on('close', (code, reason) => {
162
+ console.log(chalk.yellow(`WebSocket connection closed (${code}): ${reason}`));
163
+ // Auto-reconnect after delay
164
+ setTimeout(() => {
165
+ if (this.isConnected) {
166
+ console.log(chalk.blue('🔄 Attempting to reconnect to WebSocket...'));
167
+ this.initializeWebSocket(wsUrl).catch(err => {
168
+ console.error('Failed to reconnect:', err);
169
+ });
170
+ }
171
+ }, 5000);
172
+ });
173
+ }
174
+ catch (error) {
175
+ reject(error);
176
+ }
177
+ });
178
+ }
179
+ /**
180
+ * Send a message over the WebSocket connection
181
+ */
182
+ sendWebSocketMessage(message) {
183
+ if (!this.wsConnection) {
184
+ throw new Error('WebSocket not connected');
185
+ }
186
+ this.wsConnection.send(JSON.stringify(message));
187
+ }
127
188
  /**
128
189
  * Disconnect from MCP server
129
190
  */
@@ -160,7 +221,12 @@ export class MCPClient {
160
221
  name: toolName,
161
222
  arguments: args
162
223
  });
163
- return result;
224
+ // Convert the SDK result to our expected MCPToolResponse format
225
+ return {
226
+ result: result,
227
+ code: 200,
228
+ message: 'Success'
229
+ };
164
230
  }
165
231
  catch (error) {
166
232
  throw new Error(`MCP tool call failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -190,20 +256,21 @@ export class MCPClient {
190
256
  },
191
257
  'memory_get_memory': {
192
258
  method: 'GET',
193
- endpoint: `/api/v1/memory/${args.memory_id}`,
259
+ endpoint: '/api/v1/memory/{id}',
194
260
  transform: () => undefined
195
261
  },
196
262
  'memory_update_memory': {
197
263
  method: 'PUT',
198
- endpoint: `/api/v1/memory/${args.memory_id}`,
264
+ endpoint: '/api/v1/memory/{id}',
199
265
  transform: (args) => {
200
- const { memory_id, ...data } = args;
266
+ const data = { ...args };
267
+ delete data.memory_id;
201
268
  return data;
202
269
  }
203
270
  },
204
271
  'memory_delete_memory': {
205
272
  method: 'DELETE',
206
- endpoint: `/api/v1/memory/${args.memory_id}`,
273
+ endpoint: '/api/v1/memory/{id}',
207
274
  transform: () => undefined
208
275
  },
209
276
  'memory_list_memories': {
@@ -218,9 +285,15 @@ export class MCPClient {
218
285
  }
219
286
  try {
220
287
  const axios = (await import('axios')).default;
288
+ // Handle dynamic endpoint for memory operations that need ID
289
+ let endpoint = mapping.endpoint;
290
+ if (endpoint.includes('{id}') && args.memory_id) {
291
+ // Ensure memory_id is treated as a string for replacement
292
+ endpoint = endpoint.replace('{id}', String(args.memory_id));
293
+ }
221
294
  const response = await axios({
222
295
  method: mapping.method,
223
- url: `${apiUrl}${mapping.endpoint}`,
296
+ url: `${apiUrl}${endpoint}`,
224
297
  headers: {
225
298
  'Authorization': `Bearer ${token}`,
226
299
  'Content-Type': 'application/json'
@@ -231,7 +304,11 @@ export class MCPClient {
231
304
  return response.data;
232
305
  }
233
306
  catch (error) {
234
- throw new Error(`Remote tool call failed: ${error.response?.data?.error || error.message}`);
307
+ // Safely handle errors with type checking
308
+ const errorObj = error;
309
+ const errorMsg = errorObj.response?.data?.error ||
310
+ (errorObj.message ? errorObj.message : 'Unknown error');
311
+ throw new Error(`Remote tool call failed: ${errorMsg}`);
235
312
  }
236
313
  }
237
314
  /**
package/package.json CHANGED
@@ -1,22 +1,25 @@
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.2",
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
+ "onasis": "dist/index-simple.js",
9
+ "lanonasis-mcp-server": "dist/mcp-server.js",
10
+ "lanonasis-mcp-cli-aligned": "dist/mcp-server.js",
11
+ "memory": "dist/index-simple.js",
12
+ "maas": "dist/index-simple.js"
11
13
  },
12
14
  "type": "module",
13
15
  "scripts": {
14
16
  "dev": "tsx src/index.ts",
15
- "build": "tsc && chmod +x dist/index.js",
17
+ "build": "tsc && chmod +x dist/index-simple.js && chmod +x dist/mcp-server.js && cp -r src/completions dist/",
16
18
  "start": "node dist/index.js",
17
19
  "test": "jest",
18
20
  "lint": "eslint src/**/*.ts",
19
- "type-check": "tsc --noEmit"
21
+ "type-check": "tsc --noEmit",
22
+ "prepare": "npm run build"
20
23
  },
21
24
  "keywords": [
22
25
  "lanonasis",
@@ -26,7 +29,11 @@
26
29
  "maas",
27
30
  "enterprise",
28
31
  "infrastructure",
29
- "orchestrator"
32
+ "orchestrator",
33
+ "api-keys",
34
+ "secrets",
35
+ "mcp",
36
+ "security"
30
37
  ],
31
38
  "author": "Lanonasis (Seye Derick)",
32
39
  "license": "MIT",
@@ -35,10 +42,12 @@
35
42
  "README.md"
36
43
  ],
37
44
  "dependencies": {
38
- "@modelcontextprotocol/sdk": "^1.17.0",
45
+ "@modelcontextprotocol/sdk": "^1.17.1",
39
46
  "@types/eventsource": "^1.1.15",
47
+ "@types/ws": "^8.18.1",
40
48
  "axios": "^1.11.0",
41
49
  "chalk": "^5.4.1",
50
+ "cli-table3": "^0.6.5",
42
51
  "commander": "^12.1.0",
43
52
  "date-fns": "^4.1.0",
44
53
  "dotenv": "^17.2.1",
@@ -48,7 +57,8 @@
48
57
  "open": "^10.2.0",
49
58
  "ora": "^8.2.0",
50
59
  "table": "^6.9.0",
51
- "word-wrap": "^1.2.5"
60
+ "word-wrap": "^1.2.5",
61
+ "ws": "^8.18.3"
52
62
  },
53
63
  "devDependencies": {
54
64
  "@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
- }