luxlabs 1.0.21 → 1.0.24

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.
Files changed (37) hide show
  1. package/README.md +16 -21
  2. package/commands/ab-tests.js +14 -11
  3. package/commands/agents.js +11 -11
  4. package/commands/data.js +19 -17
  5. package/commands/deploy.js +145 -82
  6. package/commands/flows.js +152 -133
  7. package/commands/interface/init.js +36 -35
  8. package/commands/interface.js +135 -10
  9. package/commands/knowledge.js +3 -3
  10. package/commands/list.js +6 -24
  11. package/commands/login.js +31 -26
  12. package/commands/logout.js +13 -4
  13. package/commands/logs.js +17 -66
  14. package/commands/project.js +74 -47
  15. package/commands/secrets.js +1 -1
  16. package/commands/servers.js +9 -113
  17. package/commands/storage.js +1 -1
  18. package/commands/tools.js +4 -4
  19. package/commands/validate-data-lux.js +5 -2
  20. package/commands/voice-agents.js +22 -18
  21. package/lib/config.js +235 -83
  22. package/lib/helpers.js +6 -4
  23. package/lux.js +4 -94
  24. package/package.json +6 -1
  25. package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +41 -34
  26. package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +41 -34
  27. package/templates/interface-boilerplate/components/providers/posthog-provider.tsx +41 -26
  28. package/templates/interface-boilerplate/gitignore.template +4 -0
  29. package/templates/interface-boilerplate/lib/auth.config.ts +3 -2
  30. package/templates/interface-boilerplate/lib/knowledge.ts +2 -2
  31. package/templates/interface-boilerplate/middleware.ts +14 -3
  32. package/templates/interface-boilerplate/next-env.d.ts +6 -0
  33. package/templates/interface-boilerplate/package-lock.json +432 -8
  34. package/commands/dev.js +0 -578
  35. package/commands/init.js +0 -126
  36. package/commands/link.js +0 -127
  37. package/commands/up.js +0 -211
