@lanonasis/cli 3.6.5 → 3.7.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 +19 -2
- package/dist/commands/api-keys.d.ts +2 -1
- package/dist/commands/api-keys.js +73 -78
- package/dist/commands/auth.js +244 -177
- package/dist/commands/completion.js +31 -39
- package/dist/commands/config.js +162 -201
- package/dist/commands/enhanced-memory.js +11 -17
- package/dist/commands/guide.js +79 -88
- package/dist/commands/init.js +14 -20
- package/dist/commands/mcp.d.ts +10 -0
- package/dist/commands/mcp.js +215 -156
- package/dist/commands/memory.js +77 -83
- package/dist/commands/organization.js +15 -21
- package/dist/commands/topics.js +52 -58
- package/dist/core/achievements.js +19 -26
- package/dist/core/architecture.js +42 -59
- package/dist/core/dashboard.js +71 -81
- package/dist/core/error-handler.js +30 -39
- package/dist/core/power-mode.js +46 -53
- package/dist/core/progress.js +35 -44
- package/dist/core/welcome.js +56 -64
- package/dist/enhanced-cli.js +49 -58
- package/dist/index-simple.js +75 -113
- package/dist/index.js +64 -69
- package/dist/mcp/access-control.js +13 -17
- package/dist/mcp/client/enhanced-client.js +17 -28
- package/dist/mcp/enhanced-server.js +10 -14
- package/dist/mcp/logger.js +3 -7
- package/dist/mcp/memory-state.js +13 -17
- package/dist/mcp/schemas/tool-schemas.d.ts +16 -16
- package/dist/mcp/schemas/tool-schemas.js +122 -126
- package/dist/mcp/server/lanonasis-server.js +66 -57
- package/dist/mcp/transports/transport-manager.js +18 -25
- package/dist/mcp/vector-store.js +6 -10
- package/dist/mcp-server.js +23 -27
- package/dist/utils/api.js +21 -27
- package/dist/utils/config.d.ts +2 -1
- package/dist/utils/config.js +65 -78
- package/dist/utils/formatting.js +6 -14
- package/dist/utils/hash-utils.d.ts +23 -0
- package/dist/utils/hash-utils.js +37 -0
- package/dist/utils/mcp-client.js +76 -117
- package/package.json +36 -5
package/dist/commands/auth.js
CHANGED
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const open_1 = __importDefault(require("open"));
|
|
12
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
13
|
-
const http_1 = __importDefault(require("http"));
|
|
14
|
-
const url_1 = __importDefault(require("url"));
|
|
15
|
-
const api_js_1 = require("../utils/api.js");
|
|
16
|
-
const config_js_1 = require("../utils/config.js");
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import http from 'http';
|
|
7
|
+
import url from 'url';
|
|
8
|
+
import axios from 'axios';
|
|
9
|
+
import { apiClient } from '../utils/api.js';
|
|
10
|
+
import { CLIConfig } from '../utils/config.js';
|
|
17
11
|
// Color scheme
|
|
18
12
|
const colors = {
|
|
19
|
-
primary:
|
|
20
|
-
success:
|
|
21
|
-
warning:
|
|
22
|
-
error:
|
|
23
|
-
info:
|
|
24
|
-
accent:
|
|
25
|
-
muted:
|
|
26
|
-
highlight:
|
|
13
|
+
primary: chalk.blue.bold,
|
|
14
|
+
success: chalk.green,
|
|
15
|
+
warning: chalk.yellow,
|
|
16
|
+
error: chalk.red,
|
|
17
|
+
info: chalk.cyan,
|
|
18
|
+
accent: chalk.magenta,
|
|
19
|
+
muted: chalk.gray,
|
|
20
|
+
highlight: chalk.white.bold
|
|
27
21
|
};
|
|
28
22
|
// Helper function to handle authentication delays
|
|
29
23
|
async function handleAuthDelay(config) {
|
|
@@ -32,15 +26,15 @@ async function handleAuthDelay(config) {
|
|
|
32
26
|
const failureCount = config.getFailureCount();
|
|
33
27
|
const lastFailure = config.getLastAuthFailure();
|
|
34
28
|
console.log();
|
|
35
|
-
console.log(
|
|
29
|
+
console.log(chalk.yellow(`⚠️ Multiple authentication failures detected (${failureCount} attempts)`));
|
|
36
30
|
if (lastFailure) {
|
|
37
31
|
const lastFailureDate = new Date(lastFailure);
|
|
38
|
-
console.log(
|
|
32
|
+
console.log(chalk.gray(`Last failure: ${lastFailureDate.toLocaleString()}`));
|
|
39
33
|
}
|
|
40
|
-
console.log(
|
|
41
|
-
console.log(
|
|
34
|
+
console.log(chalk.yellow(`Waiting ${Math.round(delayMs / 1000)} seconds before retry...`));
|
|
35
|
+
console.log(chalk.gray('This delay helps prevent account lockouts and reduces server load.'));
|
|
42
36
|
// Show countdown
|
|
43
|
-
const spinner = (
|
|
37
|
+
const spinner = ora(`Waiting ${Math.round(delayMs / 1000)} seconds...`).start();
|
|
44
38
|
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
45
39
|
spinner.succeed('Ready to retry authentication');
|
|
46
40
|
console.log();
|
|
@@ -54,70 +48,70 @@ async function handleAuthenticationFailure(error, config, authMethod = 'jwt') {
|
|
|
54
48
|
// Determine error type and provide specific guidance
|
|
55
49
|
const errorType = categorizeAuthError(error);
|
|
56
50
|
console.log();
|
|
57
|
-
console.log(
|
|
51
|
+
console.log(chalk.red('✖ Authentication failed'));
|
|
58
52
|
switch (errorType) {
|
|
59
53
|
case 'invalid_credentials':
|
|
60
|
-
console.log(
|
|
54
|
+
console.log(chalk.red('Invalid credentials provided'));
|
|
61
55
|
if (authMethod === 'vendor_key') {
|
|
62
|
-
console.log(
|
|
63
|
-
console.log(
|
|
64
|
-
console.log(
|
|
56
|
+
console.log(chalk.gray('• Verify the vendor key matches the value shown in your dashboard'));
|
|
57
|
+
console.log(chalk.gray('• Confirm the key is active and has not been revoked'));
|
|
58
|
+
console.log(chalk.gray('• Ensure you copied the entire key without extra spaces'));
|
|
65
59
|
}
|
|
66
60
|
else {
|
|
67
|
-
console.log(
|
|
68
|
-
console.log(
|
|
69
|
-
console.log(
|
|
61
|
+
console.log(chalk.gray('• Double-check your email and password'));
|
|
62
|
+
console.log(chalk.gray('• Passwords are case-sensitive'));
|
|
63
|
+
console.log(chalk.gray('• Consider resetting your password if needed'));
|
|
70
64
|
}
|
|
71
65
|
break;
|
|
72
66
|
case 'network_error':
|
|
73
|
-
console.log(
|
|
74
|
-
console.log(
|
|
75
|
-
console.log(
|
|
76
|
-
console.log(
|
|
67
|
+
console.log(chalk.red('Network connection failed'));
|
|
68
|
+
console.log(chalk.gray('• Check your internet connection'));
|
|
69
|
+
console.log(chalk.gray('• Verify you can access https://auth.lanonasis.com'));
|
|
70
|
+
console.log(chalk.gray('• Try again in a few moments'));
|
|
77
71
|
if (failureCount >= 2) {
|
|
78
|
-
console.log(
|
|
72
|
+
console.log(chalk.gray('• Consider using a different network if issues persist'));
|
|
79
73
|
}
|
|
80
74
|
break;
|
|
81
75
|
case 'server_error':
|
|
82
|
-
console.log(
|
|
83
|
-
console.log(
|
|
84
|
-
console.log(
|
|
85
|
-
console.log(
|
|
76
|
+
console.log(chalk.red('Server temporarily unavailable'));
|
|
77
|
+
console.log(chalk.gray('• The authentication service may be experiencing issues'));
|
|
78
|
+
console.log(chalk.gray('• Please try again in a few minutes'));
|
|
79
|
+
console.log(chalk.gray('• Check https://status.lanonasis.com for service status'));
|
|
86
80
|
break;
|
|
87
81
|
case 'rate_limited':
|
|
88
|
-
console.log(
|
|
89
|
-
console.log(
|
|
90
|
-
console.log(
|
|
91
|
-
console.log(
|
|
82
|
+
console.log(chalk.red('Too many authentication attempts'));
|
|
83
|
+
console.log(chalk.gray('• Please wait before trying again'));
|
|
84
|
+
console.log(chalk.gray('• Rate limiting helps protect your account'));
|
|
85
|
+
console.log(chalk.gray('• Consider using a vendor key for automated access'));
|
|
92
86
|
break;
|
|
93
87
|
case 'expired_token':
|
|
94
|
-
console.log(
|
|
95
|
-
console.log(
|
|
96
|
-
console.log(
|
|
88
|
+
console.log(chalk.red('Authentication token has expired'));
|
|
89
|
+
console.log(chalk.gray('• Please log in again to refresh your session'));
|
|
90
|
+
console.log(chalk.gray('• Consider using a vendor key for longer-term access'));
|
|
97
91
|
await config.clearInvalidCredentials();
|
|
98
92
|
break;
|
|
99
93
|
default:
|
|
100
|
-
console.log(
|
|
101
|
-
console.log(
|
|
102
|
-
console.log(
|
|
94
|
+
console.log(chalk.red(`Unexpected error: ${error.message || 'Unknown error'}`));
|
|
95
|
+
console.log(chalk.gray('• Please try again'));
|
|
96
|
+
console.log(chalk.gray('• If the problem persists, contact support'));
|
|
103
97
|
}
|
|
104
98
|
// Progressive guidance for repeated failures
|
|
105
99
|
if (failureCount >= 3) {
|
|
106
100
|
console.log();
|
|
107
|
-
console.log(
|
|
101
|
+
console.log(chalk.yellow('💡 Multiple failures detected. Recovery options:'));
|
|
108
102
|
if (authMethod === 'vendor_key') {
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
111
|
-
console.log(
|
|
103
|
+
console.log(chalk.cyan('• Generate a new vendor key from your dashboard'));
|
|
104
|
+
console.log(chalk.cyan('• Try: lanonasis auth logout && lanonasis auth login'));
|
|
105
|
+
console.log(chalk.cyan('• Switch to browser login: lanonasis auth login (choose Browser Login)'));
|
|
112
106
|
}
|
|
113
107
|
else {
|
|
114
|
-
console.log(
|
|
115
|
-
console.log(
|
|
116
|
-
console.log(
|
|
108
|
+
console.log(chalk.cyan('• Reset your password if you\'re unsure'));
|
|
109
|
+
console.log(chalk.cyan('• Try vendor key authentication instead'));
|
|
110
|
+
console.log(chalk.cyan('• Clear stored config: lanonasis auth logout'));
|
|
117
111
|
}
|
|
118
112
|
if (failureCount >= 5) {
|
|
119
|
-
console.log(
|
|
120
|
-
console.log(
|
|
113
|
+
console.log(chalk.yellow('• Consider contacting support if issues persist'));
|
|
114
|
+
console.log(chalk.gray('• Include error details and your email address'));
|
|
121
115
|
}
|
|
122
116
|
}
|
|
123
117
|
}
|
|
@@ -181,9 +175,9 @@ function categorizeAuthError(error) {
|
|
|
181
175
|
*/
|
|
182
176
|
function generatePKCE() {
|
|
183
177
|
// Generate random verifier (43-128 chars, base64url)
|
|
184
|
-
const verifier =
|
|
178
|
+
const verifier = crypto.randomBytes(32).toString('base64url');
|
|
185
179
|
// Generate challenge: base64url(sha256(verifier))
|
|
186
|
-
const challenge =
|
|
180
|
+
const challenge = crypto
|
|
187
181
|
.createHash('sha256')
|
|
188
182
|
.update(verifier)
|
|
189
183
|
.digest('base64url');
|
|
@@ -194,8 +188,8 @@ function generatePKCE() {
|
|
|
194
188
|
*/
|
|
195
189
|
function createCallbackServer(port = 8888) {
|
|
196
190
|
return new Promise((resolve, reject) => {
|
|
197
|
-
const server =
|
|
198
|
-
const parsedUrl =
|
|
191
|
+
const server = http.createServer((req, res) => {
|
|
192
|
+
const parsedUrl = url.parse(req.url, true);
|
|
199
193
|
if (parsedUrl.pathname === '/callback') {
|
|
200
194
|
const { code, state, error, error_description } = parsedUrl.query;
|
|
201
195
|
// Send response to browser
|
|
@@ -237,7 +231,7 @@ function createCallbackServer(port = 8888) {
|
|
|
237
231
|
}
|
|
238
232
|
});
|
|
239
233
|
server.listen(port, () => {
|
|
240
|
-
console.log(
|
|
234
|
+
console.log(chalk.gray(` Local callback server listening on port ${port}`));
|
|
241
235
|
});
|
|
242
236
|
// Timeout after 5 minutes
|
|
243
237
|
setTimeout(() => {
|
|
@@ -249,16 +243,40 @@ function createCallbackServer(port = 8888) {
|
|
|
249
243
|
/**
|
|
250
244
|
* Exchange authorization code for OAuth2 tokens
|
|
251
245
|
*/
|
|
252
|
-
async function exchangeCodeForTokens(code, verifier, authBase) {
|
|
246
|
+
async function exchangeCodeForTokens(code, verifier, authBase, redirectUri) {
|
|
253
247
|
const tokenEndpoint = `${authBase}/oauth/token`;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
248
|
+
try {
|
|
249
|
+
// Use axios directly to have full control over error handling
|
|
250
|
+
const response = await axios.post(tokenEndpoint, {
|
|
251
|
+
grant_type: 'authorization_code',
|
|
252
|
+
code,
|
|
253
|
+
code_verifier: verifier,
|
|
254
|
+
client_id: 'lanonasis-cli',
|
|
255
|
+
redirect_uri: redirectUri
|
|
256
|
+
}, {
|
|
257
|
+
headers: {
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
return response.data;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
// Extract detailed error information from axios error response
|
|
265
|
+
if (error.response) {
|
|
266
|
+
const errorData = error.response.data || {};
|
|
267
|
+
const status = error.response.status;
|
|
268
|
+
const errorMessage = errorData.error_description || errorData.error || error.message || `Request failed with status code ${status}`;
|
|
269
|
+
const details = errorData.details;
|
|
270
|
+
const enhancedError = new Error(errorMessage);
|
|
271
|
+
enhancedError.response = error.response;
|
|
272
|
+
enhancedError.status = status;
|
|
273
|
+
enhancedError.details = details;
|
|
274
|
+
enhancedError.errorData = errorData;
|
|
275
|
+
throw enhancedError;
|
|
276
|
+
}
|
|
277
|
+
// If it's not an axios error, just rethrow
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
262
280
|
}
|
|
263
281
|
/**
|
|
264
282
|
* Refresh OAuth2 access token using refresh token
|
|
@@ -270,7 +288,7 @@ async function refreshOAuth2Token(config) {
|
|
|
270
288
|
}
|
|
271
289
|
try {
|
|
272
290
|
const authBase = config.getDiscoveredApiUrl();
|
|
273
|
-
const response = await
|
|
291
|
+
const response = await apiClient.post(`${authBase}/oauth/token`, {
|
|
274
292
|
grant_type: 'refresh_token',
|
|
275
293
|
refresh_token: refreshToken,
|
|
276
294
|
client_id: 'lanonasis-cli'
|
|
@@ -283,14 +301,14 @@ async function refreshOAuth2Token(config) {
|
|
|
283
301
|
return true;
|
|
284
302
|
}
|
|
285
303
|
catch (error) {
|
|
286
|
-
console.error(
|
|
304
|
+
console.error(chalk.yellow('⚠️ Token refresh failed, please re-authenticate'));
|
|
287
305
|
return false;
|
|
288
306
|
}
|
|
289
307
|
}
|
|
290
|
-
async function diagnoseCommand() {
|
|
291
|
-
const config = new
|
|
308
|
+
export async function diagnoseCommand() {
|
|
309
|
+
const config = new CLIConfig();
|
|
292
310
|
await config.init();
|
|
293
|
-
console.log(
|
|
311
|
+
console.log(chalk.blue.bold('🔍 Authentication Diagnostic'));
|
|
294
312
|
console.log(colors.info('━'.repeat(50)));
|
|
295
313
|
console.log();
|
|
296
314
|
const diagnostics = {
|
|
@@ -306,146 +324,146 @@ async function diagnoseCommand() {
|
|
|
306
324
|
deviceId: null
|
|
307
325
|
};
|
|
308
326
|
// Step 1: Check if config exists
|
|
309
|
-
console.log(
|
|
327
|
+
console.log(chalk.cyan('1. Configuration File'));
|
|
310
328
|
try {
|
|
311
329
|
const configExists = await config.exists();
|
|
312
330
|
diagnostics.configExists = configExists;
|
|
313
331
|
if (configExists) {
|
|
314
|
-
console.log(
|
|
332
|
+
console.log(chalk.green(' ✓ Config file exists at'), config.getConfigPath());
|
|
315
333
|
}
|
|
316
334
|
else {
|
|
317
|
-
console.log(
|
|
318
|
-
console.log(
|
|
335
|
+
console.log(chalk.red(' ✖ Config file not found at'), config.getConfigPath());
|
|
336
|
+
console.log(chalk.gray(' → Run: lanonasis auth login'));
|
|
319
337
|
}
|
|
320
338
|
}
|
|
321
339
|
catch (error) {
|
|
322
|
-
console.log(
|
|
340
|
+
console.log(chalk.red(' ✖ Error checking config:'), error instanceof Error ? error.message : 'Unknown error');
|
|
323
341
|
}
|
|
324
342
|
// Step 2: Check stored credentials
|
|
325
|
-
console.log(
|
|
343
|
+
console.log(chalk.cyan('\n2. Stored Credentials'));
|
|
326
344
|
const token = config.getToken();
|
|
327
345
|
const vendorKey = config.getVendorKey();
|
|
328
346
|
const authMethod = config.get('authMethod');
|
|
329
347
|
if (vendorKey) {
|
|
330
348
|
diagnostics.hasCredentials = true;
|
|
331
349
|
diagnostics.credentialType = 'vendor_key';
|
|
332
|
-
console.log(
|
|
350
|
+
console.log(chalk.green(' ✓ Vendor key found'));
|
|
333
351
|
// Validate vendor key presence
|
|
334
352
|
const formatValidation = config.validateVendorKeyFormat(vendorKey);
|
|
335
353
|
if (formatValidation !== true) {
|
|
336
|
-
console.log(
|
|
354
|
+
console.log(chalk.red(` ✖ Vendor key issue: ${formatValidation}`));
|
|
337
355
|
}
|
|
338
356
|
}
|
|
339
357
|
else if (token) {
|
|
340
358
|
diagnostics.hasCredentials = true;
|
|
341
359
|
diagnostics.credentialType = authMethod === 'oauth' ? 'oauth' : 'jwt';
|
|
342
|
-
console.log(
|
|
360
|
+
console.log(chalk.green(` ✓ ${diagnostics.credentialType.toUpperCase()} token found`));
|
|
343
361
|
// Check token expiry
|
|
344
362
|
try {
|
|
345
363
|
const isAuth = await config.isAuthenticated();
|
|
346
364
|
if (!isAuth) {
|
|
347
365
|
diagnostics.tokenExpired = true;
|
|
348
|
-
console.log(
|
|
366
|
+
console.log(chalk.red(' ✖ Token is expired'));
|
|
349
367
|
}
|
|
350
368
|
else {
|
|
351
|
-
console.log(
|
|
369
|
+
console.log(chalk.green(' ✓ Token is not expired'));
|
|
352
370
|
}
|
|
353
371
|
}
|
|
354
372
|
catch (error) {
|
|
355
|
-
console.log(
|
|
373
|
+
console.log(chalk.yellow(' ⚠ Could not validate token expiry'));
|
|
356
374
|
if (process.env.CLI_VERBOSE === 'true' && error instanceof Error) {
|
|
357
|
-
console.log(
|
|
375
|
+
console.log(chalk.gray(` ${error.message}`));
|
|
358
376
|
}
|
|
359
377
|
}
|
|
360
378
|
}
|
|
361
379
|
else {
|
|
362
|
-
console.log(
|
|
363
|
-
console.log(
|
|
380
|
+
console.log(chalk.red(' ✖ No credentials found'));
|
|
381
|
+
console.log(chalk.gray(' → Run: lanonasis auth login'));
|
|
364
382
|
}
|
|
365
383
|
// Step 3: Check authentication failures
|
|
366
|
-
console.log(
|
|
384
|
+
console.log(chalk.cyan('\n3. Authentication History'));
|
|
367
385
|
diagnostics.authFailures = config.getFailureCount();
|
|
368
386
|
diagnostics.lastFailure = config.getLastAuthFailure() ?? null;
|
|
369
387
|
if (diagnostics.authFailures === 0) {
|
|
370
|
-
console.log(
|
|
388
|
+
console.log(chalk.green(' ✓ No recent authentication failures'));
|
|
371
389
|
}
|
|
372
390
|
else {
|
|
373
|
-
console.log(
|
|
391
|
+
console.log(chalk.yellow(` ⚠ ${diagnostics.authFailures} recent authentication failures`));
|
|
374
392
|
if (diagnostics.lastFailure) {
|
|
375
393
|
const lastFailureDate = new Date(diagnostics.lastFailure);
|
|
376
|
-
console.log(
|
|
394
|
+
console.log(chalk.gray(` Last failure: ${lastFailureDate.toLocaleString()}`));
|
|
377
395
|
}
|
|
378
396
|
if (config.shouldDelayAuth()) {
|
|
379
397
|
const delayMs = config.getAuthDelayMs();
|
|
380
|
-
console.log(
|
|
398
|
+
console.log(chalk.yellow(` ⚠ Authentication delay active: ${Math.round(delayMs / 1000)}s`));
|
|
381
399
|
}
|
|
382
400
|
}
|
|
383
401
|
// Step 4: Test credential validation against server
|
|
384
|
-
console.log(
|
|
402
|
+
console.log(chalk.cyan('\n4. Server Validation'));
|
|
385
403
|
if (diagnostics.hasCredentials) {
|
|
386
|
-
const spinner = (
|
|
404
|
+
const spinner = ora('Testing credentials against server...').start();
|
|
387
405
|
try {
|
|
388
406
|
const isValid = await config.validateStoredCredentials();
|
|
389
407
|
diagnostics.credentialsValid = isValid;
|
|
390
408
|
if (isValid) {
|
|
391
409
|
spinner.succeed('Credentials are valid');
|
|
392
|
-
console.log(
|
|
410
|
+
console.log(chalk.green(' ✓ Server authentication successful'));
|
|
393
411
|
}
|
|
394
412
|
else {
|
|
395
413
|
spinner.fail('Credentials are invalid');
|
|
396
|
-
console.log(
|
|
397
|
-
console.log(
|
|
414
|
+
console.log(chalk.red(' ✖ Server rejected credentials'));
|
|
415
|
+
console.log(chalk.gray(' → Try: lanonasis auth login'));
|
|
398
416
|
}
|
|
399
417
|
}
|
|
400
418
|
catch (error) {
|
|
401
419
|
spinner.fail('Server validation failed');
|
|
402
|
-
console.log(
|
|
403
|
-
console.log(
|
|
420
|
+
console.log(chalk.red(' ✖ Could not validate with server:'));
|
|
421
|
+
console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
404
422
|
}
|
|
405
423
|
}
|
|
406
424
|
else {
|
|
407
|
-
console.log(
|
|
425
|
+
console.log(chalk.gray(' - Skipped (no credentials to validate)'));
|
|
408
426
|
}
|
|
409
427
|
// Step 5: Test endpoint connectivity
|
|
410
|
-
console.log(
|
|
411
|
-
const spinner2 = (
|
|
428
|
+
console.log(chalk.cyan('\n5. Endpoint Connectivity'));
|
|
429
|
+
const spinner2 = ora('Testing authentication endpoints...').start();
|
|
412
430
|
try {
|
|
413
431
|
await config.discoverServices();
|
|
414
432
|
diagnostics.serviceDiscovery = true;
|
|
415
433
|
const services = config.get('discoveredServices');
|
|
416
434
|
if (services) {
|
|
417
435
|
spinner2.succeed('Authentication endpoints reachable');
|
|
418
|
-
console.log(
|
|
419
|
-
console.log(
|
|
436
|
+
console.log(chalk.green(' ✓ Service discovery successful'));
|
|
437
|
+
console.log(chalk.gray(` Auth endpoint: ${services.auth_base}`));
|
|
420
438
|
diagnostics.endpointsReachable = true;
|
|
421
439
|
}
|
|
422
440
|
else {
|
|
423
441
|
spinner2.warn('Using fallback endpoints');
|
|
424
|
-
console.log(
|
|
442
|
+
console.log(chalk.yellow(' ⚠ Service discovery failed, using fallbacks'));
|
|
425
443
|
diagnostics.endpointsReachable = true; // Fallbacks still work
|
|
426
444
|
}
|
|
427
445
|
}
|
|
428
446
|
catch (error) {
|
|
429
447
|
spinner2.fail('Endpoint connectivity failed');
|
|
430
|
-
console.log(
|
|
431
|
-
console.log(
|
|
432
|
-
console.log(
|
|
448
|
+
console.log(chalk.red(' ✖ Cannot reach authentication endpoints'));
|
|
449
|
+
console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
450
|
+
console.log(chalk.gray(' → Check internet connection'));
|
|
433
451
|
}
|
|
434
452
|
// Step 6: Device identification
|
|
435
|
-
console.log(
|
|
453
|
+
console.log(chalk.cyan('\n6. Device Information'));
|
|
436
454
|
try {
|
|
437
455
|
const deviceId = await config.getDeviceId();
|
|
438
456
|
diagnostics.deviceId = deviceId;
|
|
439
|
-
console.log(
|
|
457
|
+
console.log(chalk.green(' ✓ Device ID:'), chalk.gray(deviceId));
|
|
440
458
|
}
|
|
441
459
|
catch (error) {
|
|
442
|
-
console.log(
|
|
460
|
+
console.log(chalk.yellow(' ⚠ Could not get device ID'));
|
|
443
461
|
if (process.env.CLI_VERBOSE === 'true' && error instanceof Error) {
|
|
444
|
-
console.log(
|
|
462
|
+
console.log(chalk.gray(` ${error.message}`));
|
|
445
463
|
}
|
|
446
464
|
}
|
|
447
465
|
// Summary and recommendations
|
|
448
|
-
console.log(
|
|
466
|
+
console.log(chalk.blue.bold('\n📋 Diagnostic Summary'));
|
|
449
467
|
console.log(colors.info('━'.repeat(50)));
|
|
450
468
|
const issues = [];
|
|
451
469
|
const recommendations = [];
|
|
@@ -474,32 +492,32 @@ async function diagnoseCommand() {
|
|
|
474
492
|
recommendations.push('Check internet connection and firewall settings');
|
|
475
493
|
}
|
|
476
494
|
if (issues.length === 0) {
|
|
477
|
-
console.log(
|
|
478
|
-
console.log(
|
|
495
|
+
console.log(chalk.green('✅ All authentication checks passed!'));
|
|
496
|
+
console.log(chalk.cyan(' Your authentication is working correctly.'));
|
|
479
497
|
}
|
|
480
498
|
else {
|
|
481
|
-
console.log(
|
|
499
|
+
console.log(chalk.red(`❌ Found ${issues.length} issue(s):`));
|
|
482
500
|
issues.forEach(issue => {
|
|
483
|
-
console.log(
|
|
501
|
+
console.log(chalk.red(` • ${issue}`));
|
|
484
502
|
});
|
|
485
|
-
console.log(
|
|
503
|
+
console.log(chalk.yellow('\n💡 Recommended actions:'));
|
|
486
504
|
recommendations.forEach(rec => {
|
|
487
|
-
console.log(
|
|
505
|
+
console.log(chalk.cyan(` • ${rec}`));
|
|
488
506
|
});
|
|
489
507
|
}
|
|
490
508
|
// Additional troubleshooting info
|
|
491
509
|
if (diagnostics.authFailures > 0 || !diagnostics.credentialsValid) {
|
|
492
|
-
console.log(
|
|
493
|
-
console.log(
|
|
494
|
-
console.log(
|
|
495
|
-
console.log(
|
|
496
|
-
console.log(
|
|
510
|
+
console.log(chalk.gray('\n🔧 Additional troubleshooting:'));
|
|
511
|
+
console.log(chalk.gray(' • Verify the vendor key matches the value shown in your dashboard'));
|
|
512
|
+
console.log(chalk.gray(' • Check if your key is active in the dashboard'));
|
|
513
|
+
console.log(chalk.gray(' • Try browser authentication: lanonasis auth login (choose Browser Login)'));
|
|
514
|
+
console.log(chalk.gray(' • Contact support if issues persist'));
|
|
497
515
|
}
|
|
498
516
|
}
|
|
499
|
-
async function loginCommand(options) {
|
|
500
|
-
const config = new
|
|
517
|
+
export async function loginCommand(options) {
|
|
518
|
+
const config = new CLIConfig();
|
|
501
519
|
await config.init();
|
|
502
|
-
console.log(
|
|
520
|
+
console.log(chalk.blue.bold('🔐 Onasis-Core Golden Contract Authentication'));
|
|
503
521
|
console.log(colors.info('━'.repeat(50)));
|
|
504
522
|
console.log();
|
|
505
523
|
// Debug: Check options
|
|
@@ -521,7 +539,7 @@ async function loginCommand(options) {
|
|
|
521
539
|
return;
|
|
522
540
|
}
|
|
523
541
|
// Show authentication options
|
|
524
|
-
const authChoice = await
|
|
542
|
+
const authChoice = await inquirer.prompt([
|
|
525
543
|
{
|
|
526
544
|
type: 'list',
|
|
527
545
|
name: 'method',
|
|
@@ -557,14 +575,14 @@ async function loginCommand(options) {
|
|
|
557
575
|
async function handleVendorKeyAuth(vendorKey, config) {
|
|
558
576
|
// Check for authentication delay before attempting
|
|
559
577
|
await handleAuthDelay(config);
|
|
560
|
-
const spinner = (
|
|
578
|
+
const spinner = ora('Validating vendor key...').start();
|
|
561
579
|
try {
|
|
562
580
|
await config.setVendorKey(vendorKey);
|
|
563
581
|
// Test the vendor key with a health check
|
|
564
|
-
await
|
|
582
|
+
await apiClient.get('/health');
|
|
565
583
|
spinner.succeed('Vendor key authentication successful');
|
|
566
584
|
console.log();
|
|
567
|
-
console.log(
|
|
585
|
+
console.log(chalk.green('✓ Authenticated with vendor key'));
|
|
568
586
|
console.log(colors.info('Ready to use Onasis-Core services'));
|
|
569
587
|
}
|
|
570
588
|
catch (error) {
|
|
@@ -576,16 +594,16 @@ async function handleVendorKeyAuth(vendorKey, config) {
|
|
|
576
594
|
}
|
|
577
595
|
async function handleVendorKeyFlow(config) {
|
|
578
596
|
console.log();
|
|
579
|
-
console.log(
|
|
580
|
-
console.log(
|
|
597
|
+
console.log(chalk.yellow('🔑 Vendor Key Authentication'));
|
|
598
|
+
console.log(chalk.gray('Vendor keys provide secure API access for automation and integrations.'));
|
|
581
599
|
console.log();
|
|
582
600
|
// Enhanced guidance for obtaining vendor keys
|
|
583
|
-
console.log(
|
|
584
|
-
console.log(
|
|
585
|
-
console.log(
|
|
586
|
-
console.log(
|
|
601
|
+
console.log(chalk.cyan('📋 How to get your vendor key:'));
|
|
602
|
+
console.log(chalk.gray('1. Visit your Lanonasis dashboard at https://dashboard.lanonasis.com'));
|
|
603
|
+
console.log(chalk.gray('2. Navigate to Settings → API Keys'));
|
|
604
|
+
console.log(chalk.gray('3. Click "Generate New Key" and copy the full key value'));
|
|
587
605
|
console.log();
|
|
588
|
-
const { vendorKey } = await
|
|
606
|
+
const { vendorKey } = await inquirer.prompt([
|
|
589
607
|
{
|
|
590
608
|
type: 'password',
|
|
591
609
|
name: 'vendorKey',
|
|
@@ -600,10 +618,10 @@ async function handleVendorKeyFlow(config) {
|
|
|
600
618
|
}
|
|
601
619
|
async function handleOAuthFlow(config) {
|
|
602
620
|
console.log();
|
|
603
|
-
console.log(
|
|
604
|
-
console.log(
|
|
621
|
+
console.log(chalk.yellow('🌐 Browser-Based OAuth2 Authentication'));
|
|
622
|
+
console.log(chalk.gray('Secure authentication using OAuth2 with PKCE'));
|
|
605
623
|
console.log();
|
|
606
|
-
const { openBrowser } = await
|
|
624
|
+
const { openBrowser } = await inquirer.prompt([
|
|
607
625
|
{
|
|
608
626
|
type: 'confirm',
|
|
609
627
|
name: 'openBrowser',
|
|
@@ -612,41 +630,49 @@ async function handleOAuthFlow(config) {
|
|
|
612
630
|
}
|
|
613
631
|
]);
|
|
614
632
|
if (!openBrowser) {
|
|
615
|
-
console.log(
|
|
633
|
+
console.log(chalk.yellow('⚠️ Authentication cancelled'));
|
|
616
634
|
return;
|
|
617
635
|
}
|
|
618
636
|
try {
|
|
619
637
|
// Generate PKCE challenge
|
|
620
638
|
const pkce = generatePKCE();
|
|
621
|
-
console.log(
|
|
639
|
+
console.log(chalk.gray(' ✓ Generated PKCE challenge'));
|
|
622
640
|
// Start local callback server
|
|
623
641
|
const callbackPort = 8888;
|
|
624
642
|
const callbackPromise = createCallbackServer(callbackPort);
|
|
625
|
-
console.log(
|
|
643
|
+
console.log(chalk.gray(` ✓ Started local callback server on port ${callbackPort}`));
|
|
626
644
|
// Build OAuth2 authorization URL
|
|
627
645
|
const authBase = config.getDiscoveredApiUrl();
|
|
646
|
+
const redirectUri = `http://localhost:${callbackPort}/callback`;
|
|
628
647
|
const authUrl = new URL(`${authBase}/oauth/authorize`);
|
|
629
648
|
authUrl.searchParams.set('response_type', 'code');
|
|
630
649
|
authUrl.searchParams.set('client_id', 'lanonasis-cli');
|
|
631
|
-
authUrl.searchParams.set('redirect_uri',
|
|
650
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
632
651
|
authUrl.searchParams.set('scope', 'read write offline_access');
|
|
633
652
|
authUrl.searchParams.set('code_challenge', pkce.challenge);
|
|
634
653
|
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
635
|
-
authUrl.searchParams.set('state',
|
|
654
|
+
authUrl.searchParams.set('state', crypto.randomBytes(16).toString('hex'));
|
|
636
655
|
console.log();
|
|
637
656
|
console.log(colors.info('Opening browser for authentication...'));
|
|
638
|
-
await (
|
|
657
|
+
await open(authUrl.toString());
|
|
639
658
|
console.log(colors.info('Waiting for authentication in browser...'));
|
|
640
659
|
console.log(colors.muted(`If browser doesn't open, visit: ${authUrl.toString()}`));
|
|
641
660
|
console.log();
|
|
642
661
|
// Wait for callback
|
|
643
|
-
const spinner = (
|
|
662
|
+
const spinner = ora('Waiting for authorization...').start();
|
|
644
663
|
const { code } = await callbackPromise;
|
|
645
664
|
spinner.succeed('Authorization code received');
|
|
646
665
|
// Exchange code for tokens
|
|
647
666
|
spinner.text = 'Exchanging code for access tokens...';
|
|
648
667
|
spinner.start();
|
|
649
|
-
|
|
668
|
+
// Debug logging in verbose mode
|
|
669
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
670
|
+
console.log(chalk.dim(` Code length: ${code.length}`));
|
|
671
|
+
console.log(chalk.dim(` Verifier length: ${pkce.verifier.length}`));
|
|
672
|
+
console.log(chalk.dim(` Redirect URI: ${redirectUri}`));
|
|
673
|
+
console.log(chalk.dim(` Token endpoint: ${authBase}/oauth/token`));
|
|
674
|
+
}
|
|
675
|
+
const tokens = await exchangeCodeForTokens(code, pkce.verifier, authBase, redirectUri);
|
|
650
676
|
spinner.succeed('Access tokens received');
|
|
651
677
|
// Store tokens
|
|
652
678
|
await config.setToken(tokens.access_token);
|
|
@@ -654,27 +680,68 @@ async function handleOAuthFlow(config) {
|
|
|
654
680
|
await config.set('token_expires_at', Date.now() + (tokens.expires_in * 1000));
|
|
655
681
|
await config.set('authMethod', 'oauth2');
|
|
656
682
|
console.log();
|
|
657
|
-
console.log(
|
|
683
|
+
console.log(chalk.green('✓ OAuth2 authentication successful'));
|
|
658
684
|
console.log(colors.info('You can now use Lanonasis services'));
|
|
659
685
|
process.exit(0);
|
|
660
686
|
}
|
|
661
687
|
catch (error) {
|
|
662
|
-
console.error(
|
|
688
|
+
console.error(chalk.red('✖ OAuth2 authentication failed'));
|
|
689
|
+
// Display detailed error information
|
|
663
690
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
664
|
-
console.error(
|
|
691
|
+
console.error(chalk.gray(` ${errorMessage}`));
|
|
692
|
+
// Show validation details if available
|
|
693
|
+
if (error.details) {
|
|
694
|
+
console.error(chalk.yellow('\n Validation errors:'));
|
|
695
|
+
for (const [field, messages] of Object.entries(error.details)) {
|
|
696
|
+
const msgArray = Array.isArray(messages) ? messages : [messages];
|
|
697
|
+
msgArray.forEach((msg) => {
|
|
698
|
+
console.error(chalk.gray(` • ${field}: ${msg}`));
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// Show error data if available
|
|
703
|
+
if (error.errorData) {
|
|
704
|
+
const errorData = error.errorData;
|
|
705
|
+
if (errorData.error) {
|
|
706
|
+
console.error(chalk.yellow(`\n Error: ${errorData.error}`));
|
|
707
|
+
}
|
|
708
|
+
if (errorData.error_description) {
|
|
709
|
+
console.error(chalk.gray(` ${errorData.error_description}`));
|
|
710
|
+
}
|
|
711
|
+
// Show details if not already shown above
|
|
712
|
+
if (!error.details && errorData.details) {
|
|
713
|
+
console.error(chalk.yellow('\n Details:'));
|
|
714
|
+
console.error(chalk.gray(JSON.stringify(errorData.details, null, 2)));
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// Show full error response in verbose mode
|
|
718
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
719
|
+
if (error.response?.data) {
|
|
720
|
+
console.error(chalk.dim('\n Full error response:'));
|
|
721
|
+
console.error(chalk.dim(JSON.stringify(error.response.data, null, 2)));
|
|
722
|
+
}
|
|
723
|
+
if (error.response?.config) {
|
|
724
|
+
console.error(chalk.dim('\n Request config:'));
|
|
725
|
+
console.error(chalk.dim(JSON.stringify({
|
|
726
|
+
url: error.response.config.url,
|
|
727
|
+
method: error.response.config.method,
|
|
728
|
+
data: error.response.config.data
|
|
729
|
+
}, null, 2)));
|
|
730
|
+
}
|
|
731
|
+
}
|
|
665
732
|
process.exit(1);
|
|
666
733
|
}
|
|
667
734
|
}
|
|
668
735
|
async function handleCredentialsFlow(options, config) {
|
|
669
736
|
console.log();
|
|
670
|
-
console.log(
|
|
737
|
+
console.log(chalk.yellow('⚙️ Username/Password Authentication'));
|
|
671
738
|
console.log();
|
|
672
739
|
// Check for authentication delay before attempting
|
|
673
740
|
await handleAuthDelay(config);
|
|
674
741
|
let { email, password } = options;
|
|
675
742
|
// Get credentials if not provided
|
|
676
743
|
if (!email || !password) {
|
|
677
|
-
const answers = await
|
|
744
|
+
const answers = await inquirer.prompt([
|
|
678
745
|
{
|
|
679
746
|
type: 'input',
|
|
680
747
|
name: 'email',
|
|
@@ -696,14 +763,14 @@ async function handleCredentialsFlow(options, config) {
|
|
|
696
763
|
email = answers.email;
|
|
697
764
|
password = answers.password;
|
|
698
765
|
}
|
|
699
|
-
const spinner = (
|
|
766
|
+
const spinner = ora('Authenticating...').start();
|
|
700
767
|
try {
|
|
701
|
-
const response = await
|
|
768
|
+
const response = await apiClient.login(email, password);
|
|
702
769
|
// Store token and user info
|
|
703
770
|
await config.setToken(response.token);
|
|
704
771
|
spinner.succeed('Login successful');
|
|
705
772
|
console.log();
|
|
706
|
-
console.log(
|
|
773
|
+
console.log(chalk.green('✓ Authenticated successfully'));
|
|
707
774
|
console.log(`Welcome, ${response.user.email}!`);
|
|
708
775
|
if (response.user.organization_id) {
|
|
709
776
|
console.log(`Organization: ${response.user.organization_id}`);
|
|
@@ -718,7 +785,7 @@ async function handleCredentialsFlow(options, config) {
|
|
|
718
785
|
const errorResponse = error && typeof error === 'object' && 'response' in error ? error.response : null;
|
|
719
786
|
if (errorResponse && typeof errorResponse === 'object' && 'status' in errorResponse && errorResponse.status === 401) {
|
|
720
787
|
console.log();
|
|
721
|
-
const answer = await
|
|
788
|
+
const answer = await inquirer.prompt([
|
|
722
789
|
{
|
|
723
790
|
type: 'confirm',
|
|
724
791
|
name: 'register',
|
|
@@ -736,9 +803,9 @@ async function handleCredentialsFlow(options, config) {
|
|
|
736
803
|
}
|
|
737
804
|
async function registerFlow(defaultEmail) {
|
|
738
805
|
console.log();
|
|
739
|
-
console.log(
|
|
806
|
+
console.log(chalk.blue.bold('📝 Create New Account'));
|
|
740
807
|
console.log();
|
|
741
|
-
const answers = await
|
|
808
|
+
const answers = await inquirer.prompt([
|
|
742
809
|
{
|
|
743
810
|
type: 'input',
|
|
744
811
|
name: 'email',
|
|
@@ -772,14 +839,14 @@ async function registerFlow(defaultEmail) {
|
|
|
772
839
|
default: ''
|
|
773
840
|
}
|
|
774
841
|
]);
|
|
775
|
-
const spinner = (
|
|
842
|
+
const spinner = ora('Creating account...').start();
|
|
776
843
|
try {
|
|
777
|
-
const response = await
|
|
778
|
-
const config = new
|
|
844
|
+
const response = await apiClient.register(answers.email, answers.password, answers.organizationName || undefined);
|
|
845
|
+
const config = new CLIConfig();
|
|
779
846
|
await config.setToken(response.token);
|
|
780
847
|
spinner.succeed('Account created successfully');
|
|
781
848
|
console.log();
|
|
782
|
-
console.log(
|
|
849
|
+
console.log(chalk.green('✓ Account created and authenticated'));
|
|
783
850
|
console.log(`Welcome to MaaS, ${response.user.email}!`);
|
|
784
851
|
if (answers.organizationName) {
|
|
785
852
|
console.log(`Organization: ${answers.organizationName}`);
|
|
@@ -789,7 +856,7 @@ async function registerFlow(defaultEmail) {
|
|
|
789
856
|
catch (error) {
|
|
790
857
|
spinner.fail('Registration failed');
|
|
791
858
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
792
|
-
console.error(
|
|
859
|
+
console.error(chalk.red('✖ Registration failed:'), errorMessage);
|
|
793
860
|
process.exit(1);
|
|
794
861
|
}
|
|
795
862
|
}
|