@lanonasis/cli 1.4.2 → 1.5.0
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 +357 -80
- package/dist/commands/auth.js +151 -1
- package/dist/commands/mcp.js +37 -30
- package/dist/commands/memory.js +78 -53
- package/dist/index-simple.js +189 -522
- package/dist/index.js +221 -327
- package/dist/utils/api.d.ts +2 -12
- package/dist/utils/api.js +0 -17
- package/dist/utils/completions.d.ts +28 -0
- package/dist/utils/completions.js +276 -0
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +15 -2
- package/dist/utils/formatting.d.ts +0 -2
- package/dist/utils/formatting.js +0 -13
- package/dist/utils/mcp-client.d.ts +6 -49
- package/dist/utils/mcp-client.js +82 -161
- package/dist/utils/mcp-client.test.d.ts +1 -0
- package/dist/utils/mcp-client.test.js +125 -0
- package/dist/utils/output.d.ts +23 -0
- package/dist/utils/output.js +97 -0
- package/dist/utils/websocket-mcp-client.d.ts +60 -0
- package/dist/utils/websocket-mcp-client.js +182 -0
- package/dist/utils/websocket-mcp-client.test.d.ts +1 -0
- package/dist/utils/websocket-mcp-client.test.js +126 -0
- package/package.json +10 -17
- package/dist/commands/api-keys.d.ts +0 -3
- package/dist/commands/api-keys.js +0 -812
- package/dist/mcp-server.d.ts +0 -2
- package/dist/mcp-server.js +0 -519
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 { output } from './output.js';
|
|
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
|
+
wsClient = null;
|
|
17
17
|
constructor() {
|
|
18
18
|
this.config = new CLIConfig();
|
|
19
19
|
}
|
|
@@ -22,67 +22,81 @@ 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
|
-
|
|
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
|
|
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
|
|
42
36
|
await this.initializeWebSocket(wsUrl);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.log(chalk.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
|
|
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
|
+
}
|
|
51
44
|
// Initialize SSE connection for real-time updates
|
|
52
45
|
await this.initializeSSE(serverUrl);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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;
|
|
78
72
|
}
|
|
79
73
|
}
|
|
80
74
|
catch (error) {
|
|
81
|
-
|
|
75
|
+
if (!output.isSilent()) {
|
|
76
|
+
console.error(chalk.red('Failed to connect to MCP server:'), error);
|
|
77
|
+
}
|
|
82
78
|
this.isConnected = false;
|
|
83
79
|
return false;
|
|
84
80
|
}
|
|
85
81
|
}
|
|
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
|
+
}
|
|
86
100
|
/**
|
|
87
101
|
* Initialize SSE connection for real-time updates
|
|
88
102
|
*/
|
|
@@ -95,97 +109,20 @@ export class MCPClient {
|
|
|
95
109
|
this.sseConnection.onmessage = (event) => {
|
|
96
110
|
try {
|
|
97
111
|
const data = JSON.parse(event.data);
|
|
98
|
-
|
|
112
|
+
if (!output.isSilent() && process.env.CLI_VERBOSE === 'true') {
|
|
113
|
+
output.log(chalk.blue('📡 Real-time update:'), data.type);
|
|
114
|
+
}
|
|
99
115
|
}
|
|
100
|
-
catch {
|
|
116
|
+
catch (error) {
|
|
101
117
|
// Ignore parse errors
|
|
102
118
|
}
|
|
103
119
|
};
|
|
104
|
-
this.sseConnection.onerror = () => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
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;
|
|
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
123
|
}
|
|
124
|
-
|
|
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');
|
|
124
|
+
};
|
|
187
125
|
}
|
|
188
|
-
this.wsConnection.send(JSON.stringify(message));
|
|
189
126
|
}
|
|
190
127
|
/**
|
|
191
128
|
* Disconnect from MCP server
|
|
@@ -223,12 +160,7 @@ export class MCPClient {
|
|
|
223
160
|
name: toolName,
|
|
224
161
|
arguments: args
|
|
225
162
|
});
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
result: result,
|
|
229
|
-
code: 200,
|
|
230
|
-
message: 'Success'
|
|
231
|
-
};
|
|
163
|
+
return result;
|
|
232
164
|
}
|
|
233
165
|
catch (error) {
|
|
234
166
|
throw new Error(`MCP tool call failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -258,21 +190,20 @@ export class MCPClient {
|
|
|
258
190
|
},
|
|
259
191
|
'memory_get_memory': {
|
|
260
192
|
method: 'GET',
|
|
261
|
-
endpoint:
|
|
193
|
+
endpoint: `/api/v1/memory/${args.memory_id}`,
|
|
262
194
|
transform: () => undefined
|
|
263
195
|
},
|
|
264
196
|
'memory_update_memory': {
|
|
265
197
|
method: 'PUT',
|
|
266
|
-
endpoint:
|
|
198
|
+
endpoint: `/api/v1/memory/${args.memory_id}`,
|
|
267
199
|
transform: (args) => {
|
|
268
|
-
const data =
|
|
269
|
-
delete data.memory_id;
|
|
200
|
+
const { memory_id, ...data } = args;
|
|
270
201
|
return data;
|
|
271
202
|
}
|
|
272
203
|
},
|
|
273
204
|
'memory_delete_memory': {
|
|
274
205
|
method: 'DELETE',
|
|
275
|
-
endpoint:
|
|
206
|
+
endpoint: `/api/v1/memory/${args.memory_id}`,
|
|
276
207
|
transform: () => undefined
|
|
277
208
|
},
|
|
278
209
|
'memory_list_memories': {
|
|
@@ -287,15 +218,9 @@ export class MCPClient {
|
|
|
287
218
|
}
|
|
288
219
|
try {
|
|
289
220
|
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
|
-
}
|
|
296
221
|
const response = await axios({
|
|
297
222
|
method: mapping.method,
|
|
298
|
-
url: `${apiUrl}${endpoint}`,
|
|
223
|
+
url: `${apiUrl}${mapping.endpoint}`,
|
|
299
224
|
headers: {
|
|
300
225
|
'Authorization': `Bearer ${token}`,
|
|
301
226
|
'Content-Type': 'application/json'
|
|
@@ -306,11 +231,7 @@ export class MCPClient {
|
|
|
306
231
|
return response.data;
|
|
307
232
|
}
|
|
308
233
|
catch (error) {
|
|
309
|
-
|
|
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}`);
|
|
234
|
+
throw new Error(`Remote tool call failed: ${error.response?.data?.error || error.message}`);
|
|
314
235
|
}
|
|
315
236
|
}
|
|
316
237
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
2
|
+
import { MCPClient } from './mcp-client';
|
|
3
|
+
describe('MCPClient', () => {
|
|
4
|
+
let client;
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
client = new MCPClient();
|
|
7
|
+
});
|
|
8
|
+
describe('constructor', () => {
|
|
9
|
+
it('should create client instance', () => {
|
|
10
|
+
expect(client).toBeDefined();
|
|
11
|
+
expect(client.isConnected()).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('connection modes', () => {
|
|
15
|
+
it('should support local connection mode', async () => {
|
|
16
|
+
const result = await client.connect({ mode: 'local' });
|
|
17
|
+
// Local mode may fail without actual server, but should not throw
|
|
18
|
+
expect(typeof result).toBe('boolean');
|
|
19
|
+
});
|
|
20
|
+
it('should support remote connection mode', async () => {
|
|
21
|
+
const result = await client.connect({ mode: 'remote' });
|
|
22
|
+
// Remote mode may fail without credentials, but should not throw
|
|
23
|
+
expect(typeof result).toBe('boolean');
|
|
24
|
+
});
|
|
25
|
+
it('should support websocket connection mode', async () => {
|
|
26
|
+
const result = await client.connect({ mode: 'websocket' });
|
|
27
|
+
// WebSocket mode may fail without server, but should not throw
|
|
28
|
+
expect(typeof result).toBe('boolean');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('connection options', () => {
|
|
32
|
+
it('should handle server path option', async () => {
|
|
33
|
+
const options = {
|
|
34
|
+
mode: 'local',
|
|
35
|
+
serverPath: '/custom/path/to/server'
|
|
36
|
+
};
|
|
37
|
+
const result = await client.connect(options);
|
|
38
|
+
expect(typeof result).toBe('boolean');
|
|
39
|
+
});
|
|
40
|
+
it('should handle server URL option', async () => {
|
|
41
|
+
const options = {
|
|
42
|
+
mode: 'websocket',
|
|
43
|
+
serverUrl: 'ws://custom.server.com:8081'
|
|
44
|
+
};
|
|
45
|
+
const result = await client.connect(options);
|
|
46
|
+
expect(typeof result).toBe('boolean');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('tool operations', () => {
|
|
50
|
+
it('should handle list tools request', async () => {
|
|
51
|
+
// Mock successful connection first
|
|
52
|
+
client.isConnected = () => true;
|
|
53
|
+
try {
|
|
54
|
+
const tools = await client.listTools();
|
|
55
|
+
expect(Array.isArray(tools)).toBe(true);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Expected if no actual connection
|
|
59
|
+
expect(error).toBeDefined();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
it('should handle call tool request', async () => {
|
|
63
|
+
// Mock successful connection first
|
|
64
|
+
client.isConnected = () => true;
|
|
65
|
+
try {
|
|
66
|
+
const result = await client.callTool('test_tool', { param: 'value' });
|
|
67
|
+
expect(result).toBeDefined();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// Expected if no actual connection
|
|
71
|
+
expect(error).toBeDefined();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('memory operations', () => {
|
|
76
|
+
it('should handle create memory request', async () => {
|
|
77
|
+
// Mock successful connection first
|
|
78
|
+
client.isConnected = () => true;
|
|
79
|
+
try {
|
|
80
|
+
const result = await client.createMemory({
|
|
81
|
+
title: 'Test Memory',
|
|
82
|
+
content: 'Test content',
|
|
83
|
+
tags: ['test']
|
|
84
|
+
});
|
|
85
|
+
expect(result).toBeDefined();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// Expected if no actual connection
|
|
89
|
+
expect(error).toBeDefined();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
it('should handle search memories request', async () => {
|
|
93
|
+
// Mock successful connection first
|
|
94
|
+
client.isConnected = () => true;
|
|
95
|
+
try {
|
|
96
|
+
const result = await client.searchMemories({
|
|
97
|
+
query: 'test query',
|
|
98
|
+
limit: 10
|
|
99
|
+
});
|
|
100
|
+
expect(result).toBeDefined();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Expected if no actual connection
|
|
104
|
+
expect(error).toBeDefined();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe('error handling', () => {
|
|
109
|
+
it('should handle connection failures gracefully', async () => {
|
|
110
|
+
const result = await client.connect({
|
|
111
|
+
mode: 'websocket',
|
|
112
|
+
serverUrl: 'ws://nonexistent.server:9999'
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it('should handle invalid connection modes', async () => {
|
|
117
|
+
try {
|
|
118
|
+
await client.connect({ mode: 'invalid' });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
expect(error).toBeDefined();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface OutputOptions {
|
|
2
|
+
format?: string;
|
|
3
|
+
silent?: boolean;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare class OutputManager {
|
|
7
|
+
private static instance;
|
|
8
|
+
private options;
|
|
9
|
+
static getInstance(): OutputManager;
|
|
10
|
+
setOptions(options: OutputOptions): void;
|
|
11
|
+
isJsonOutput(): boolean;
|
|
12
|
+
isSilent(): boolean;
|
|
13
|
+
log(...args: any[]): void;
|
|
14
|
+
error(...args: any[]): void;
|
|
15
|
+
json(data: any): void;
|
|
16
|
+
table(data: any[]): void;
|
|
17
|
+
success(message: string): void;
|
|
18
|
+
warning(message: string): void;
|
|
19
|
+
info(message: string): void;
|
|
20
|
+
}
|
|
21
|
+
export declare const output: OutputManager;
|
|
22
|
+
export declare function showOutput(format?: string): boolean;
|
|
23
|
+
export declare function formatOutput(data: any, format?: string): string;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export class OutputManager {
|
|
3
|
+
static instance;
|
|
4
|
+
options = {};
|
|
5
|
+
static getInstance() {
|
|
6
|
+
if (!OutputManager.instance) {
|
|
7
|
+
OutputManager.instance = new OutputManager();
|
|
8
|
+
}
|
|
9
|
+
return OutputManager.instance;
|
|
10
|
+
}
|
|
11
|
+
setOptions(options) {
|
|
12
|
+
this.options = { ...this.options, ...options };
|
|
13
|
+
}
|
|
14
|
+
isJsonOutput() {
|
|
15
|
+
return this.options.format === 'json' ||
|
|
16
|
+
this.options.json === true ||
|
|
17
|
+
process.env.CLI_OUTPUT_FORMAT === 'json';
|
|
18
|
+
}
|
|
19
|
+
isSilent() {
|
|
20
|
+
return this.options.silent === true ||
|
|
21
|
+
this.isJsonOutput() ||
|
|
22
|
+
process.env.CLI_SILENT === 'true';
|
|
23
|
+
}
|
|
24
|
+
log(...args) {
|
|
25
|
+
if (!this.isSilent()) {
|
|
26
|
+
console.log(...args);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
error(...args) {
|
|
30
|
+
if (this.isJsonOutput()) {
|
|
31
|
+
console.error(JSON.stringify({
|
|
32
|
+
error: true,
|
|
33
|
+
message: args.join(' ').replace(/\x1b\[[0-9;]*m/g, '') // Strip ANSI codes
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.error(...args);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
json(data) {
|
|
41
|
+
console.log(JSON.stringify(data, null, 2));
|
|
42
|
+
}
|
|
43
|
+
table(data) {
|
|
44
|
+
if (this.isJsonOutput()) {
|
|
45
|
+
this.json(data);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.table(data);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
success(message) {
|
|
52
|
+
if (this.isJsonOutput()) {
|
|
53
|
+
this.json({ success: true, message });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.log(chalk.green('✓'), message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
warning(message) {
|
|
60
|
+
if (this.isJsonOutput()) {
|
|
61
|
+
this.json({ warning: true, message });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.log(chalk.yellow('⚠️'), message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
info(message) {
|
|
68
|
+
if (!this.isSilent()) {
|
|
69
|
+
if (this.isJsonOutput()) {
|
|
70
|
+
this.json({ info: true, message });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.log(chalk.blue('ℹ'), message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export const output = OutputManager.getInstance();
|
|
79
|
+
// Helper to conditionally show output
|
|
80
|
+
export function showOutput(format) {
|
|
81
|
+
return format !== 'json' && process.env.CLI_OUTPUT_FORMAT !== 'json';
|
|
82
|
+
}
|
|
83
|
+
// Helper to format output based on format
|
|
84
|
+
export function formatOutput(data, format) {
|
|
85
|
+
const outputFormat = format || process.env.CLI_OUTPUT_FORMAT || 'table';
|
|
86
|
+
switch (outputFormat) {
|
|
87
|
+
case 'json':
|
|
88
|
+
return JSON.stringify(data, null, 2);
|
|
89
|
+
case 'yaml':
|
|
90
|
+
// Simple YAML-like format
|
|
91
|
+
return Object.entries(data)
|
|
92
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
93
|
+
.join('\n');
|
|
94
|
+
default:
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket MCP Client for Enterprise Connections
|
|
3
|
+
* Connects to mcp.lanonasis.com WebSocket server for real-time MCP operations
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
export interface MCPParams {
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface MCPResult {
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface MCPError {
|
|
13
|
+
code: number;
|
|
14
|
+
message: string;
|
|
15
|
+
data?: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface MCPMessage {
|
|
18
|
+
id?: string;
|
|
19
|
+
type: 'request' | 'response' | 'notification' | 'error';
|
|
20
|
+
method?: string;
|
|
21
|
+
params?: MCPParams;
|
|
22
|
+
result?: MCPResult;
|
|
23
|
+
error?: MCPError;
|
|
24
|
+
}
|
|
25
|
+
export interface WebSocketMCPClientOptions {
|
|
26
|
+
url?: string;
|
|
27
|
+
apiKey: string;
|
|
28
|
+
reconnectInterval?: number;
|
|
29
|
+
maxReconnectAttempts?: number;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class WebSocketMCPClient extends EventEmitter {
|
|
33
|
+
private ws;
|
|
34
|
+
private url;
|
|
35
|
+
private apiKey;
|
|
36
|
+
private reconnectInterval;
|
|
37
|
+
private maxReconnectAttempts;
|
|
38
|
+
private timeout;
|
|
39
|
+
private reconnectAttempts;
|
|
40
|
+
private isConnected;
|
|
41
|
+
private messageId;
|
|
42
|
+
private pendingRequests;
|
|
43
|
+
constructor(options: WebSocketMCPClientOptions);
|
|
44
|
+
connect(): Promise<void>;
|
|
45
|
+
private handleMessage;
|
|
46
|
+
sendRequest(method: string, params?: MCPParams): Promise<MCPResult>;
|
|
47
|
+
listTools(): Promise<MCPResult>;
|
|
48
|
+
callTool(name: string, arguments_?: MCPParams): Promise<MCPResult>;
|
|
49
|
+
deleteMemory(id: string): Promise<MCPResult>;
|
|
50
|
+
searchMemories(args: MCPParams): Promise<MCPResult>;
|
|
51
|
+
listResources(): Promise<MCPResult>;
|
|
52
|
+
getMemories(query?: string): Promise<MCPResult>;
|
|
53
|
+
disconnect(): void;
|
|
54
|
+
getConnectionStatus(): {
|
|
55
|
+
connected: boolean;
|
|
56
|
+
url: string;
|
|
57
|
+
reconnectAttempts: number;
|
|
58
|
+
pendingRequests: number;
|
|
59
|
+
};
|
|
60
|
+
}
|