@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.
- package/README.md +284 -586
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.js +812 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +127 -138
- package/dist/commands/completion.d.ts +33 -0
- package/dist/commands/completion.js +378 -0
- package/dist/commands/guide.d.ts +19 -0
- package/dist/commands/guide.js +446 -0
- package/dist/commands/mcp.js +30 -37
- package/dist/commands/memory.js +53 -78
- package/dist/completions/bash-completion.sh +88 -0
- package/dist/completions/fish-completion.fish +132 -0
- package/dist/completions/zsh-completion.zsh +196 -0
- package/dist/index-simple.js +633 -183
- package/dist/index.js +327 -221
- package/dist/mcp-server.d.ts +38 -0
- package/dist/mcp-server.js +154 -0
- package/dist/utils/api.d.ts +12 -2
- package/dist/utils/api.js +38 -4
- package/dist/utils/config.d.ts +5 -2
- package/dist/utils/config.js +39 -15
- package/dist/utils/formatting.d.ts +2 -0
- package/dist/utils/formatting.js +13 -0
- package/dist/utils/mcp-client.d.ts +49 -6
- package/dist/utils/mcp-client.js +159 -82
- package/package.json +22 -12
- package/dist/utils/completions.d.ts +0 -28
- package/dist/utils/completions.js +0 -276
- package/dist/utils/mcp-client.test.d.ts +0 -1
- package/dist/utils/mcp-client.test.js +0 -125
- package/dist/utils/output.d.ts +0 -23
- package/dist/utils/output.js +0 -97
- package/dist/utils/websocket-mcp-client.d.ts +0 -60
- package/dist/utils/websocket-mcp-client.js +0 -182
- package/dist/utils/websocket-mcp-client.test.d.ts +0 -1
- package/dist/utils/websocket-mcp-client.test.js +0 -126
package/dist/utils/mcp-client.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
98
|
+
catch {
|
|
117
99
|
// Ignore parse errors
|
|
118
100
|
}
|
|
119
101
|
};
|
|
120
|
-
this.sseConnection.onerror = (
|
|
121
|
-
|
|
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
|
-
|
|
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:
|
|
259
|
+
endpoint: '/api/v1/memory/{id}',
|
|
194
260
|
transform: () => undefined
|
|
195
261
|
},
|
|
196
262
|
'memory_update_memory': {
|
|
197
263
|
method: 'PUT',
|
|
198
|
-
endpoint:
|
|
264
|
+
endpoint: '/api/v1/memory/{id}',
|
|
199
265
|
transform: (args) => {
|
|
200
|
-
const {
|
|
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:
|
|
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}${
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
10
|
-
"
|
|
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.
|
|
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
|
-
}
|