create-fleetbo-project 1.2.40 → 1.2.42
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/install-react-template.js +102 -75
- package/package.json +1 -1
|
@@ -31,12 +31,13 @@ const PORT = 3000;
|
|
|
31
31
|
const args = process.argv.slice(2);
|
|
32
32
|
const command = args[0];
|
|
33
33
|
|
|
34
|
+
process.env.DOTENV_SILENT = 'true';
|
|
34
35
|
const envPath = path.join(process.cwd(), '.env');
|
|
35
36
|
if (!fs.existsSync(envPath)) {
|
|
36
37
|
console.error('\\\\x1b[31m%s\\\\x1b[0m', '❌ Error: Configuration file (.env) not found.');
|
|
37
38
|
process.exit(1);
|
|
38
39
|
}
|
|
39
|
-
dotenv.config({ path: envPath });
|
|
40
|
+
dotenv.config({ path: envPath, quiet: true });
|
|
40
41
|
|
|
41
42
|
const projectId = process.env.REACT_APP_ENTERPRISE_ID;
|
|
42
43
|
const keyApp = process.env.REACT_KEY_APP;
|
|
@@ -63,24 +64,29 @@ if (!projectId) {
|
|
|
63
64
|
process.exit(1);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const injectRouteIntoAppJs = (pageName) => {
|
|
67
|
+
const injectRouteIntoAppJs = (pageName, subPath = '') => {
|
|
67
68
|
if (!fs.existsSync(APP_JS_PATH)) return false;
|
|
68
69
|
let content = fs.readFileSync(APP_JS_PATH, 'utf8');
|
|
69
|
-
|
|
70
|
+
|
|
71
|
+
const importAnchor = '// FLEETBO_MORE_IMPORTS';
|
|
72
|
+
const alexRouteAnchor = '{/* FLEETBO_DYNAMIC ROUTES */}';
|
|
73
|
+
|
|
74
|
+
if (!content.includes(alexRouteAnchor)) return false;
|
|
70
75
|
|
|
71
|
-
const
|
|
72
|
-
const
|
|
76
|
+
const pathPrefix = subPath ? \\\`\\\${subPath}/\\\` : '';
|
|
77
|
+
const importLine = \\\`import \\\${pageName} from './pages/\\\${pathPrefix}\\\${pageName}';\\\`;
|
|
78
|
+
const routeLine = \\\`<Route path="/\\\${pathPrefix.toLowerCase()}\\\${pageName.toLowerCase()}" element=<\\\${pageName} /> />\\\`;
|
|
73
79
|
|
|
74
80
|
let injected = false;
|
|
75
|
-
if (!content.includes(importLine)
|
|
76
|
-
content = content.replace(
|
|
81
|
+
if (!content.includes(importLine)) {
|
|
82
|
+
content = content.replace(importAnchor, \\\`\\\${importLine}\\\\n\\\${importAnchor}\\\`);
|
|
77
83
|
injected = true;
|
|
78
84
|
}
|
|
79
|
-
if (!content.includes(routeLine)
|
|
80
|
-
|
|
81
|
-
content = content.replace(anchor, \`\\\${routeLine}\\\\n \\\${anchor}\`);
|
|
85
|
+
if (!content.includes(routeLine)) {
|
|
86
|
+
content = content.replace(alexRouteAnchor, \\\`\\\${routeLine}\\\\n \\\${alexRouteAnchor}\\\`);
|
|
82
87
|
injected = true;
|
|
83
88
|
}
|
|
89
|
+
|
|
84
90
|
if (injected) fs.writeFileSync(APP_JS_PATH, content);
|
|
85
91
|
return injected;
|
|
86
92
|
};
|
|
@@ -89,7 +95,7 @@ const showEnergyTransfer = async () => {
|
|
|
89
95
|
const width = 30;
|
|
90
96
|
for (let i = 0; i <= width; i++) {
|
|
91
97
|
const dots = "█".repeat(i); const empty = "░".repeat(width - i);
|
|
92
|
-
process.stdout.write(
|
|
98
|
+
process.stdout.write(\\\`\\\\r \\\\x1b[32m⚡ Alex Energy Sync:\\\\x1b[0m [\\\${dots}\\\${empty}] \\\${Math.round((i / width) * 100)}%\\\`);
|
|
93
99
|
await new Promise(r => setTimeout(r, 45));
|
|
94
100
|
}
|
|
95
101
|
process.stdout.write('\\\\n');
|
|
@@ -100,68 +106,88 @@ if (command === 'alex') {
|
|
|
100
106
|
const initialPrompt = args.slice(1).join(' ');
|
|
101
107
|
|
|
102
108
|
const processAlexRequest = async (prompt) => {
|
|
103
|
-
process.stdout.write('\\\\
|
|
109
|
+
process.stdout.write('\\\\x1b[33m🧠 Alex is thinking...\\\\x1b[0m');
|
|
104
110
|
try {
|
|
105
111
|
const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
|
|
106
112
|
headers: { 'x-project-id': projectId }
|
|
107
113
|
});
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
|
|
115
|
+
const aiData = result.data;
|
|
116
|
+
process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
|
|
117
|
+
|
|
118
|
+
if (aiData.status === 'quota_exceeded') {
|
|
119
|
+
console.log(\\\`\\\\n\\\\x1b[31m⛔ ENGINE OUT OF FUEL:\\\\x1b[0m \\\${aiData.message}\\\`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (aiData.status === 'success' || aiData.status === 'message') {
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(\\\`\\\\x1b[32mAlex ❯\\\\x1b[0m \\\${aiData.message || "I'm ready."}\\\`);
|
|
126
|
+
|
|
127
|
+
if (aiData.remainingTokens !== undefined) {
|
|
128
|
+
const remaining = aiData.remainingTokens;
|
|
129
|
+
const limit = aiData.limit || 2500;
|
|
130
|
+
const percent = Math.round((remaining / limit) * 100);
|
|
131
|
+
const energyColor = percent > 30 ? '\\\\x1b[32m' : '\\\\x1b[31m';
|
|
132
|
+
console.log(\\\`\\\\x1b[36m📊 Energy:\\\\x1b[0m \\\${energyColor}\\\${percent}%\\\\x1b[0m (\\\${remaining}/\\\${limit} tokens left)\\\`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
110
135
|
|
|
111
136
|
if (aiData.status === 'success' && aiData.moduleData) {
|
|
112
|
-
const { fileName, code, mockFileName, mockCode } = aiData.moduleData;
|
|
137
|
+
const { fileName, code, mockFileName, mockCode, moduleName } = aiData.moduleData;
|
|
138
|
+
console.log(\\\` \\\\x1b[90m⚙️ Architecting: \\\${moduleName}\\\\x1b[0m\\\`);
|
|
139
|
+
|
|
113
140
|
const writeFile = (dir, name, content) => {
|
|
114
141
|
const fullPath = path.join(process.cwd(), dir);
|
|
115
142
|
const filePath = path.join(fullPath, name);
|
|
116
143
|
if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const backupPath = filePath.replace(/(\\\\.jsx|\\\\.kt|\\\\.swift)$/, '.original\\\$1');
|
|
120
|
-
fs.copyFileSync(filePath, backupPath);
|
|
121
|
-
fs.writeFileSync(filePath, content);
|
|
122
|
-
console.log(\` ✅ \\\\x1b[33m[Updated]\\\\x1b[0m \\\${dir}\\\${name} \\\\x1b[90m(Backup created)\\\\x1b[0m\`);
|
|
123
|
-
} else {
|
|
124
|
-
fs.writeFileSync(filePath, content);
|
|
125
|
-
console.log(\` ✅ \\\\x1b[32m[Created]\\\\x1b[0m \\\${dir}\\\${name}\`);
|
|
126
|
-
}
|
|
144
|
+
fs.writeFileSync(filePath, content);
|
|
145
|
+
console.log(\\\` ✅ \\\\x1b[32m[Written]\\\\x1b[0m \\\${dir}\\\${name}\\\`);
|
|
127
146
|
};
|
|
128
|
-
|
|
129
|
-
if (code && fileName
|
|
130
|
-
|
|
147
|
+
|
|
148
|
+
if (code && fileName) {
|
|
149
|
+
const folder = fileName.endsWith('.kt') ? 'public/native/android/' : 'src/pages/';
|
|
150
|
+
writeFile(folder, fileName, code);
|
|
151
|
+
if (fileName.endsWith('.jsx')) injectRouteIntoAppJs(fileName.replace('.jsx', ''));
|
|
131
152
|
}
|
|
132
153
|
if (mockCode && mockFileName) {
|
|
133
154
|
writeFile('src/pages/mocks/', mockFileName, mockCode);
|
|
134
|
-
writeFile('src/pages/mocks/', 'Quick.
|
|
135
|
-
}
|
|
136
|
-
if (code && fileName && fileName.endsWith('.jsx') && !mockCode) {
|
|
137
|
-
const pageName = fileName.replace('.jsx', '');
|
|
138
|
-
writeFile('src/pages/', fileName, code);
|
|
139
|
-
injectRouteIntoAppJs(pageName);
|
|
155
|
+
writeFile('src/pages/mocks/', 'Quick.jsx', mockCode);
|
|
140
156
|
}
|
|
141
|
-
console.log('\\\\n\\\\x1b[32mAlex ❯\\\\x1b[0m ' + aiData.message);
|
|
142
157
|
}
|
|
143
|
-
} catch (error) {
|
|
158
|
+
} catch (error) {
|
|
159
|
+
process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
|
|
160
|
+
console.error('\\\\n\\\\x1b[31m❌ Alex Error:\\\\x1b[0m ' + (error.response?.data?.message || error.message));
|
|
161
|
+
}
|
|
144
162
|
};
|
|
145
163
|
|
|
146
164
|
const startAlexSession = async () => {
|
|
147
|
-
process.stdout.write('\\\\
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
process.stdout.write('\\\\x1b[33m🛡️ Alex is checking runtime state...\\\\x1b[0m\\\\r');
|
|
166
|
+
let attempts = 0; const maxAttempts = 5; let isReady = false;
|
|
167
|
+
while (attempts < maxAttempts && !isReady) {
|
|
168
|
+
try {
|
|
169
|
+
const validation = await axios.post(ALEX_ENGINE_URL, { prompt: "ping", validateProject: true, checkNetwork: true }, { headers: { 'x-project-id': projectId }, timeout: 5000 });
|
|
170
|
+
if (validation.data?.isRunning) { isReady = true; break; }
|
|
171
|
+
attempts++; if (attempts < maxAttempts) await new Promise(r => setTimeout(r, 2000));
|
|
172
|
+
} catch (error) { attempts++; await new Promise(r => setTimeout(r, 2000)); }
|
|
173
|
+
}
|
|
174
|
+
if (!isReady) {
|
|
175
|
+
console.error('\\\\n\\\\x1b[31m⚠️ ENGINE OFFLINE:\\\\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
process.stdout.write(' '.repeat(60) + '\\\\r');
|
|
179
|
+
console.log('\\\\n\\\\x1b[32m🤖 Alex is now online.\\\\x1b[0m');
|
|
180
|
+
console.log('\\\\x1b[32mAlex ❯\\\\x1b[0m Welcome! I am Alex, your architect. What are we building today?\\\\n');
|
|
181
|
+
const userName = testerEmail ? testerEmail.split('@')[0] : 'Dev';
|
|
182
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: \\\`\\\\x1b[34m\\\${userName} ❯ \\\\x1b[0m\\\` });
|
|
183
|
+
rl.prompt();
|
|
184
|
+
rl.on('line', async (line) => {
|
|
185
|
+
if (['exit', 'quit'].includes(line.trim().toLowerCase())) { console.log('\\\\n\\\\x1b[90m👋 Alex session closed.\\\\x1b[0m'); rl.close(); return; }
|
|
186
|
+
if (line.trim()) { await processAlexRequest(line.trim()); console.log(''); }
|
|
157
187
|
rl.prompt();
|
|
158
|
-
|
|
159
|
-
if (['exit', 'quit', 'bye'].includes(line.trim().toLowerCase())) { rl.close(); return; }
|
|
160
|
-
if (line.trim()) await processAlexRequest(line.trim());
|
|
161
|
-
rl.prompt();
|
|
162
|
-
}).on('close', () => { console.log('\\\\n👋 Alex going offline.'); process.exit(0); });
|
|
163
|
-
} catch (error) { process.exit(1); }
|
|
188
|
+
}).on('close', () => { process.exit(0); });
|
|
164
189
|
};
|
|
190
|
+
|
|
165
191
|
if (!initialPrompt || initialPrompt === '?') startAlexSession();
|
|
166
192
|
else processAlexRequest(initialPrompt);
|
|
167
193
|
return;
|
|
@@ -192,21 +218,19 @@ if (command === 'deploy') {
|
|
|
192
218
|
|
|
193
219
|
const GENERATOR_COMMANDS = ['page', 'g', 'generate', 'android', 'ios'];
|
|
194
220
|
if (GENERATOR_COMMANDS.includes(command)) {
|
|
195
|
-
try { require('./page.js'); } catch (
|
|
221
|
+
try { require('./page.js'); } catch (e) { console.error(e.message); process.exit(1); }
|
|
196
222
|
return;
|
|
197
223
|
}
|
|
198
224
|
|
|
199
225
|
const NULL_DEV = process.platform === 'win32' ? '>nul 2>&1' : '2>/dev/null';
|
|
200
|
-
|
|
201
226
|
function killProcessOnPort(port) {
|
|
202
227
|
try {
|
|
203
228
|
if (process.platform !== 'win32') {
|
|
204
|
-
const pid = execSync(
|
|
205
|
-
if (pid)
|
|
229
|
+
const pid = execSync(\\\`lsof -ti:\\\${port} \\\${NULL_DEV}\\\`).toString().trim();
|
|
230
|
+
if (pid) execSync(\\\`kill -9 \\\${pid.split('\\\\n').join(' ')} \\\${NULL_DEV}\\\`);
|
|
206
231
|
}
|
|
207
232
|
} catch (e) {}
|
|
208
233
|
}
|
|
209
|
-
|
|
210
234
|
function killNetworkService() {
|
|
211
235
|
try {
|
|
212
236
|
const cmd = process.platform === 'win32' ? 'taskkill /IM cloudflared.exe /F' : 'pkill cloudflared';
|
|
@@ -216,14 +240,10 @@ function killNetworkService() {
|
|
|
216
240
|
|
|
217
241
|
let isExiting = false;
|
|
218
242
|
async function cleanupAndExit(code = 0) {
|
|
219
|
-
if (isExiting) return;
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
|
|
223
|
-
} catch (e) {}
|
|
224
|
-
killNetworkService();
|
|
225
|
-
killProcessOnPort(PORT);
|
|
226
|
-
process.exit(code);
|
|
243
|
+
if (isExiting) return; isExiting = true;
|
|
244
|
+
console.log('\\\\n[Fleetbo] 🛑 Stopping environment...');
|
|
245
|
+
try { await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail }); } catch (e) {}
|
|
246
|
+
killNetworkService(); killProcessOnPort(PORT); process.exit(code);
|
|
227
247
|
}
|
|
228
248
|
process.on('SIGINT', () => cleanupAndExit(0));
|
|
229
249
|
process.on('SIGTERM', () => cleanupAndExit(0));
|
|
@@ -231,30 +251,37 @@ process.on('SIGTERM', () => cleanupAndExit(0));
|
|
|
231
251
|
async function syncFirebase(keyApp, networkUrl, testerEmail) {
|
|
232
252
|
try {
|
|
233
253
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
234
|
-
console.log(
|
|
235
|
-
console.log(
|
|
236
|
-
|
|
254
|
+
console.log(\\\`\\\\n\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\\\`);
|
|
255
|
+
console.log('\\\\x1b[32m[Fleetbo] GO GO GO ! FLEETBO STUDIO IS READY \\\\x1b[0m');
|
|
256
|
+
console.log(\\\`\\\\x1b[32m[Fleetbo] Link: https://fleetbo.io/studio/\\\${keyApp}\\\\x1b[0m\\\`);
|
|
257
|
+
console.log('\\\\x1b[32m[Fleetbo] You can now start coding and previewing in Studio. 🚀\\\\x1b[0m\\\`);
|
|
258
|
+
console.log(\\\`\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\\\`);
|
|
259
|
+
} catch (err) { console.error(\\\`[Fleetbo] ❌ Sync Error: \\\${err.message}\\\`); }
|
|
237
260
|
}
|
|
238
261
|
|
|
239
262
|
async function runDevEnvironment() {
|
|
240
|
-
|
|
241
|
-
killProcessOnPort(PORT);
|
|
263
|
+
console.log(\\\`[Fleetbo] 🛡️ Initializing Dev Environment...\\\`);
|
|
264
|
+
killNetworkService(); killProcessOnPort(PORT);
|
|
242
265
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
243
266
|
const devServer = spawn(npmCmd, ['start'], { stdio: ['ignore', 'pipe', 'pipe'], shell: true, env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() } });
|
|
267
|
+
devServer.stdout.pipe(process.stdout); devServer.stderr.pipe(process.stderr);
|
|
268
|
+
let connectionStarted = false;
|
|
244
269
|
devServer.stdout.on('data', (data) => {
|
|
245
270
|
const output = data.toString();
|
|
246
|
-
if (output.includes('Local:') || output.includes('Compiled successfully')) {
|
|
271
|
+
if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
272
|
+
connectionStarted = true;
|
|
273
|
+
console.log('\\\\n[Fleetbo] ---------------------------------------------------');
|
|
274
|
+
console.log(\\\`[Fleetbo] 🔗 Establishing Secure Uplink...\\\\n[Fleetbo] 🛑 DO NOT open the Fleetbo Studio yet.\\\\n[Fleetbo] ⏳ Please wait green message...\\\\n[Fleetbo] ---------------------------------------------------\\\`);
|
|
247
275
|
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
248
|
-
const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url',
|
|
276
|
+
const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \\\`http://localhost:\\\${PORT}\\\`], { shell: true });
|
|
249
277
|
uplink.stderr.on('data', (chunk) => {
|
|
250
|
-
const match =
|
|
278
|
+
const match = chunk.toString().match(/https:\\\\/\\\\/[a-zA-Z0-9-]+\\\\.trycloudflare\\\\.com/);
|
|
251
279
|
if (match) syncFirebase(keyApp, match[0], testerEmail);
|
|
252
280
|
});
|
|
253
281
|
}
|
|
254
282
|
});
|
|
255
283
|
}
|
|
256
|
-
runDevEnvironment()
|
|
257
|
-
`;
|
|
284
|
+
runDevEnvironment();`;
|
|
258
285
|
|
|
259
286
|
|
|
260
287
|
const args = process.argv.slice(2);
|