@lanonasis/cli 3.2.14 → 3.4.15
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 +57 -13
- package/dist/commands/auth.js +7 -7
- package/dist/commands/completion.js +2 -0
- package/dist/commands/config.js +4 -4
- package/dist/commands/enhanced-memory.js +1 -1
- package/dist/commands/mcp.js +15 -9
- package/dist/core/achievements.js +1 -1
- package/dist/core/power-mode.js +5 -3
- package/dist/core/welcome.js +7 -6
- package/dist/enhanced-cli.js +6 -3
- package/dist/index-simple.js +5 -1
- package/dist/index.js +5 -1
- package/dist/mcp/access-control.d.ts +1 -1
- package/dist/mcp/access-control.js +4 -4
- package/dist/mcp/client/enhanced-client.js +4 -5
- package/dist/mcp/schemas/tool-schemas.d.ts +1 -1
- package/dist/mcp/schemas/tool-schemas.js +1 -1
- package/dist/mcp/server/lanonasis-server.d.ts +2 -1
- package/dist/mcp/server/lanonasis-server.js +7 -5
- package/dist/mcp/transports/transport-manager.js +3 -3
- package/dist/mcp-server.js +3 -3
- package/dist/utils/config.js +59 -10
- package/dist/utils/mcp-client.d.ts +4 -2
- package/dist/utils/mcp-client.js +86 -42
- package/package.json +3 -3
- package/dist/__tests__/auth-persistence.test.d.ts +0 -1
- package/dist/__tests__/auth-persistence.test.js +0 -243
- package/dist/__tests__/cross-device-integration.test.d.ts +0 -1
- package/dist/__tests__/cross-device-integration.test.js +0 -305
- package/dist/__tests__/mcp-connection-reliability.test.d.ts +0 -1
- package/dist/__tests__/mcp-connection-reliability.test.js +0 -489
- package/dist/__tests__/setup.d.ts +0 -1
- package/dist/__tests__/setup.js +0 -26
- package/dist/mcp/server/mcp/server/lanonasis-server.js +0 -911
- package/dist/mcp/server/utils/api.js +0 -431
- package/dist/mcp/server/utils/config.js +0 -855
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
|
|
2
|
-
import { CLIConfig } from '../utils/config.js';
|
|
3
|
-
import * as fs from 'fs/promises';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
|
-
// Mock dependencies
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
-
const mockAxios = {
|
|
9
|
-
get: jest.fn(),
|
|
10
|
-
post: jest.fn()
|
|
11
|
-
};
|
|
12
|
-
jest.mock('axios', () => ({
|
|
13
|
-
default: mockAxios,
|
|
14
|
-
get: mockAxios.get,
|
|
15
|
-
post: mockAxios.post
|
|
16
|
-
}));
|
|
17
|
-
jest.mock('eventsource');
|
|
18
|
-
jest.mock('ws');
|
|
19
|
-
jest.mock('@modelcontextprotocol/sdk/client/index.js', () => ({
|
|
20
|
-
Client: jest.fn().mockImplementation(() => ({
|
|
21
|
-
connect: jest.fn(),
|
|
22
|
-
close: jest.fn(),
|
|
23
|
-
callTool: jest.fn(),
|
|
24
|
-
listTools: jest.fn()
|
|
25
|
-
}))
|
|
26
|
-
}));
|
|
27
|
-
jest.mock('@modelcontextprotocol/sdk/client/stdio.js', () => ({
|
|
28
|
-
StdioClientTransport: jest.fn()
|
|
29
|
-
}));
|
|
30
|
-
describe('Cross-Device Integration Tests', () => {
|
|
31
|
-
let device1Config;
|
|
32
|
-
let device2Config;
|
|
33
|
-
let device3Config;
|
|
34
|
-
let device1Dir;
|
|
35
|
-
let device2Dir;
|
|
36
|
-
let device3Dir;
|
|
37
|
-
beforeEach(async () => {
|
|
38
|
-
// Create separate test directories for each "device"
|
|
39
|
-
device1Dir = path.join(os.tmpdir(), `test-device1-${Date.now()}-${Math.random()}`);
|
|
40
|
-
device2Dir = path.join(os.tmpdir(), `test-device2-${Date.now()}-${Math.random()}`);
|
|
41
|
-
device3Dir = path.join(os.tmpdir(), `test-device3-${Date.now()}-${Math.random()}`);
|
|
42
|
-
await fs.mkdir(device1Dir, { recursive: true });
|
|
43
|
-
await fs.mkdir(device2Dir, { recursive: true });
|
|
44
|
-
await fs.mkdir(device3Dir, { recursive: true });
|
|
45
|
-
// Create config instances for each "device"
|
|
46
|
-
device1Config = new CLIConfig();
|
|
47
|
-
device1Config.configDir = device1Dir;
|
|
48
|
-
device1Config.configPath = path.join(device1Dir, 'config.json');
|
|
49
|
-
device1Config.lockFile = path.join(device1Dir, 'config.lock');
|
|
50
|
-
device2Config = new CLIConfig();
|
|
51
|
-
device2Config.configDir = device2Dir;
|
|
52
|
-
device2Config.configPath = path.join(device2Dir, 'config.json');
|
|
53
|
-
device2Config.lockFile = path.join(device2Dir, 'config.lock');
|
|
54
|
-
device3Config = new CLIConfig();
|
|
55
|
-
device3Config.configDir = device3Dir;
|
|
56
|
-
device3Config.configPath = path.join(device3Dir, 'config.json');
|
|
57
|
-
device3Config.lockFile = path.join(device3Dir, 'config.lock');
|
|
58
|
-
// Initialize all configs
|
|
59
|
-
await device1Config.init();
|
|
60
|
-
await device2Config.init();
|
|
61
|
-
await device3Config.init();
|
|
62
|
-
// Clear axios mocks
|
|
63
|
-
mockAxios.get.mockClear();
|
|
64
|
-
mockAxios.post.mockClear();
|
|
65
|
-
});
|
|
66
|
-
afterEach(async () => {
|
|
67
|
-
// Clean up all test directories
|
|
68
|
-
try {
|
|
69
|
-
await fs.rm(device1Dir, { recursive: true, force: true });
|
|
70
|
-
await fs.rm(device2Dir, { recursive: true, force: true });
|
|
71
|
-
await fs.rm(device3Dir, { recursive: true, force: true });
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Ignore cleanup errors
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
describe('Same Credentials Working on Multiple Simulated Devices', () => {
|
|
78
|
-
it('should allow same vendor key to work on multiple devices', async () => {
|
|
79
|
-
const sharedVendorKey = 'pk_shared123456789.sk_shared123456789012345';
|
|
80
|
-
// Mock successful server validation for all devices
|
|
81
|
-
mockAxios.get.mockResolvedValue({ status: 200, data: { status: 'ok' } });
|
|
82
|
-
// Set same vendor key on all devices
|
|
83
|
-
await device1Config.setVendorKey(sharedVendorKey);
|
|
84
|
-
await device2Config.setVendorKey(sharedVendorKey);
|
|
85
|
-
await device3Config.setVendorKey(sharedVendorKey);
|
|
86
|
-
// Verify all devices have the same vendor key
|
|
87
|
-
expect(device1Config.getVendorKey()).toBe(sharedVendorKey);
|
|
88
|
-
expect(device2Config.getVendorKey()).toBe(sharedVendorKey);
|
|
89
|
-
expect(device3Config.getVendorKey()).toBe(sharedVendorKey);
|
|
90
|
-
// Verify all devices have same auth method
|
|
91
|
-
expect(device1Config.get('authMethod')).toBe('vendor_key');
|
|
92
|
-
expect(device2Config.get('authMethod')).toBe('vendor_key');
|
|
93
|
-
expect(device3Config.get('authMethod')).toBe('vendor_key');
|
|
94
|
-
// Verify server validation was called for each device
|
|
95
|
-
expect(mockAxios.get).toHaveBeenCalledTimes(3);
|
|
96
|
-
});
|
|
97
|
-
it('should maintain separate device IDs while sharing credentials', async () => {
|
|
98
|
-
const sharedVendorKey = 'pk_shared123456789.sk_shared123456789012345';
|
|
99
|
-
// Mock successful server validation
|
|
100
|
-
mockAxios.get.mockResolvedValue({ status: 200, data: { status: 'ok' } });
|
|
101
|
-
// Set same vendor key on all devices
|
|
102
|
-
await device1Config.setVendorKey(sharedVendorKey);
|
|
103
|
-
await device2Config.setVendorKey(sharedVendorKey);
|
|
104
|
-
await device3Config.setVendorKey(sharedVendorKey);
|
|
105
|
-
// Get device IDs
|
|
106
|
-
const deviceId1 = await device1Config.getDeviceId();
|
|
107
|
-
const deviceId2 = await device2Config.getDeviceId();
|
|
108
|
-
const deviceId3 = await device3Config.getDeviceId();
|
|
109
|
-
// Device IDs should be different
|
|
110
|
-
expect(deviceId1).not.toBe(deviceId2);
|
|
111
|
-
expect(deviceId1).not.toBe(deviceId3);
|
|
112
|
-
expect(deviceId2).not.toBe(deviceId3);
|
|
113
|
-
// But credentials should be the same
|
|
114
|
-
expect(device1Config.getVendorKey()).toBe(sharedVendorKey);
|
|
115
|
-
expect(device2Config.getVendorKey()).toBe(sharedVendorKey);
|
|
116
|
-
expect(device3Config.getVendorKey()).toBe(sharedVendorKey);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
describe('Service Discovery Consistency Across Environments', () => {
|
|
120
|
-
it('should discover same service endpoints from all devices', async () => {
|
|
121
|
-
// Mock service discovery response
|
|
122
|
-
const mockDiscoveryResponse = {
|
|
123
|
-
auth: { login: 'https://api.lanonasis.com/auth/login' },
|
|
124
|
-
endpoints: {
|
|
125
|
-
http: 'https://mcp.lanonasis.com/api/v1',
|
|
126
|
-
websocket: 'wss://mcp.lanonasis.com/ws',
|
|
127
|
-
sse: 'https://mcp.lanonasis.com/api/v1/events'
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
mockAxios.get.mockResolvedValue({ data: mockDiscoveryResponse });
|
|
131
|
-
// Perform service discovery on all devices
|
|
132
|
-
await device1Config.discoverServices();
|
|
133
|
-
await device2Config.discoverServices();
|
|
134
|
-
await device3Config.discoverServices();
|
|
135
|
-
// All devices should have discovered the same endpoints
|
|
136
|
-
const services1 = device1Config.get('discoveredServices');
|
|
137
|
-
const services2 = device2Config.get('discoveredServices');
|
|
138
|
-
const services3 = device3Config.get('discoveredServices');
|
|
139
|
-
expect(services1).toEqual(services2);
|
|
140
|
-
expect(services1).toEqual(services3);
|
|
141
|
-
expect(services2).toEqual(services3);
|
|
142
|
-
// Verify specific endpoints
|
|
143
|
-
expect(services1.mcp_base).toBe('https://mcp.lanonasis.com/api/v1');
|
|
144
|
-
expect(services1.mcp_ws_base).toBe('wss://mcp.lanonasis.com/ws');
|
|
145
|
-
expect(services1.mcp_sse_base).toBe('https://mcp.lanonasis.com/api/v1/events');
|
|
146
|
-
});
|
|
147
|
-
it('should handle service discovery failures consistently', async () => {
|
|
148
|
-
// Mock service discovery failure
|
|
149
|
-
mockAxios.get.mockRejectedValue(new Error('Service discovery failed'));
|
|
150
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
151
|
-
// Attempt service discovery on all devices
|
|
152
|
-
await device1Config.discoverServices(true);
|
|
153
|
-
await device2Config.discoverServices(true);
|
|
154
|
-
await device3Config.discoverServices(true);
|
|
155
|
-
// All devices should fall back to same default endpoints
|
|
156
|
-
const services1 = device1Config.get('discoveredServices');
|
|
157
|
-
const services2 = device2Config.get('discoveredServices');
|
|
158
|
-
const services3 = device3Config.get('discoveredServices');
|
|
159
|
-
expect(services1).toEqual(services2);
|
|
160
|
-
expect(services1).toEqual(services3);
|
|
161
|
-
// Should use fallback endpoints
|
|
162
|
-
expect(services1.auth_base).toBe('https://api.lanonasis.com');
|
|
163
|
-
expect(services1.mcp_base).toBe('https://mcp.lanonasis.com/api/v1');
|
|
164
|
-
expect(services1.mcp_ws_base).toBe('wss://mcp.lanonasis.com/ws');
|
|
165
|
-
consoleSpy.mockRestore();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
describe('Configuration Synchronization and Compatibility', () => {
|
|
169
|
-
it('should maintain configuration version compatibility across devices', async () => {
|
|
170
|
-
// All devices should have the same config version
|
|
171
|
-
expect(device1Config.get('version')).toBe('1.0.0');
|
|
172
|
-
expect(device2Config.get('version')).toBe('1.0.0');
|
|
173
|
-
expect(device3Config.get('version')).toBe('1.0.0');
|
|
174
|
-
// Set some data on device 1
|
|
175
|
-
await device1Config.setAndSave('testData', 'test-value');
|
|
176
|
-
// Read config file directly and apply to device 2
|
|
177
|
-
const configPath1 = device1Config.configPath;
|
|
178
|
-
const configData = JSON.parse(await fs.readFile(configPath1, 'utf-8'));
|
|
179
|
-
const configPath2 = device2Config.configPath;
|
|
180
|
-
await fs.writeFile(configPath2, JSON.stringify(configData, null, 2));
|
|
181
|
-
// Reload device 2 config
|
|
182
|
-
await device2Config.load();
|
|
183
|
-
// Device 2 should have the same data and version
|
|
184
|
-
expect(device2Config.get('testData')).toBe('test-value');
|
|
185
|
-
expect(device2Config.get('version')).toBe('1.0.0');
|
|
186
|
-
});
|
|
187
|
-
it('should create consistent backup files across devices', async () => {
|
|
188
|
-
const testData = { test: 'backup-data', timestamp: Date.now() };
|
|
189
|
-
// Set data on all devices
|
|
190
|
-
await device1Config.setAndSave('backupTest', testData);
|
|
191
|
-
await device2Config.setAndSave('backupTest', testData);
|
|
192
|
-
await device3Config.setAndSave('backupTest', testData);
|
|
193
|
-
// Create backups on all devices
|
|
194
|
-
const backup1 = await device1Config.backupConfig();
|
|
195
|
-
const backup2 = await device2Config.backupConfig();
|
|
196
|
-
const backup3 = await device3Config.backupConfig();
|
|
197
|
-
// All backups should exist
|
|
198
|
-
expect(await fs.access(backup1).then(() => true).catch(() => false)).toBe(true);
|
|
199
|
-
expect(await fs.access(backup2).then(() => true).catch(() => false)).toBe(true);
|
|
200
|
-
expect(await fs.access(backup3).then(() => true).catch(() => false)).toBe(true);
|
|
201
|
-
// All backups should contain the same data
|
|
202
|
-
const backupData1 = JSON.parse(await fs.readFile(backup1, 'utf-8'));
|
|
203
|
-
const backupData2 = JSON.parse(await fs.readFile(backup2, 'utf-8'));
|
|
204
|
-
const backupData3 = JSON.parse(await fs.readFile(backup3, 'utf-8'));
|
|
205
|
-
expect(backupData1.backupTest).toEqual(testData);
|
|
206
|
-
expect(backupData2.backupTest).toEqual(testData);
|
|
207
|
-
expect(backupData3.backupTest).toEqual(testData);
|
|
208
|
-
// All should have same version
|
|
209
|
-
expect(backupData1.version).toBe('1.0.0');
|
|
210
|
-
expect(backupData2.version).toBe('1.0.0');
|
|
211
|
-
expect(backupData3.version).toBe('1.0.0');
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
describe('Error Message Consistency Across Different Failure Scenarios', () => {
|
|
215
|
-
it('should provide consistent error messages for authentication failures', async () => {
|
|
216
|
-
const invalidVendorKey = 'pk_invalid.sk_invalid';
|
|
217
|
-
// Mock authentication failure
|
|
218
|
-
mockAxios.get.mockRejectedValue({
|
|
219
|
-
response: { status: 401, data: { error: 'invalid vendor key' } }
|
|
220
|
-
});
|
|
221
|
-
// Attempt to set invalid vendor key on all devices
|
|
222
|
-
const errors = [];
|
|
223
|
-
try {
|
|
224
|
-
await device1Config.setVendorKey(invalidVendorKey);
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
errors.push(error.message);
|
|
228
|
-
}
|
|
229
|
-
try {
|
|
230
|
-
await device2Config.setVendorKey(invalidVendorKey);
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
errors.push(error.message);
|
|
234
|
-
}
|
|
235
|
-
try {
|
|
236
|
-
await device3Config.setVendorKey(invalidVendorKey);
|
|
237
|
-
}
|
|
238
|
-
catch (error) {
|
|
239
|
-
errors.push(error.message);
|
|
240
|
-
}
|
|
241
|
-
// All devices should get the same error message
|
|
242
|
-
expect(errors).toHaveLength(3);
|
|
243
|
-
expect(errors[0]).toBe(errors[1]);
|
|
244
|
-
expect(errors[0]).toBe(errors[2]);
|
|
245
|
-
expect(errors[0]).toContain('Vendor key authentication failed');
|
|
246
|
-
});
|
|
247
|
-
it('should provide consistent validation error messages', async () => {
|
|
248
|
-
const invalidFormats = [
|
|
249
|
-
'invalid-key',
|
|
250
|
-
'pk_short.sk_short',
|
|
251
|
-
'pk_.sk_test123456789012345',
|
|
252
|
-
'pk_test123456789.sk_'
|
|
253
|
-
];
|
|
254
|
-
for (const invalidKey of invalidFormats) {
|
|
255
|
-
const validationResults = [];
|
|
256
|
-
// Test validation on all devices
|
|
257
|
-
validationResults.push(device1Config.validateVendorKeyFormat(invalidKey));
|
|
258
|
-
validationResults.push(device2Config.validateVendorKeyFormat(invalidKey));
|
|
259
|
-
validationResults.push(device3Config.validateVendorKeyFormat(invalidKey));
|
|
260
|
-
// All devices should return the same validation error
|
|
261
|
-
expect(validationResults[0]).toBe(validationResults[1]);
|
|
262
|
-
expect(validationResults[0]).toBe(validationResults[2]);
|
|
263
|
-
expect(typeof validationResults[0]).toBe('string'); // Should be error message
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
it('should maintain consistent failure tracking across devices', async () => {
|
|
267
|
-
const sharedVendorKey = 'pk_shared123456789.sk_shared123456789012345';
|
|
268
|
-
// Mock successful initial setup
|
|
269
|
-
mockAxios.get.mockResolvedValue({ status: 200, data: { status: 'ok' } });
|
|
270
|
-
// Set vendor key on all devices
|
|
271
|
-
await device1Config.setVendorKey(sharedVendorKey);
|
|
272
|
-
await device2Config.setVendorKey(sharedVendorKey);
|
|
273
|
-
await device3Config.setVendorKey(sharedVendorKey);
|
|
274
|
-
// Mock validation failure
|
|
275
|
-
mockAxios.get.mockRejectedValue({
|
|
276
|
-
response: { status: 401, data: { error: 'invalid credentials' } }
|
|
277
|
-
});
|
|
278
|
-
// Validate credentials on all devices (should fail)
|
|
279
|
-
await device1Config.validateStoredCredentials();
|
|
280
|
-
await device2Config.validateStoredCredentials();
|
|
281
|
-
await device3Config.validateStoredCredentials();
|
|
282
|
-
// All devices should have incremented failure count
|
|
283
|
-
expect(device1Config.getFailureCount()).toBe(1);
|
|
284
|
-
expect(device2Config.getFailureCount()).toBe(1);
|
|
285
|
-
expect(device3Config.getFailureCount()).toBe(1);
|
|
286
|
-
// Add more failures to test delay calculation consistency
|
|
287
|
-
await device1Config.incrementFailureCount();
|
|
288
|
-
await device1Config.incrementFailureCount();
|
|
289
|
-
await device2Config.incrementFailureCount();
|
|
290
|
-
await device2Config.incrementFailureCount();
|
|
291
|
-
await device3Config.incrementFailureCount();
|
|
292
|
-
await device3Config.incrementFailureCount();
|
|
293
|
-
// All devices should have same failure count and delay
|
|
294
|
-
expect(device1Config.getFailureCount()).toBe(3);
|
|
295
|
-
expect(device2Config.getFailureCount()).toBe(3);
|
|
296
|
-
expect(device3Config.getFailureCount()).toBe(3);
|
|
297
|
-
expect(device1Config.shouldDelayAuth()).toBe(true);
|
|
298
|
-
expect(device2Config.shouldDelayAuth()).toBe(true);
|
|
299
|
-
expect(device3Config.shouldDelayAuth()).toBe(true);
|
|
300
|
-
expect(device1Config.getAuthDelayMs()).toBe(2000);
|
|
301
|
-
expect(device2Config.getAuthDelayMs()).toBe(2000);
|
|
302
|
-
expect(device3Config.getAuthDelayMs()).toBe(2000);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|