@@ -77,14 +77,14 @@ async function initInterface(options) {
77
77
  // Check authentication
78
78
  if (!isAuthenticated()) {
79
79
  console.log(
80
- chalk.red('āŒ Not authenticated. Run'),
80
+ chalk.red('Not authenticated. Run'),
81
81
  chalk.white('lux login'),
82
82
  chalk.red('first.')
83
83
  );
84
84
  process.exit(1);
85
85
  }
86
86
 
87
- console.log(chalk.cyan('\nšŸ“¦ Initialize New App\n'));
87
+ console.log(chalk.cyan('\nInitialize New App\n'));
88
88
  console.log(chalk.dim('─'.repeat(50)));
89
89
  console.log(chalk.blue('[STEP 0/6]'), 'Validating options...');
90
90
 
@@ -93,8 +93,8 @@ async function initInterface(options) {
93
93
  let description = options.description || '';
94
94
 
95
95
  if (!name) {
96
- console.log(chalk.red('āŒ --name flag is required'));
97
- console.log(chalk.dim('Usage: lux i init --name <name> [--description <desc>]'));
96
+ console.log(chalk.red('--name flag is required'));
97
+ console.log(chalk.dim('Usage: lux i create --name <name> [--description <desc>]'));
98
98
  process.exit(1);
99
99
  }
100
100
 
@@ -105,12 +105,12 @@ async function initInterface(options) {
105
105
 
106
106
  // Check if interface with this slug already exists
107
107
  if (interfaceExists(slug)) {
108
- console.log(chalk.red(`āŒ Interface already exists: ${slug}`));
108
+ console.log(chalk.red(`Interface already exists: ${slug}`));
109
109
  console.log(chalk.dim('Use a different name or delete the existing interface first.'));
110
110
  process.exit(1);
111
111
  }
112
112
 
113
- console.log(chalk.green(' āœ“ Options validated'));
113
+ console.log(chalk.green(' Options validated'));
114
114
  console.log(chalk.dim('─'.repeat(50)));
115
115
 
116
116
  try {
@@ -138,23 +138,23 @@ async function initInterface(options) {
138
138
  const vercelProjectId = interfaceData.vercel_project_id;
139
139
  const vercelProjectUrl = interfaceData.vercel_project_url;
140
140
 
141
- console.log(chalk.green(' āœ“ Interface registered in database'));
141
+ console.log(chalk.green(' Interface registered in database'));
142
142
  console.log(chalk.dim(' Interface ID:'), interfaceId);
143
143
  if (githubRepoUrl) {
144
- console.log(chalk.green(' āœ“ GitHub repository created'));
144
+ console.log(chalk.green(' GitHub repository created'));
145
145
  console.log(chalk.dim(' GitHub URL:'), githubRepoUrl);
146
146
  } else {
147
- console.log(chalk.yellow(' ⚠ No GitHub repository URL returned'));
147
+ console.log(chalk.yellow(' No GitHub repository URL returned'));
148
148
  }
149
149
 
150
150
  if (vercelProjectId) {
151
- console.log(chalk.green(' āœ“ Vercel project created'));
151
+ console.log(chalk.green(' Vercel project created'));
152
152
  console.log(chalk.dim(' Vercel Project ID:'), vercelProjectId);
153
153
  if (vercelProjectUrl) {
154
154
  console.log(chalk.dim(' Vercel Dashboard:'), vercelProjectUrl);
155
155
  }
156
156
  } else {
157
- console.log(chalk.yellow(' ⚠ Vercel project not created (may need VERCEL_TOKEN)'));
157
+ console.log(chalk.yellow(' Vercel project not created (may need VERCEL_TOKEN)'));
158
158
  }
159
159
 
160
160
  console.log(chalk.dim('─'.repeat(50)));
@@ -175,15 +175,15 @@ async function initInterface(options) {
175
175
  });
176
176
  if (tokenRes.data?.token) {
177
177
  githubToken = tokenRes.data.token;
178
- console.log(chalk.green(' āœ“ GitHub token fetched from API'));
178
+ console.log(chalk.green(' GitHub token fetched from API'));
179
179
  } else {
180
- console.log(chalk.yellow(' ⚠ No token returned from API'));
180
+ console.log(chalk.yellow(' No token returned from API'));
181
181
  }
182
182
  } catch (tokenErr) {
183
- console.log(chalk.yellow(' ⚠ Failed to fetch token:'), tokenErr.message);
183
+ console.log(chalk.yellow(' Failed to fetch token:'), tokenErr.message);
184
184
  }
185
185
  } else {
186
- console.log(chalk.green(' āœ“ Using local GitHub token'));
186
+ console.log(chalk.green(' Using local GitHub token'));
187
187
  }
188
188
  console.log(chalk.dim('─'.repeat(50)));
189
189
 
@@ -200,7 +200,7 @@ async function initInterface(options) {
200
200
  console.log(chalk.dim(' Repo dir:'), repoDir);
201
201
 
202
202
  if (fs.existsSync(repoDir)) {
203
- console.log(chalk.yellow(' ⚠ Project directory already exists'));
203
+ console.log(chalk.yellow(' Project directory already exists'));
204
204
  } else {
205
205
  fs.mkdirSync(repoDir, { recursive: true });
206
206
  console.log(chalk.dim(' Created directory'));
@@ -217,10 +217,10 @@ async function initInterface(options) {
217
217
  // List what was copied
218
218
  const copiedFiles = fs.readdirSync(repoDir);
219
219
  console.log(chalk.dim(' Copied files:'), copiedFiles.join(', '));
220
- console.log(chalk.green(' āœ“ Template files copied successfully'));
220
+ console.log(chalk.green(' Template files copied successfully'));
221
221
  }
222
222
  } catch (copyError) {
223
- console.log(chalk.red(' āœ— Failed to copy templates:'), copyError.message);
223
+ console.log(chalk.red(' Failed to copy templates:'), copyError.message);
224
224
  throw copyError;
225
225
  }
226
226
  console.log(chalk.dim('─'.repeat(50)));
@@ -233,9 +233,9 @@ async function initInterface(options) {
233
233
  const npmStart = Date.now();
234
234
  await runNpmInstall(repoDir);
235
235
  const npmDuration = ((Date.now() - npmStart) / 1000).toFixed(1);
236
- console.log(chalk.green(` āœ“ Dependencies installed (${npmDuration}s)`));
236
+ console.log(chalk.green(` Dependencies installed (${npmDuration}s)`));
237
237
  } catch (npmError) {
238
- console.log(chalk.yellow(` ⚠ npm install failed: ${npmError.message}`));
238
+ console.log(chalk.yellow(` npm install failed: ${npmError.message}`));
239
239
  console.log(chalk.dim(' Dependencies will be installed when you start the dev server'));
240
240
  }
241
241
  console.log(chalk.dim('─'.repeat(50)));
@@ -250,34 +250,34 @@ async function initInterface(options) {
250
250
  const hasNodeModules = gitignoreContent.includes('node_modules');
251
251
  console.log(chalk.dim(' .gitignore exists:'), hasNodeModules ? 'includes node_modules' : 'WARNING: missing node_modules!');
252
252
  } else {
253
- console.log(chalk.yellow(' ⚠ No .gitignore found'));
253
+ console.log(chalk.yellow(' No .gitignore found'));
254
254
  }
255
255
 
256
256
  // Initialize git repo
257
257
  await runGitCommand(['init'], repoDir);
258
- console.log(chalk.green(' āœ“ git init completed'));
258
+ console.log(chalk.green(' git init completed'));
259
259
 
260
260
  // Add remote origin
261
261
  if (githubToken) {
262
262
  const targetUrl = githubRepoUrl.replace('https://github.com/', `https://${githubToken}@github.com/`) + '.git';
263
263
  await runGitCommand(['remote', 'add', 'origin', targetUrl], repoDir);
264
- console.log(chalk.green(' āœ“ Remote origin added'));
264
+ console.log(chalk.green(' Remote origin added'));
265
265
  console.log(chalk.dim(' Remote URL:'), githubRepoUrl + '.git');
266
266
  } else {
267
- console.log(chalk.yellow(' ⚠ No GitHub token - remote not added'));
267
+ console.log(chalk.yellow(' No GitHub token - remote not added'));
268
268
  }
269
269
 
270
270
  // Set main branch
271
271
  await runGitCommand(['branch', '-M', 'main'], repoDir);
272
- console.log(chalk.green(' āœ“ Branch set to main'));
272
+ console.log(chalk.green(' Branch set to main'));
273
273
 
274
274
  // Initial commit (but don't push yet - that happens on first deploy)
275
275
  await runGitCommand(['add', '.'], repoDir);
276
276
  await runGitCommand(['commit', '-m', 'Initial commit from Lux Studio'], repoDir);
277
- console.log(chalk.green(' āœ“ Initial commit created'));
277
+ console.log(chalk.green(' Initial commit created'));
278
278
  console.log(chalk.dim(' Note: Push to GitHub will happen on first deploy'));
279
279
  } catch (gitError) {
280
- console.log(chalk.yellow(` ⚠ Git setup failed: ${gitError.message}`));
280
+ console.log(chalk.yellow(` Git setup failed: ${gitError.message}`));
281
281
  }
282
282
  console.log(chalk.dim('─'.repeat(50)));
283
283
 
@@ -289,6 +289,7 @@ async function initInterface(options) {
289
289
  slug, // Keep slug for display/reference
290
290
  name,
291
291
  description,
292
+ version: 0, // Draft interfaces start at version 0
292
293
  status: 'draft', // New interfaces are drafts until first deploy
293
294
  githubUrl: githubRepoUrl,
294
295
  vercelProjectId: vercelProjectId || null,
@@ -296,14 +297,14 @@ async function initInterface(options) {
296
297
  createdAt: new Date().toISOString(),
297
298
  updatedAt: new Date().toISOString(),
298
299
  });
299
- console.log(chalk.green(' āœ“ Metadata saved'));
300
+ console.log(chalk.green(' Metadata saved'));
300
301
  console.log(chalk.dim(' ID:'), interfaceId);
301
302
  console.log(chalk.dim(' Slug:'), slug);
302
303
  if (vercelProjectId) {
303
304
  console.log(chalk.dim(' Vercel:'), vercelProjectId);
304
305
  }
305
306
  } catch (metadataError) {
306
- console.log(chalk.red(' āœ— Failed to save metadata:'), metadataError.message);
307
+ console.log(chalk.red(' Failed to save metadata:'), metadataError.message);
307
308
  }
308
309
  console.log(chalk.dim('─'.repeat(50)));
309
310
 
@@ -316,7 +317,7 @@ async function initInterface(options) {
316
317
  console.log(`Vercel project: ${vercelProjectId}`);
317
318
  }
318
319
 
319
- console.log(chalk.green('\nāœ… Interface created successfully!\n'));
320
+ console.log(chalk.green('\nInterface created successfully!\n'));
320
321
  console.log(chalk.dim(' Created:'));
321
322
  console.log(chalk.dim(' • Registered in system.interfaces'));
322
323
  console.log(chalk.dim(' • GitHub repository created'));
@@ -336,7 +337,7 @@ async function initInterface(options) {
336
337
  // No GitHub URL - output parseable format anyway (cloud-only interface)
337
338
  console.log(`App created: ${interfaceId}`);
338
339
 
339
- console.log(chalk.yellow('\n⚠ No GitHub URL returned - skipping local setup'));
340
+ console.log(chalk.yellow('\nNo GitHub URL returned - skipping local setup'));
340
341
  console.log(chalk.dim('\n Created:'));
341
342
  console.log(chalk.dim(' • Registered in system.interfaces'));
342
343
  console.log(chalk.cyan('\n Next steps:'));
@@ -345,12 +346,12 @@ async function initInterface(options) {
345
346
 
346
347
  process.exit(0);
347
348
  } catch (error) {
348
- console.log(chalk.red('\nāŒ Failed to initialize interface'));
349
+ console.log(chalk.red('\nFailed to initialize interface'));
349
350
 
350
351
  // Detailed error logging
351
352
  if (error.response) {
352
353
  // Server responded with an error status
353
- console.error(chalk.red('\nāŒ API Error:'));
354
+ console.error(chalk.red('\nAPI Error:'));
354
355
  console.error(chalk.dim(' Status:'), error.response.status);
355
356
  console.error(chalk.dim(' Message:'), error.response.data?.error || error.response.data?.message || 'Unknown error');
356
357
  if (error.response.data?.details) {
@@ -361,12 +362,12 @@ async function initInterface(options) {
361
362
  }
362
363
  } else if (error.request) {
363
364
  // Request was made but no response received
364
- console.error(chalk.red('\nāŒ Network Error:'));
365
+ console.error(chalk.red('\nNetwork Error:'));
365
366
  console.error(chalk.dim(' No response received from server'));
366
367
  console.error(chalk.dim(' URL:'), error.config?.url);
367
368
  } else {
368
369
  // Something else went wrong
369
- console.error(chalk.red('\nāŒ Error:'), error.message);
370
+ console.error(chalk.red('\nError:'), error.message);
370
371
  }
371
372
 
372
373
  process.exit(1);
@@ -15,6 +15,9 @@ const chalk = require('chalk');
15
15
  // Import from modular files
16
16
  const { initInterface } = require('./interface/init');
17
17
  const { getInterfacePath } = require('./interface/path');
18
+ const { screenshot, click, type: typeText, evaluate, getUrl, navigate, wait, startPreview } = require('./webview');
19
+ const { getLogs: getTerminalLogs, getBrowserConsoleLogs, handleServers } = require('./servers');
20
+ const { handleABTests } = require('./ab-tests');
18
21
 
19
22
  /**
20
23
  * Deploy interface to production
@@ -33,9 +36,9 @@ async function listInterfaces(options) {
33
36
  }
34
37
 
35
38
  /**
36
- * View interface logs
39
+ * View deployed interface logs (build or runtime from Vercel)
37
40
  */
38
- async function viewLogs(options) {
41
+ async function viewDeployedLogs(options) {
39
42
  const { logs } = require('./logs');
40
43
  await logs(options);
41
44
  }
@@ -75,15 +78,20 @@ function showHelp() {
75
78
  console.log(chalk.dim(' Create new interface'));
76
79
  console.log(chalk.dim(' Required: --name <interface-name>'));
77
80
  console.log(chalk.dim(' Optional: --description <desc>\n'));
78
- console.log(chalk.white(' lux interface init') + chalk.dim(' (alias for create)\n'));
79
81
  console.log(chalk.white(' lux interface list') + chalk.dim(' (or lux i list)'));
80
82
  console.log(chalk.dim(' List all interfaces\n'));
81
83
  console.log(chalk.white(' lux interface path <name-or-id>'));
82
84
  console.log(chalk.dim(' Get local repo path for an interface\n'));
83
85
  console.log(chalk.white(' lux interface deploy') + chalk.dim(' (or lux i deploy)'));
84
86
  console.log(chalk.dim(' Deploy interface to production\n'));
85
- console.log(chalk.white(' lux interface logs') + chalk.dim(' (or lux i logs)'));
86
- console.log(chalk.dim(' View interface logs\n'));
87
+ console.log(chalk.white(' lux interface logs <name-or-id>') + chalk.dim(' (or lux i logs)'));
88
+ console.log(chalk.dim(' View interface logs'));
89
+ console.log(chalk.dim(' Optional: --type <deployed|terminal|console> (default: terminal)'));
90
+ console.log(chalk.dim(' Optional: --lines <number> (default: 50)\n'));
91
+ console.log(chalk.white(' lux interface servers [subcommand]') + chalk.dim(' (or lux i servers)'));
92
+ console.log(chalk.dim(' Manage dev servers (list, restart)\n'));
93
+ console.log(chalk.white(' lux interface ab-tests [subcommand]') + chalk.dim(' (or lux i ab-tests)'));
94
+ console.log(chalk.dim(' Manage A/B tests (list-tests, get-test, get-variant)\n'));
87
95
  }
88
96
 
89
97
  /**
@@ -96,23 +104,140 @@ async function handleInterface(subcommand, args) {
96
104
  }
97
105
 
98
106
  switch (subcommand) {
99
- case 'init':
100
107
  case 'create':
101
108
  await initInterface(parseOptions(args));
102
109
  break;
103
- case 'deploy':
104
- await deployInterface(parseOptions(args));
110
+ case 'deploy': {
111
+ const deployOpts = parseOptions(args);
112
+ // First non-flag arg is the interface ID
113
+ const deployId = args.find(a => !a.startsWith('-'));
114
+ if (deployId) deployOpts.id = deployId;
115
+ await deployInterface(deployOpts);
105
116
  break;
117
+ }
106
118
  case 'list':
107
119
  case 'ls':
108
120
  await listInterfaces(parseOptions(args));
109
121
  break;
110
- case 'logs':
111
- await viewLogs(parseOptions(args));
122
+ case 'logs': {
123
+ const logsId = args.find(a => !a.startsWith('-'));
124
+ const logsOpts = parseOptions(args);
125
+ const logType = logsOpts.type || 'terminal';
126
+ const logLines = parseInt(logsOpts.lines || '50', 10);
127
+
128
+ if (!logsId) {
129
+ console.log(chalk.red('Error: interface name or ID is required'));
130
+ console.log(chalk.dim('Usage: lux i logs <name-or-id> [--type deployed|terminal|console] [--lines <number>]'));
131
+ return;
132
+ }
133
+
134
+ if (logType === 'deployed') {
135
+ await viewDeployedLogs({ id: logsId, type: 'runtime' });
136
+ } else if (logType === 'console') {
137
+ await getBrowserConsoleLogs(logsId, { lines: logLines });
138
+ } else {
139
+ // Default: terminal logs
140
+ await getTerminalLogs(logsId, { lines: logLines });
141
+ }
112
142
  break;
143
+ }
113
144
  case 'path':
114
145
  await getInterfacePath(args[0]);
115
146
  break;
147
+ case 'screenshot': {
148
+ const interfaceId = args[0];
149
+ if (!interfaceId) {
150
+ console.log(chalk.red('Error: interface name or ID is required'));
151
+ console.log(chalk.dim('Usage: lux i screenshot <name-or-id>'));
152
+ return;
153
+ }
154
+ await screenshot(interfaceId, args[1]);
155
+ break;
156
+ }
157
+ case 'click': {
158
+ const interfaceId = args[0];
159
+ const selector = args[1];
160
+ if (!interfaceId || !selector) {
161
+ console.log(chalk.red('Error: interface name or ID and selector are required'));
162
+ console.log(chalk.dim('Usage: lux i click <name-or-id> <selector>'));
163
+ return;
164
+ }
165
+ await click(interfaceId, selector);
166
+ break;
167
+ }
168
+ case 'type': {
169
+ const interfaceId = args[0];
170
+ const selector = args[1];
171
+ const text = args.slice(2).join(' ');
172
+ if (!interfaceId || !selector || !text) {
173
+ console.log(chalk.red('Error: interface name or ID, selector, and text are required'));
174
+ console.log(chalk.dim('Usage: lux i type <name-or-id> <selector> <text>'));
175
+ return;
176
+ }
177
+ await typeText(interfaceId, selector, text);
178
+ break;
179
+ }
180
+ case 'eval': {
181
+ const interfaceId = args[0];
182
+ const code = args.slice(1).join(' ');
183
+ if (!interfaceId || !code) {
184
+ console.log(chalk.red('Error: interface name or ID and code are required'));
185
+ console.log(chalk.dim('Usage: lux i eval <name-or-id> <code>'));
186
+ return;
187
+ }
188
+ await evaluate(interfaceId, code);
189
+ break;
190
+ }
191
+ case 'url': {
192
+ const interfaceId = args[0];
193
+ if (!interfaceId) {
194
+ console.log(chalk.red('Error: interface name or ID is required'));
195
+ console.log(chalk.dim('Usage: lux i url <name-or-id>'));
196
+ return;
197
+ }
198
+ await getUrl(interfaceId);
199
+ break;
200
+ }
201
+ case 'nav':
202
+ case 'navigate': {
203
+ const interfaceId = args[0];
204
+ const url = args[1];
205
+ if (!interfaceId || !url) {
206
+ console.log(chalk.red('Error: interface name or ID and URL are required'));
207
+ console.log(chalk.dim('Usage: lux i nav <name-or-id> <url>'));
208
+ return;
209
+ }
210
+ await navigate(interfaceId, url);
211
+ break;
212
+ }
213
+ case 'wait': {
214
+ const interfaceId = args[0];
215
+ const ms = args[1];
216
+ if (!interfaceId || !ms) {
217
+ console.log(chalk.red('Error: interface name or ID and duration are required'));
218
+ console.log(chalk.dim('Usage: lux i wait <name-or-id> <ms>'));
219
+ return;
220
+ }
221
+ await wait(interfaceId, ms);
222
+ break;
223
+ }
224
+ case 'preview': {
225
+ const interfaceId = args[0];
226
+ if (!interfaceId) {
227
+ console.log(chalk.red('Error: interface name or ID is required'));
228
+ console.log(chalk.dim('Usage: lux i preview <name-or-id>'));
229
+ return;
230
+ }
231
+ await startPreview(interfaceId);
232
+ break;
233
+ }
234
+ case 'servers':
235
+ await handleServers(args);
236
+ break;
237
+ case 'ab-tests':
238
+ case 'experiments':
239
+ await handleABTests(args);
240
+ break;
116
241
  default:
117
242
  console.log(chalk.red(`Unknown subcommand: ${subcommand}`));
118
243
  showHelp();
@@ -27,7 +27,7 @@ async function handleKnowledge(args) {
27
27
  // Check authentication
28
28
  if (!isAuthenticated()) {
29
29
  console.log(
30
- chalk.red('āŒ Not authenticated. Run'),
30
+ chalk.red('Not authenticated. Run'),
31
31
  chalk.white('lux login'),
32
32
  chalk.red('first.')
33
33
  );
@@ -97,13 +97,13 @@ ${chalk.bold('Examples:')}
97
97
  function printTree(nodes, indent = '') {
98
98
  for (const node of nodes) {
99
99
  if (node.type === 'folder') {
100
- console.log(`${indent}${chalk.blue('šŸ“')} ${chalk.bold(node.name)}/`);
100
+ console.log(`${indent}${chalk.blue('[dir]')} ${chalk.bold(node.name)}/`);
101
101
  if (node.children && node.children.length > 0) {
102
102
  printTree(node.children, indent + ' ');
103
103
  }
104
104
  } else {
105
105
  const size = node.size ? ` (${formatFileSize(node.size)})` : '';
106
- console.log(`${indent}${chalk.gray('šŸ“„')} ${node.name}${chalk.gray(size)}`);
106
+ console.log(`${indent}${chalk.gray('-')} ${node.name}${chalk.gray(size)}`);
107
107
  }
108
108
  }
109
109
  }
package/commands/list.js CHANGED
@@ -6,7 +6,7 @@ async function list(options) {
6
6
  // Check authentication
7
7
  if (!isAuthenticated()) {
8
8
  console.log(
9
- chalk.red('āŒ Not authenticated. Run'),
9
+ chalk.red('Not authenticated. Run'),
10
10
  chalk.white('lux login'),
11
11
  chalk.red('first.')
12
12
  );
@@ -26,43 +26,25 @@ async function list(options) {
26
26
  console.log(chalk.yellow('\nNo interfaces found.'));
27
27
  console.log(
28
28
  chalk.dim('Create one with:'),
29
- chalk.white('lux init'),
30
- chalk.dim('and'),
31
- chalk.white('lux up\n')
29
+ chalk.white('lux i create --name <name>\n')
32
30
  );
33
31
  return;
34
32
  }
35
33
 
36
- console.log(chalk.cyan(`\nšŸ“¦ Interfaces (${interfaces.length})\n`));
34
+ console.log(chalk.cyan(`\nInterfaces (${interfaces.length})\n`));
37
35
 
38
36
  for (const iface of interfaces) {
39
37
  const status = getStatusDisplay(iface.status);
40
38
 
41
39
  console.log(
42
- `šŸ“± ${chalk.white(iface.name)} ${chalk.dim(`(${iface.id})`)}`
40
+ ` ${chalk.white(iface.name)} ${chalk.dim(iface.description || 'No description')} ${chalk.dim(iface.id)} ${status}`
43
41
  );
44
- console.log(
45
- ` ${status} ${chalk.dim(iface.description || 'No description')}`
46
- );
47
-
48
- if (iface.vercel_deployment_url) {
49
- console.log(` ${chalk.cyan(iface.vercel_deployment_url)}`);
50
- }
51
-
52
- if (iface.github_repo_url) {
53
- console.log(` ${chalk.dim(iface.github_repo_url)}`);
54
- }
55
-
56
- console.log('');
57
42
  }
58
43
 
59
- console.log(
60
- chalk.dim('Link to an interface: '),
61
- chalk.white('lux link <interface-id>\n')
62
- );
44
+ console.log('');
63
45
  } catch (error) {
64
46
  console.error(
65
- chalk.red('\nāŒ Error:'),
47
+ chalk.red('Error:'),
66
48
  error.response?.data?.error || error.message
67
49
  );
68
50
 
package/commands/login.js CHANGED
@@ -4,7 +4,7 @@ const chalk = require('chalk');
4
4
  const ora = require('ora');
5
5
  const inquirer = require('inquirer');
6
6
  const axios = require('axios');
7
- const { saveConfig, getApiUrl, loadConfig } = require('../lib/config');
7
+ const { getApiUrl, loadConfig, loadUnifiedConfig, saveUnifiedConfig } = require('../lib/config');
8
8
 
9
9
  async function login(options) {
10
10
  // Non-interactive mode with --key flag
@@ -19,8 +19,8 @@ async function login(options) {
19
19
  name: 'method',
20
20
  message: 'How do you want to authenticate?',
21
21
  choices: [
22
- { name: '🌐 Browser (recommended)', value: 'browser' },
23
- { name: 'šŸ”‘ Enter API key manually', value: 'manual' },
22
+ { name: 'Browser (recommended)', value: 'browser' },
23
+ { name: 'Enter API key manually', value: 'manual' },
24
24
  ],
25
25
  },
26
26
  ]);
@@ -36,7 +36,7 @@ async function browserLogin() {
36
36
  const app = express();
37
37
  const port = 8976;
38
38
 
39
- console.log(chalk.cyan('\nšŸ” Authenticating with Lux...\n'));
39
+ console.log(chalk.cyan('\nAuthenticating with Lux...\n'));
40
40
 
41
41
  return new Promise((resolve, reject) => {
42
42
  let tokenReceived = false;
@@ -58,7 +58,7 @@ async function browserLogin() {
58
58
  </head>
59
59
  <body>
60
60
  <div class="container">
61
- <div class="error">āŒ Authentication Failed</div>
61
+ <div class="error">Authentication Failed</div>
62
62
  <p>Please try again from your terminal.</p>
63
63
  </div>
64
64
  </body>
@@ -95,7 +95,7 @@ async function browserLogin() {
95
95
  </head>
96
96
  <body>
97
97
  <div class="container">
98
- <div class="success">āœ“ Authentication Successful!</div>
98
+ <div class="success">Authentication Successful!</div>
99
99
  <p>You can now close this window and return to your terminal.</p>
100
100
  <p class="info">Org ID: ${orgId}</p>
101
101
  </div>
@@ -115,7 +115,7 @@ async function browserLogin() {
115
115
 
116
116
  try {
117
117
  await open(authUrl);
118
- console.log(chalk.yellow('ā³ Waiting for authentication...\n'));
118
+ console.log(chalk.yellow('Waiting for authentication...\n'));
119
119
  } catch (error) {
120
120
  console.log(chalk.red('Could not open browser automatically.'));
121
121
  console.log(chalk.yellow(`\nPlease open this URL in your browser:\n`));
@@ -135,13 +135,13 @@ async function browserLogin() {
135
135
  );
136
136
  })
137
137
  .then((config) => {
138
- console.log(chalk.green('āœ“ Successfully authenticated!\n'));
138
+ console.log(chalk.green('Successfully authenticated!\n'));
139
139
  console.log(chalk.dim(`Org ID: ${config.orgId}\n`));
140
140
  console.log(chalk.cyan('You can now use Lux CLI commands.\n'));
141
141
  process.exit(0);
142
142
  })
143
143
  .catch((error) => {
144
- console.error(chalk.red('\nāŒ Authentication failed:'), error.message);
144
+ console.error(chalk.red('\nAuthentication failed:'), error.message);
145
145
  process.exit(1);
146
146
  });
147
147
  }
@@ -176,28 +176,33 @@ async function manualLogin(providedKey) {
176
176
  try {
177
177
  const apiUrl = getApiUrl();
178
178
 
179
- // Validate the API key by making a test request
180
- const response = await axios.get(`${apiUrl}/api/flows`, {
179
+ // Validate the API key via the proper user-auth endpoint (SHA-256 hashed lookup)
180
+ const response = await axios.post(`${apiUrl}/api/user-auth/token/validate`, null, {
181
181
  headers: {
182
182
  Authorization: `Bearer ${apiKey}`,
183
- 'X-Org-Id': extractOrgIdFromKey(apiKey),
184
183
  },
185
184
  });
186
185
 
187
- if (response.status === 200) {
188
- spinner.succeed(chalk.green('āœ“ API key validated successfully!'));
186
+ if (response.status === 200 && response.data?.valid) {
187
+ spinner.succeed(chalk.green('API key validated successfully!'));
189
188
 
190
- // Save config
191
- const config = {
192
- token: apiKey,
193
- orgId: extractOrgIdFromKey(apiKey),
194
- timestamp: Date.now(),
195
- };
189
+ // Save credentials to unified config
190
+ const orgId = response.data.org_id || extractOrgIdFromKey(apiKey);
191
+ const unified = loadUnifiedConfig();
192
+ if (!unified.orgs) unified.orgs = {};
193
+ if (!unified.orgs[orgId]) unified.orgs[orgId] = {};
194
+ unified.orgs[orgId].apiKey = apiKey;
195
+ unified.currentOrg = orgId;
196
196
 
197
- saveConfig(config);
197
+ // Restore currentProject if the org has projects and none is set
198
+ if (!unified.currentProject && unified.orgs[orgId]?.projects?.length) {
199
+ unified.currentProject = unified.orgs[orgId].projects[0];
200
+ }
198
201
 
199
- console.log(chalk.green('\nāœ“ Successfully authenticated!\n'));
200
- console.log(chalk.dim(`Org ID: ${config.orgId}\n`));
202
+ saveUnifiedConfig(unified);
203
+
204
+ console.log(chalk.green('\nSuccessfully authenticated!\n'));
205
+ console.log(chalk.dim(`Org ID: ${orgId}\n`));
201
206
  console.log(chalk.cyan('You can now use Lux CLI commands.\n'));
202
207
  }
203
208
  } catch (error) {
@@ -205,11 +210,11 @@ async function manualLogin(providedKey) {
205
210
 
206
211
  if (error.response?.status === 401 || error.response?.status === 403) {
207
212
  console.error(
208
- chalk.red('\nāŒ Invalid or expired API key. Please check your key and try again.\n')
213
+ chalk.red('\nInvalid or expired API key. Please check your key and try again.\n')
209
214
  );
210
215
  } else {
211
216
  console.error(
212
- chalk.red('\nāŒ Error:'),
217
+ chalk.red('\nError:'),
213
218
  error.response?.data?.error || error.message
214
219
  );
215
220
  }
@@ -238,7 +243,7 @@ async function logout() {
238
243
  const { saveConfig } = require('../lib/config');
239
244
  saveConfig({});
240
245
 
241
- console.log(chalk.green('āœ“ Successfully logged out'));
246
+ console.log(chalk.green('Successfully logged out'));
242
247
  }
243
248
 
244
249
  module.exports = {