@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@createlex/createlexgenai",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "CLI tool and MCP server for CreatelexGenAI — Unreal Engine AI integration",
5
5
  "bin": {
6
6
  "createlex": "./bin/createlex.js"
@@ -1,11 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const http = require('http');
4
- const crypto = require('crypto');
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 a temporary local server to receive the auth callback
24
- const server = http.createServer((req, res) => {
25
- const url = new URL(req.url, 'http://localhost');
26
-
27
- if (url.pathname === '/callback') {
28
- const token = url.searchParams.get('token');
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
- const validation = authManager.validateTokenFormat(token);
44
- if (!validation.valid) {
45
- res.writeHead(400, { 'Content-Type': 'text/html' });
46
- res.end(`<html><body><h2>Authentication failed: ${validation.reason}</h2></body></html>`);
47
- return;
48
- }
31
+ if (req.method === 'OPTIONS') {
32
+ res.writeHead(200);
33
+ res.end();
34
+ return;
35
+ }
49
36
 
50
- // Store the token
51
- authManager.setToken(token, {
52
- email: validation.payload?.email,
53
- userId: validation.payload?.sub || validation.payload?.userId
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
- // Find an available port
90
+ // Listen on the same port as VS Code extension / Bridge app
74
91
  await new Promise((resolve, reject) => {
75
- server.listen(0, '127.0.0.1', () => resolve());
76
- server.on('error', reject);
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
- const port = server.address().port;
80
- const callbackUrl = encodeURIComponent(`http://localhost:${port}/callback`);
81
- const authUrl = `${webBaseUrl}/auth/cli?callback=${callbackUrl}&state=${state}`;
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(authUrl);
115
+ log.info(loginUrl.toString());
88
116
  log.blank();
89
117
 
90
118
  try {
91
119
  const open = require('open');
92
- await open(authUrl);
120
+ await open(loginUrl.toString());
93
121
  } catch {
94
122
  log.warn('Could not open browser automatically. Please visit the URL above.');
95
123
  }