@createlex/createlexgenai 1.0.0 → 1.0.1
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/package.json +1 -1
- package/src/commands/login.js +79 -51
package/package.json
CHANGED
package/src/commands/login.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const http = require('http');
|
|
4
|
-
const
|
|
4
|
+
const url = require('url');
|
|
5
5
|
const log = require('../utils/logger');
|
|
6
6
|
const authManager = require('../core/auth-manager');
|
|
7
7
|
const configStore = require('../core/config-store');
|
|
8
8
|
|
|
9
|
+
const CALLBACK_PORT = 7891; // Same port as VS Code extension & Bridge app
|
|
10
|
+
|
|
9
11
|
async function login() {
|
|
10
12
|
const existing = authManager.getToken();
|
|
11
13
|
if (existing) {
|
|
@@ -17,79 +19,105 @@ async function login() {
|
|
|
17
19
|
log.warn('Existing token is expired or invalid. Starting fresh login...');
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const state = crypto.randomBytes(16).toString('hex');
|
|
21
22
|
const webBaseUrl = configStore.get('webBaseUrl') || 'https://createlex.com';
|
|
22
23
|
|
|
23
|
-
// Start
|
|
24
|
-
const server = http.createServer((req, res) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const returnedState = url.searchParams.get('state');
|
|
30
|
-
|
|
31
|
-
if (returnedState !== state) {
|
|
32
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
33
|
-
res.end('<html><body><h2>Authentication failed: state mismatch</h2></body></html>');
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!token) {
|
|
38
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
39
|
-
res.end('<html><body><h2>Authentication failed: no token received</h2></body></html>');
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
24
|
+
// Start local callback server (same as VS Code extension / Bridge app)
|
|
25
|
+
const server = http.createServer(async (req, res) => {
|
|
26
|
+
// CORS headers (bridge-callback page does cross-origin fetch)
|
|
27
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
28
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
29
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
31
|
+
if (req.method === 'OPTIONS') {
|
|
32
|
+
res.writeHead(200);
|
|
33
|
+
res.end();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
const parsedUrl = url.parse(req.url, true);
|
|
38
|
+
|
|
39
|
+
// POST /auth/success — same endpoint as VS Code extension & Bridge app
|
|
40
|
+
if (req.method === 'POST' && parsedUrl.pathname === '/auth/success') {
|
|
41
|
+
let body = '';
|
|
42
|
+
req.on('data', chunk => body += chunk.toString());
|
|
43
|
+
req.on('end', () => {
|
|
44
|
+
try {
|
|
45
|
+
const { token, userId, email, hasSubscription } = JSON.parse(body);
|
|
46
|
+
|
|
47
|
+
if (!token || !userId) {
|
|
48
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
49
|
+
res.end(JSON.stringify({ success: false, error: 'Missing token or userId' }));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const validation = authManager.validateTokenFormat(token);
|
|
54
|
+
if (!validation.valid) {
|
|
55
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
56
|
+
res.end(JSON.stringify({ success: false, error: validation.reason }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Store the token
|
|
61
|
+
authManager.setToken(token, {
|
|
62
|
+
email: email || validation.payload?.email,
|
|
63
|
+
userId: userId || validation.payload?.sub
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
67
|
+
res.end(JSON.stringify({ success: true }));
|
|
68
|
+
|
|
69
|
+
log.blank();
|
|
70
|
+
log.success('Login successful!');
|
|
71
|
+
if (email) {
|
|
72
|
+
log.keyValue('Email', email);
|
|
73
|
+
}
|
|
74
|
+
if (hasSubscription !== undefined) {
|
|
75
|
+
log.keyValue('Subscription', hasSubscription ? 'Active' : 'Inactive');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
server.close();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
81
|
+
res.end(JSON.stringify({ success: false, error: err.message }));
|
|
82
|
+
}
|
|
54
83
|
});
|
|
55
|
-
|
|
56
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
57
|
-
res.end('<html><body><h2>Authentication successful!</h2><p>You can close this window and return to the terminal.</p></body></html>');
|
|
58
|
-
|
|
59
|
-
log.blank();
|
|
60
|
-
log.success('Login successful!');
|
|
61
|
-
|
|
62
|
-
if (validation.payload?.email) {
|
|
63
|
-
log.keyValue('Email', validation.payload.email);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
server.close();
|
|
67
84
|
} else {
|
|
68
85
|
res.writeHead(404);
|
|
69
86
|
res.end();
|
|
70
87
|
}
|
|
71
88
|
});
|
|
72
89
|
|
|
73
|
-
//
|
|
90
|
+
// Listen on the same port as VS Code extension / Bridge app
|
|
74
91
|
await new Promise((resolve, reject) => {
|
|
75
|
-
server.listen(
|
|
76
|
-
server.on('error',
|
|
92
|
+
server.listen(CALLBACK_PORT, 'localhost', () => resolve());
|
|
93
|
+
server.on('error', (err) => {
|
|
94
|
+
if (err.code === 'EADDRINUSE') {
|
|
95
|
+
log.error(`Port ${CALLBACK_PORT} is already in use.`);
|
|
96
|
+
log.info('Close the VS Code extension or Bridge app and try again,');
|
|
97
|
+
log.info('or use `createlex config set token <your-jwt>` to set the token manually.');
|
|
98
|
+
reject(err);
|
|
99
|
+
} else {
|
|
100
|
+
reject(err);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
77
103
|
});
|
|
78
104
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
105
|
+
// Use same OAuth flow as VS Code extension: /login?redirect=bridge-callback&source=bridge
|
|
106
|
+
const frontendCallbackUrl = `${webBaseUrl}/bridge-callback`;
|
|
107
|
+
const loginUrl = new URL('/login', webBaseUrl);
|
|
108
|
+
loginUrl.searchParams.set('redirect', encodeURIComponent(frontendCallbackUrl));
|
|
109
|
+
loginUrl.searchParams.set('source', 'bridge');
|
|
82
110
|
|
|
83
111
|
log.header('CreateLex Login');
|
|
84
112
|
log.info('Opening browser for authentication...');
|
|
85
113
|
log.blank();
|
|
86
114
|
log.info(`If the browser doesn't open, visit:`);
|
|
87
|
-
log.info(
|
|
115
|
+
log.info(loginUrl.toString());
|
|
88
116
|
log.blank();
|
|
89
117
|
|
|
90
118
|
try {
|
|
91
119
|
const open = require('open');
|
|
92
|
-
await open(
|
|
120
|
+
await open(loginUrl.toString());
|
|
93
121
|
} catch {
|
|
94
122
|
log.warn('Could not open browser automatically. Please visit the URL above.');
|
|
95
123
|
}
|