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.
- package/README.md +16 -21
- package/commands/ab-tests.js +14 -11
- package/commands/agents.js +11 -11
- package/commands/data.js +19 -17
- package/commands/deploy.js +145 -82
- package/commands/flows.js +152 -133
- package/commands/interface/init.js +36 -35
- package/commands/interface.js +135 -10
- package/commands/knowledge.js +3 -3
- package/commands/list.js +6 -24
- package/commands/login.js +31 -26
- package/commands/logout.js +13 -4
- package/commands/logs.js +17 -66
- package/commands/project.js +74 -47
- package/commands/secrets.js +1 -1
- package/commands/servers.js +9 -113
- package/commands/storage.js +1 -1
- package/commands/tools.js +4 -4
- package/commands/validate-data-lux.js +5 -2
- package/commands/voice-agents.js +22 -18
- package/lib/config.js +235 -83
- package/lib/helpers.js +6 -4
- package/lux.js +4 -94
- package/package.json +6 -1
- package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +41 -34
- package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +41 -34
- package/templates/interface-boilerplate/components/providers/posthog-provider.tsx +41 -26
- package/templates/interface-boilerplate/gitignore.template +4 -0
- package/templates/interface-boilerplate/lib/auth.config.ts +3 -2
- package/templates/interface-boilerplate/lib/knowledge.ts +2 -2
- package/templates/interface-boilerplate/middleware.ts +14 -3
- package/templates/interface-boilerplate/next-env.d.ts +6 -0
- package/templates/interface-boilerplate/package-lock.json +432 -8
- package/commands/dev.js +0 -578
- package/commands/init.js +0 -126
- package/commands/link.js +0 -127
- package/commands/up.js +0 -211
package/commands/link.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
const ora = require('ora');
|
|
4
|
-
const {
|
|
5
|
-
saveInterfaceConfig,
|
|
6
|
-
loadInterfaceConfig,
|
|
7
|
-
getApiUrl,
|
|
8
|
-
getAuthHeaders,
|
|
9
|
-
isAuthenticated,
|
|
10
|
-
} = require('../lib/config');
|
|
11
|
-
|
|
12
|
-
async function link(interfaceId) {
|
|
13
|
-
// Check authentication
|
|
14
|
-
if (!isAuthenticated()) {
|
|
15
|
-
console.log(
|
|
16
|
-
chalk.red('❌ Not authenticated. Run'),
|
|
17
|
-
chalk.white('lux login'),
|
|
18
|
-
chalk.red('first.')
|
|
19
|
-
);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!interfaceId) {
|
|
24
|
-
console.log(chalk.red('❌ Interface ID required.'));
|
|
25
|
-
console.log(chalk.dim('Usage:'), chalk.white('lux link <interface-id>'));
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Check if already linked
|
|
30
|
-
const existing = loadInterfaceConfig();
|
|
31
|
-
if (existing && existing.id) {
|
|
32
|
-
console.log(
|
|
33
|
-
chalk.yellow('⚠️ Already linked to:'),
|
|
34
|
-
existing.name,
|
|
35
|
-
chalk.dim(`(${existing.id})`)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const inquirer = require('inquirer');
|
|
39
|
-
const { confirm } = await inquirer.prompt([
|
|
40
|
-
{
|
|
41
|
-
type: 'confirm',
|
|
42
|
-
name: 'confirm',
|
|
43
|
-
message: 'Do you want to relink to a different interface?',
|
|
44
|
-
default: false,
|
|
45
|
-
},
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
if (!confirm) {
|
|
49
|
-
console.log(chalk.dim('Cancelled.'));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const apiUrl = getApiUrl();
|
|
55
|
-
const spinner = ora('Fetching interface...').start();
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
// Fetch interface details
|
|
59
|
-
const { data } = await axios.get(
|
|
60
|
-
`${apiUrl}/api/interfaces/${interfaceId}`,
|
|
61
|
-
{
|
|
62
|
-
headers: getAuthHeaders(),
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const iface = data.interface;
|
|
67
|
-
|
|
68
|
-
if (!iface) {
|
|
69
|
-
spinner.fail('Interface not found');
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
spinner.succeed(chalk.green('✓ Found interface'));
|
|
74
|
-
|
|
75
|
-
// Save config
|
|
76
|
-
const config = {
|
|
77
|
-
id: iface.id,
|
|
78
|
-
name: iface.name,
|
|
79
|
-
description: iface.description,
|
|
80
|
-
githubRepoUrl: iface.github_repo_url,
|
|
81
|
-
deploymentUrl: iface.vercel_deployment_url,
|
|
82
|
-
linkedAt: new Date().toISOString(),
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
saveInterfaceConfig(config);
|
|
86
|
-
|
|
87
|
-
console.log(chalk.green('\n✓ Linked successfully!\n'));
|
|
88
|
-
console.log(chalk.white(iface.name));
|
|
89
|
-
console.log(chalk.dim(iface.description || 'No description'));
|
|
90
|
-
|
|
91
|
-
if (iface.vercel_deployment_url) {
|
|
92
|
-
console.log(chalk.cyan(`\n${iface.vercel_deployment_url}`));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log(chalk.dim('\nYou can now run:'));
|
|
96
|
-
console.log(chalk.white(' lux up'), chalk.dim('- Upload changes'));
|
|
97
|
-
console.log(
|
|
98
|
-
chalk.white(' lux deploy'),
|
|
99
|
-
chalk.dim('- Deploy to production')
|
|
100
|
-
);
|
|
101
|
-
console.log(chalk.white(' lux logs'), chalk.dim('- View logs\n'));
|
|
102
|
-
} catch (error) {
|
|
103
|
-
spinner.fail('Failed to link');
|
|
104
|
-
console.error(
|
|
105
|
-
chalk.red('\n❌ Error:'),
|
|
106
|
-
error.response?.data?.error || error.message
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
if (error.response?.status === 401) {
|
|
110
|
-
console.log(
|
|
111
|
-
chalk.yellow('\nYour session may have expired. Try running:'),
|
|
112
|
-
chalk.white('lux login')
|
|
113
|
-
);
|
|
114
|
-
} else if (error.response?.status === 404) {
|
|
115
|
-
console.log(
|
|
116
|
-
chalk.yellow('\nInterface not found. Check the ID and try again.')
|
|
117
|
-
);
|
|
118
|
-
console.log(chalk.dim('List interfaces with:'), chalk.white('lux list'));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
module.exports = {
|
|
126
|
-
link,
|
|
127
|
-
};
|
package/commands/up.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const archiver = require('archiver');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const ora = require('ora');
|
|
6
|
-
const chalk = require('chalk');
|
|
7
|
-
const ignore = require('ignore');
|
|
8
|
-
const {
|
|
9
|
-
loadConfig,
|
|
10
|
-
loadInterfaceConfig,
|
|
11
|
-
saveInterfaceConfig,
|
|
12
|
-
getApiUrl,
|
|
13
|
-
getAuthHeaders,
|
|
14
|
-
isAuthenticated,
|
|
15
|
-
} = require('../lib/config');
|
|
16
|
-
|
|
17
|
-
async function up(options) {
|
|
18
|
-
// Check authentication
|
|
19
|
-
if (!isAuthenticated()) {
|
|
20
|
-
console.log(
|
|
21
|
-
chalk.red('❌ Not authenticated. Run'),
|
|
22
|
-
chalk.white('lux login'),
|
|
23
|
-
chalk.red('first.')
|
|
24
|
-
);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check if initialized
|
|
29
|
-
const interfaceConfig = loadInterfaceConfig();
|
|
30
|
-
if (!interfaceConfig) {
|
|
31
|
-
console.log(
|
|
32
|
-
chalk.red('❌ Not initialized. Run'),
|
|
33
|
-
chalk.white('lux init'),
|
|
34
|
-
chalk.red('first.')
|
|
35
|
-
);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const config = loadConfig();
|
|
40
|
-
const apiUrl = getApiUrl();
|
|
41
|
-
const spinner = ora('Preparing upload...').start();
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
let interfaceId = interfaceConfig.id;
|
|
45
|
-
|
|
46
|
-
// Step 1: Create interface if it doesn't exist
|
|
47
|
-
if (!interfaceId) {
|
|
48
|
-
spinner.text = 'Creating interface...';
|
|
49
|
-
|
|
50
|
-
const { data } = await axios.post(
|
|
51
|
-
`${apiUrl}/api/interfaces`,
|
|
52
|
-
{
|
|
53
|
-
name: interfaceConfig.name,
|
|
54
|
-
description: interfaceConfig.description,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
headers: getAuthHeaders(),
|
|
58
|
-
}
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
interfaceId = data.interface.id;
|
|
62
|
-
interfaceConfig.id = interfaceId;
|
|
63
|
-
interfaceConfig.githubRepoUrl = data.interface.github_repo_url;
|
|
64
|
-
saveInterfaceConfig(interfaceConfig);
|
|
65
|
-
|
|
66
|
-
spinner.succeed(chalk.green(`Created interface: ${interfaceId}`));
|
|
67
|
-
spinner.start();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Step 2: Create zip file
|
|
71
|
-
spinner.text = 'Creating zip file...';
|
|
72
|
-
const zipPath = await createZip(process.cwd());
|
|
73
|
-
const stats = fs.statSync(zipPath);
|
|
74
|
-
spinner.text = `Created zip (${formatBytes(stats.size)})...`;
|
|
75
|
-
|
|
76
|
-
// Step 3: Get presigned upload URL
|
|
77
|
-
spinner.text = 'Getting upload URL...';
|
|
78
|
-
const { data: urlData } = await axios.post(
|
|
79
|
-
`${apiUrl}/api/interfaces/${interfaceId}/presigned-urls`,
|
|
80
|
-
{ zip_upload: true },
|
|
81
|
-
{ headers: getAuthHeaders() }
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// Step 4: Upload zip to R2
|
|
85
|
-
spinner.text = 'Uploading files...';
|
|
86
|
-
await axios.put(urlData.upload_url, fs.readFileSync(zipPath), {
|
|
87
|
-
headers: { 'Content-Type': 'application/zip' },
|
|
88
|
-
maxBodyLength: Infinity,
|
|
89
|
-
maxContentLength: Infinity,
|
|
90
|
-
onUploadProgress: (progressEvent) => {
|
|
91
|
-
const percent = Math.round(
|
|
92
|
-
(progressEvent.loaded * 100) / progressEvent.total
|
|
93
|
-
);
|
|
94
|
-
spinner.text = `Uploading files... ${percent}%`;
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Step 5: Trigger extraction and GitHub sync
|
|
99
|
-
spinner.text = 'Syncing to GitHub...';
|
|
100
|
-
const { data: uploadResult } = await axios.post(
|
|
101
|
-
`${apiUrl}/api/interfaces/${interfaceId}/upload-files`,
|
|
102
|
-
{ zip_path: urlData.storage_path },
|
|
103
|
-
{ headers: getAuthHeaders() }
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// Cleanup
|
|
107
|
-
fs.unlinkSync(zipPath);
|
|
108
|
-
|
|
109
|
-
spinner.succeed(
|
|
110
|
-
chalk.green(`✓ Uploaded ${uploadResult.files_uploaded} files`)
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
console.log(chalk.dim(`\n${uploadResult.message}\n`));
|
|
114
|
-
console.log(chalk.cyan('Files synced to dev branch. Ready to deploy!'));
|
|
115
|
-
console.log(
|
|
116
|
-
chalk.dim(`Run ${chalk.white('lux deploy')} to publish to production\n`)
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
if (interfaceConfig.githubRepoUrl) {
|
|
120
|
-
console.log(chalk.dim(`GitHub: ${interfaceConfig.githubRepoUrl}`));
|
|
121
|
-
}
|
|
122
|
-
} catch (error) {
|
|
123
|
-
spinner.fail('Upload failed');
|
|
124
|
-
console.error(
|
|
125
|
-
chalk.red('\n❌ Error:'),
|
|
126
|
-
error.response?.data?.error || error.message
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
if (error.response?.status === 401) {
|
|
130
|
-
console.log(
|
|
131
|
-
chalk.yellow('\nYour session may have expired. Try running:'),
|
|
132
|
-
chalk.white('lux login')
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function createZip(sourceDir) {
|
|
141
|
-
const output = fs.createWriteStream('/tmp/lux-upload.zip');
|
|
142
|
-
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
143
|
-
|
|
144
|
-
// Load .gitignore patterns
|
|
145
|
-
const ig = ignore();
|
|
146
|
-
const gitignorePath = path.join(sourceDir, '.gitignore');
|
|
147
|
-
|
|
148
|
-
if (fs.existsSync(gitignorePath)) {
|
|
149
|
-
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
150
|
-
ig.add(gitignoreContent);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Add default ignores (same as server)
|
|
154
|
-
ig.add([
|
|
155
|
-
'node_modules/',
|
|
156
|
-
'.next/',
|
|
157
|
-
'.git/',
|
|
158
|
-
'.env.local',
|
|
159
|
-
'.DS_Store',
|
|
160
|
-
'dist/',
|
|
161
|
-
'build/',
|
|
162
|
-
'.cache/',
|
|
163
|
-
'coverage/',
|
|
164
|
-
'.lux/interface.json', // Don't upload local config
|
|
165
|
-
]);
|
|
166
|
-
|
|
167
|
-
return new Promise((resolve, reject) => {
|
|
168
|
-
output.on('close', () => resolve('/tmp/lux-upload.zip'));
|
|
169
|
-
archive.on('error', reject);
|
|
170
|
-
|
|
171
|
-
archive.pipe(output);
|
|
172
|
-
|
|
173
|
-
// Recursively add files
|
|
174
|
-
function addDirectory(dirPath, baseDir = '') {
|
|
175
|
-
const files = fs.readdirSync(dirPath);
|
|
176
|
-
|
|
177
|
-
for (const file of files) {
|
|
178
|
-
const fullPath = path.join(dirPath, file);
|
|
179
|
-
const relativePath = path.join(baseDir, file);
|
|
180
|
-
|
|
181
|
-
// Check if ignored
|
|
182
|
-
if (ig.ignores(relativePath)) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const stat = fs.statSync(fullPath);
|
|
187
|
-
|
|
188
|
-
if (stat.isDirectory()) {
|
|
189
|
-
addDirectory(fullPath, relativePath);
|
|
190
|
-
} else if (stat.isFile()) {
|
|
191
|
-
archive.file(fullPath, { name: relativePath });
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
addDirectory(sourceDir);
|
|
197
|
-
archive.finalize();
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function formatBytes(bytes) {
|
|
202
|
-
if (bytes === 0) return '0 Bytes';
|
|
203
|
-
const k = 1024;
|
|
204
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
205
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
206
|
-
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
module.exports = {
|
|
210
|
-
up,
|
|
211
|
-
};
|