luxlabs 1.0.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.
@@ -0,0 +1,166 @@
1
+ const axios = require('axios');
2
+ const ora = require('ora');
3
+ const chalk = require('chalk');
4
+ const { execSync } = require('child_process');
5
+ const {
6
+ loadInterfaceConfig,
7
+ saveInterfaceConfig,
8
+ getApiUrl,
9
+ getAuthHeaders,
10
+ isAuthenticated,
11
+ } = require('../lib/config');
12
+
13
+ async function deploy() {
14
+ // Check authentication
15
+ if (!isAuthenticated()) {
16
+ console.log(
17
+ chalk.red('❌ Not authenticated. Run'),
18
+ chalk.white('lux login'),
19
+ chalk.red('first.')
20
+ );
21
+ process.exit(1);
22
+ }
23
+
24
+ // Check if initialized
25
+ const interfaceConfig = loadInterfaceConfig();
26
+ if (!interfaceConfig || !interfaceConfig.id) {
27
+ console.log(
28
+ chalk.red('❌ No interface found. Run'),
29
+ chalk.white('lux up'),
30
+ chalk.red('first.')
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ const apiUrl = getApiUrl();
36
+ const interfaceId = interfaceConfig.id;
37
+
38
+ // Pre-deploy accessibility check
39
+ const spinner = ora('Checking accessibility...').start();
40
+ try {
41
+ execSync('npx eslint . --ext .tsx,.jsx --format stylish', {
42
+ cwd: process.cwd(),
43
+ stdio: 'pipe',
44
+ });
45
+ spinner.succeed(chalk.green('✓ Accessibility check passed'));
46
+ } catch (error) {
47
+ const output = error.stdout?.toString() || error.stderr?.toString() || '';
48
+ // Only fail on jsx-a11y errors, warn on others
49
+ if (output.includes('jsx-a11y')) {
50
+ spinner.fail('Accessibility issues found');
51
+ console.error(chalk.red('\n❌ Fix accessibility issues before deploying:\n'));
52
+ console.error(output);
53
+ process.exit(1);
54
+ } else if (output.trim()) {
55
+ spinner.warn(chalk.yellow('Linting warnings (non-blocking)'));
56
+ console.log(chalk.dim(output));
57
+ } else {
58
+ spinner.succeed(chalk.green('✓ Accessibility check passed'));
59
+ }
60
+ }
61
+
62
+ // Proceed with deploy
63
+ spinner.start('Deploying to production...');
64
+
65
+ try {
66
+ // Trigger deployment
67
+ const { data } = await axios.post(
68
+ `${apiUrl}/api/interfaces/${interfaceId}/deploy`,
69
+ {},
70
+ {
71
+ headers: getAuthHeaders(),
72
+ }
73
+ );
74
+
75
+ spinner.succeed(chalk.green('✓ Deployment started!'));
76
+
77
+ console.log(chalk.dim(`\nDeployment ID: ${data.deploymentId}`));
78
+ console.log(chalk.dim(`Status: ${data.status}\n`));
79
+
80
+ // Poll for deployment status
81
+ await pollDeploymentStatus(interfaceId, apiUrl);
82
+ } catch (error) {
83
+ spinner.fail('Deployment failed');
84
+ console.error(
85
+ chalk.red('\n❌ Error:'),
86
+ error.response?.data?.error || error.message
87
+ );
88
+
89
+ if (error.response?.status === 401) {
90
+ console.log(
91
+ chalk.yellow('\nYour session may have expired. Try running:'),
92
+ chalk.white('lux login')
93
+ );
94
+ }
95
+
96
+ process.exit(1);
97
+ }
98
+ }
99
+
100
+ async function pollDeploymentStatus(interfaceId, apiUrl) {
101
+ const spinner = ora('Building...').start();
102
+ const maxAttempts = 60; // 5 minutes max
103
+ let attempts = 0;
104
+
105
+ while (attempts < maxAttempts) {
106
+ try {
107
+ const { data } = await axios.get(
108
+ `${apiUrl}/api/interfaces/${interfaceId}`,
109
+ { headers: getAuthHeaders() }
110
+ );
111
+
112
+ const status = data.interface.status;
113
+
114
+ if (status === 'published') {
115
+ spinner.succeed(chalk.green('✓ Deployment successful!'));
116
+
117
+ const interfaceConfig = loadInterfaceConfig();
118
+ interfaceConfig.deploymentUrl = data.interface.vercel_deployment_url;
119
+ interfaceConfig.deploymentId = data.interface.vercel_deployment_id;
120
+ saveInterfaceConfig(interfaceConfig);
121
+
122
+ console.log(chalk.cyan('\n🎉 Your interface is live!\n'));
123
+ console.log(chalk.white(` ${data.interface.vercel_deployment_url}\n`));
124
+
125
+ if (data.interface.github_repo_url) {
126
+ console.log(chalk.dim(`GitHub: ${data.interface.github_repo_url}`));
127
+ }
128
+
129
+ return;
130
+ } else if (status === 'failed') {
131
+ spinner.fail('Deployment failed');
132
+
133
+ console.log(
134
+ chalk.red('\n❌ Build failed. View logs with:'),
135
+ chalk.white('lux logs --type build')
136
+ );
137
+
138
+ if (data.interface.error_message) {
139
+ console.log(chalk.dim(`\nError: ${data.interface.error_message}`));
140
+ }
141
+
142
+ process.exit(1);
143
+ } else if (status === 'building') {
144
+ spinner.text = 'Building... (this may take a few minutes)';
145
+ }
146
+
147
+ // Wait 5 seconds before next poll
148
+ await new Promise((resolve) => setTimeout(resolve, 5000));
149
+ attempts++;
150
+ } catch (error) {
151
+ spinner.fail('Failed to check deployment status');
152
+ console.error(chalk.red('\n❌ Error:'), error.message);
153
+ process.exit(1);
154
+ }
155
+ }
156
+
157
+ spinner.fail('Deployment timed out');
158
+ console.log(
159
+ chalk.yellow('\n⚠️ Deployment is still in progress. Check status with:'),
160
+ chalk.white('lux logs')
161
+ );
162
+ }
163
+
164
+ module.exports = {
165
+ deploy,
166
+ };