create-fleetbo-project 1.2.41 → 1.2.43
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 +77 -158
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@ const command = args[0];
|
|
|
34
34
|
process.env.DOTENV_SILENT = 'true';
|
|
35
35
|
const envPath = path.join(process.cwd(), '.env');
|
|
36
36
|
if (!fs.existsSync(envPath)) {
|
|
37
|
-
console.error('
|
|
37
|
+
console.error('\\x1b[31m%s\\x1b[0m', '❌ Error: Configuration file (.env) not found.');
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
dotenv.config({ path: envPath, quiet: true });
|
|
@@ -48,19 +48,19 @@ const checkGitSecurity = () => {
|
|
|
48
48
|
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
49
49
|
if (fs.existsSync(gitDir)) {
|
|
50
50
|
if (!fs.existsSync(gitignorePath)) {
|
|
51
|
-
console.error('
|
|
51
|
+
console.error('\\n\\x1b[31m🚨 SECURITY ALERT:\\x1b[0m .git detected but no .gitignore found.');
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|
|
54
54
|
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
55
55
|
if (!gitignoreContent.includes('.env')) {
|
|
56
|
-
console.error('
|
|
56
|
+
console.error('\\n\\x1b[31m🚨 CRITICAL RISK:\\x1b[0m .env is NOT ignored by Git.');
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
if (!projectId) {
|
|
63
|
-
console.error('
|
|
63
|
+
console.error('\\n❌ Error: Project ID missing in .env.\\n');
|
|
64
64
|
process.exit(1);
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -73,19 +73,17 @@ const injectRouteIntoAppJs = (pageName, subPath = '') => {
|
|
|
73
73
|
|
|
74
74
|
if (!content.includes(alexRouteAnchor)) return false;
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const routeLine = \`<Route path="/\\\${pathPrefix.toLowerCase()}\\\${pageName.toLowerCase()}" element=<\\\${\\\${pageName} />} />\`;
|
|
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} />} />\`;
|
|
80
79
|
|
|
81
80
|
let injected = false;
|
|
82
81
|
if (!content.includes(importLine)) {
|
|
83
|
-
content = content.replace(importAnchor,
|
|
82
|
+
content = content.replace(importAnchor, \`\${importLine}\\n\${importAnchor}\`);
|
|
84
83
|
injected = true;
|
|
85
84
|
}
|
|
86
85
|
if (!content.includes(routeLine)) {
|
|
87
|
-
|
|
88
|
-
content = content.replace(alexRouteAnchor, \`\\\${routeLine}\\\\n \\\${alexRouteAnchor}\`);
|
|
86
|
+
content = content.replace(alexRouteAnchor, \`\${routeLine}\\n \${alexRouteAnchor}\`);
|
|
89
87
|
injected = true;
|
|
90
88
|
}
|
|
91
89
|
|
|
@@ -96,11 +94,11 @@ const injectRouteIntoAppJs = (pageName, subPath = '') => {
|
|
|
96
94
|
const showEnergyTransfer = async () => {
|
|
97
95
|
const width = 30;
|
|
98
96
|
for (let i = 0; i <= width; i++) {
|
|
99
|
-
const dots = "
|
|
100
|
-
process.stdout.write(
|
|
97
|
+
const dots = "█".repeat(i); const empty = "░".repeat(width - i);
|
|
98
|
+
process.stdout.write(\`\\r \\x1b[32m⚡ Alex Energy Sync:\\x1b[0m [\${dots}\${empty}] \${Math.round((i / width) * 100)}%\`);
|
|
101
99
|
await new Promise(r => setTimeout(r, 45));
|
|
102
100
|
}
|
|
103
|
-
process.stdout.write('
|
|
101
|
+
process.stdout.write('\\n');
|
|
104
102
|
};
|
|
105
103
|
|
|
106
104
|
if (command === 'alex') {
|
|
@@ -108,51 +106,43 @@ if (command === 'alex') {
|
|
|
108
106
|
const initialPrompt = args.slice(1).join(' ');
|
|
109
107
|
|
|
110
108
|
const processAlexRequest = async (prompt) => {
|
|
111
|
-
process.stdout.write('
|
|
109
|
+
process.stdout.write('\\x1b[33m🧠 Alex is thinking...\\x1b[0m');
|
|
112
110
|
try {
|
|
113
111
|
const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
|
|
114
112
|
headers: { 'x-project-id': projectId }
|
|
115
113
|
});
|
|
116
114
|
|
|
117
115
|
const aiData = result.data;
|
|
118
|
-
|
|
119
|
-
process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
|
|
116
|
+
process.stdout.write('\\r' + ' '.repeat(50) + '\\r');
|
|
120
117
|
|
|
121
|
-
// --- 1. GESTION DU QUOTA (EN ROUGE) ---
|
|
122
118
|
if (aiData.status === 'quota_exceeded') {
|
|
123
|
-
console.log(
|
|
119
|
+
console.log(\`\\n\\x1b[31m⛔ ENGINE OUT OF FUEL:\\x1b[0m \${aiData.message}\`);
|
|
124
120
|
return;
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
// --- 2. AFFICHAGE DE LA RÉPONSE ---
|
|
128
123
|
if (aiData.status === 'success' || aiData.status === 'message') {
|
|
129
|
-
// ✅ AJOUT : Saut de ligne pour séparer de votre commande "username â¯"
|
|
130
124
|
console.log('');
|
|
131
|
-
|
|
132
|
-
// Alex répond en Vert
|
|
133
|
-
console.log(\`\\\\x1b[32mAlex â¯\\\\x1b[0m \\\${aiData.message || "I'm ready."}\`);
|
|
125
|
+
console.log(\`\\x1b[32mAlex ❯\\x1b[0m \${aiData.message || "I'm ready."}\`);
|
|
134
126
|
|
|
135
|
-
// Jauge d'énergie (Bleu/Vert/Rouge)
|
|
136
127
|
if (aiData.remainingTokens !== undefined) {
|
|
137
128
|
const remaining = aiData.remainingTokens;
|
|
138
129
|
const limit = aiData.limit || 2500;
|
|
139
130
|
const percent = Math.round((remaining / limit) * 100);
|
|
140
|
-
const energyColor = percent > 30 ? '
|
|
141
|
-
console.log(
|
|
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)\`);
|
|
142
133
|
}
|
|
143
134
|
}
|
|
144
135
|
|
|
145
|
-
// --- 4. TRAITEMENT DES FICHIERS (SI PRÉSENTS) ---
|
|
146
136
|
if (aiData.status === 'success' && aiData.moduleData) {
|
|
147
137
|
const { fileName, code, mockFileName, mockCode, moduleName } = aiData.moduleData;
|
|
148
|
-
console.log(\`
|
|
138
|
+
console.log(\` \\x1b[90m⚙️ Architecting: \${moduleName}\\x1b[0m\`);
|
|
149
139
|
|
|
150
140
|
const writeFile = (dir, name, content) => {
|
|
151
141
|
const fullPath = path.join(process.cwd(), dir);
|
|
152
142
|
const filePath = path.join(fullPath, name);
|
|
153
143
|
if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
|
|
154
144
|
fs.writeFileSync(filePath, content);
|
|
155
|
-
console.log(\`
|
|
145
|
+
console.log(\` ✅ \\x1b[32m[Written]\\x1b[0m \${dir}\${name}\`);
|
|
156
146
|
};
|
|
157
147
|
|
|
158
148
|
if (code && fileName) {
|
|
@@ -165,77 +155,37 @@ if (command === 'alex') {
|
|
|
165
155
|
writeFile('src/pages/mocks/', 'Quick.jsx', mockCode);
|
|
166
156
|
}
|
|
167
157
|
}
|
|
168
|
-
|
|
169
158
|
} catch (error) {
|
|
170
|
-
process.stdout.write('
|
|
171
|
-
console.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));
|
|
172
161
|
}
|
|
173
162
|
};
|
|
174
163
|
|
|
175
164
|
const startAlexSession = async () => {
|
|
176
|
-
process.stdout.write('
|
|
177
|
-
|
|
178
|
-
let attempts = 0;
|
|
179
|
-
const maxAttempts = 5;
|
|
180
|
-
let isReady = false;
|
|
181
|
-
|
|
165
|
+
process.stdout.write('\\x1b[33m🛡️ Alex is checking runtime state...\\x1b[0m\\r');
|
|
166
|
+
let attempts = 0; const maxAttempts = 5; let isReady = false;
|
|
182
167
|
while (attempts < maxAttempts && !isReady) {
|
|
183
168
|
try {
|
|
184
|
-
const validation = await axios.post(ALEX_ENGINE_URL, {
|
|
185
|
-
prompt: "ping", validateProject: true, checkNetwork: true
|
|
186
|
-
}, { headers: { 'x-project-id': projectId }, timeout: 5000 });
|
|
187
|
-
|
|
169
|
+
const validation = await axios.post(ALEX_ENGINE_URL, { prompt: "ping", validateProject: true, checkNetwork: true }, { headers: { 'x-project-id': projectId }, timeout: 5000 });
|
|
188
170
|
if (validation.data?.isRunning) { isReady = true; break; }
|
|
189
|
-
attempts++;
|
|
190
|
-
|
|
191
|
-
} catch (error) {
|
|
192
|
-
attempts++;
|
|
193
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
194
|
-
}
|
|
171
|
+
attempts++; if (attempts < maxAttempts) await new Promise(r => setTimeout(r, 2000));
|
|
172
|
+
} catch (error) { attempts++; await new Promise(r => setTimeout(r, 2000)); }
|
|
195
173
|
}
|
|
196
|
-
|
|
197
174
|
if (!isReady) {
|
|
198
|
-
console.error('
|
|
175
|
+
console.error('\\n\\x1b[31m⚠️ ENGINE OFFLINE:\\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
|
|
199
176
|
process.exit(1);
|
|
200
177
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// 1. Accueil Alex (Vert)
|
|
206
|
-
console.log('\\\\n\\\\x1b[32m🤖 Alex is now online.\\\\x1b[0m');
|
|
207
|
-
console.log('\\\\x1b[32mAlex â¯\\\\x1b[0m Welcome! I am Alex, your architect. What are we building today?');
|
|
208
|
-
|
|
209
|
-
// 2. Premier saut de ligne pour l'esthétique
|
|
210
|
-
console.log('');
|
|
211
|
-
|
|
212
|
-
// 3. Configuration du prompt Développeur (Bleu)
|
|
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');
|
|
213
181
|
const userName = testerEmail ? testerEmail.split('@')[0] : 'Dev';
|
|
214
|
-
const rl = readline.createInterface({
|
|
215
|
-
input: process.stdin,
|
|
216
|
-
output: process.stdout,
|
|
217
|
-
prompt: \`\\\\x1b[34m\\\${userName} ⯠\\\\x1b[0m\`
|
|
218
|
-
});
|
|
219
|
-
|
|
182
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: \`\\x1b[34m\${userName} ❯ \\x1b[0m\` });
|
|
220
183
|
rl.prompt();
|
|
221
|
-
|
|
222
184
|
rl.on('line', async (line) => {
|
|
223
|
-
if (['exit', 'quit'].includes(line.trim().toLowerCase())) {
|
|
224
|
-
|
|
225
|
-
rl.close();
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (line.trim()) {
|
|
230
|
-
await processAlexRequest(line.trim());
|
|
231
|
-
// ✅ SAUT DE LIGNE après la réponse d'Alex
|
|
232
|
-
console.log('');
|
|
233
|
-
}
|
|
234
|
-
|
|
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(''); }
|
|
235
187
|
rl.prompt();
|
|
236
|
-
}).on('close', () => {
|
|
237
|
-
process.exit(0);
|
|
238
|
-
});
|
|
188
|
+
}).on('close', () => { process.exit(0); });
|
|
239
189
|
};
|
|
240
190
|
|
|
241
191
|
if (!initialPrompt || initialPrompt === '?') startAlexSession();
|
|
@@ -246,7 +196,7 @@ if (command === 'alex') {
|
|
|
246
196
|
if (command === 'deploy') {
|
|
247
197
|
checkGitSecurity();
|
|
248
198
|
(async () => {
|
|
249
|
-
console.log('
|
|
199
|
+
console.log('\\n\\x1b[36m⚡ FLEETBO CLOUD ENGINE\\x1b[0m');
|
|
250
200
|
try {
|
|
251
201
|
execSync('npm run build', { stdio: 'inherit' });
|
|
252
202
|
let buildDir = fs.existsSync(path.join(process.cwd(), 'dist')) ? 'dist' : 'build';
|
|
@@ -257,38 +207,30 @@ if (command === 'deploy') {
|
|
|
257
207
|
archive.directory(path.join(process.cwd(), buildDir), false);
|
|
258
208
|
archive.finalize();
|
|
259
209
|
});
|
|
260
|
-
console.log('
|
|
210
|
+
console.log('\\n📦 \\x1b[33mPreparing Neural Logic for Uplink...\\x1b[0m');
|
|
261
211
|
await showEnergyTransfer();
|
|
262
212
|
await axios.post(CLOUD_ENGINE_URL, zipBuffer, { headers: { 'Content-Type': 'application/zip', 'x-project-id': projectId } });
|
|
263
|
-
console.log('
|
|
213
|
+
console.log('\\n✅ \\x1b[1mDEPLOYMENT SUCCESSFUL\\x1b[0m | \\x1b[32mAlex ❯\\x1b[0m Runtime updated.');
|
|
264
214
|
} catch (error) { process.exit(1); }
|
|
265
215
|
})();
|
|
266
216
|
return;
|
|
267
217
|
}
|
|
268
218
|
|
|
269
|
-
// ==========================================
|
|
270
|
-
// 2. COMMANDE: GENERATE (Original préservé)
|
|
271
|
-
// ==========================================
|
|
272
219
|
const GENERATOR_COMMANDS = ['page', 'g', 'generate', 'android', 'ios'];
|
|
273
220
|
if (GENERATOR_COMMANDS.includes(command)) {
|
|
274
221
|
try { require('./page.js'); } catch (e) { console.error(e.message); process.exit(1); }
|
|
275
222
|
return;
|
|
276
223
|
}
|
|
277
224
|
|
|
278
|
-
// ==========================================
|
|
279
|
-
// 3. COMMANDE: DEV / CLEANUP (Original préservé)
|
|
280
|
-
// ==========================================
|
|
281
225
|
const NULL_DEV = process.platform === 'win32' ? '>nul 2>&1' : '2>/dev/null';
|
|
282
|
-
|
|
283
226
|
function killProcessOnPort(port) {
|
|
284
227
|
try {
|
|
285
228
|
if (process.platform !== 'win32') {
|
|
286
|
-
const pid = execSync(\`lsof -ti
|
|
287
|
-
if (pid) execSync(\`kill -9
|
|
229
|
+
const pid = execSync(\`lsof -ti:\${port} \${NULL_DEV}\`).toString().trim();
|
|
230
|
+
if (pid) execSync(\`kill -9 \${pid.split('\\n').join(' ')} \${NULL_DEV}\`);
|
|
288
231
|
}
|
|
289
232
|
} catch (e) {}
|
|
290
233
|
}
|
|
291
|
-
|
|
292
234
|
function killNetworkService() {
|
|
293
235
|
try {
|
|
294
236
|
const cmd = process.platform === 'win32' ? 'taskkill /IM cloudflared.exe /F' : 'pkill cloudflared';
|
|
@@ -298,15 +240,10 @@ function killNetworkService() {
|
|
|
298
240
|
|
|
299
241
|
let isExiting = false;
|
|
300
242
|
async function cleanupAndExit(code = 0) {
|
|
301
|
-
if (isExiting) return;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail });
|
|
306
|
-
} catch (e) {}
|
|
307
|
-
killNetworkService();
|
|
308
|
-
killProcessOnPort(PORT);
|
|
309
|
-
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);
|
|
310
247
|
}
|
|
311
248
|
process.on('SIGINT', () => cleanupAndExit(0));
|
|
312
249
|
process.on('SIGTERM', () => cleanupAndExit(0));
|
|
@@ -314,56 +251,37 @@ process.on('SIGTERM', () => cleanupAndExit(0));
|
|
|
314
251
|
async function syncFirebase(keyApp, networkUrl, testerEmail) {
|
|
315
252
|
try {
|
|
316
253
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
317
|
-
|
|
318
|
-
console.log(
|
|
319
|
-
console.log(
|
|
320
|
-
console.log(
|
|
321
|
-
console.log(
|
|
322
|
-
|
|
323
|
-
} catch (err) {
|
|
324
|
-
console.error(\`[Fleetbo] ⌠Sync Error: \\\${err.message}\`);
|
|
325
|
-
}
|
|
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}\`); }
|
|
326
260
|
}
|
|
327
261
|
|
|
328
262
|
async function runDevEnvironment() {
|
|
329
|
-
console.log(\`[Fleetbo]
|
|
330
|
-
killNetworkService();
|
|
331
|
-
killProcessOnPort(PORT);
|
|
332
|
-
if (!testerEmail) { console.error('Error: REACT_APP_TESTER_EMAIL missing'); process.exit(1); }
|
|
333
|
-
|
|
263
|
+
console.log(\`[Fleetbo] 🛡️ Initializing Dev Environment...\`);
|
|
264
|
+
killNetworkService(); killProcessOnPort(PORT);
|
|
334
265
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
335
|
-
const devServer = spawn(npmCmd, ['start'], {
|
|
336
|
-
|
|
337
|
-
shell: true,
|
|
338
|
-
env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() }
|
|
339
|
-
});
|
|
340
|
-
devServer.stdout.pipe(process.stdout);
|
|
341
|
-
devServer.stderr.pipe(process.stderr);
|
|
342
|
-
|
|
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);
|
|
343
268
|
let connectionStarted = false;
|
|
344
269
|
devServer.stdout.on('data', (data) => {
|
|
345
270
|
const output = data.toString();
|
|
346
271
|
if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
347
272
|
connectionStarted = true;
|
|
348
|
-
|
|
349
|
-
console.log(
|
|
350
|
-
console.log(\`[Fleetbo] 🔗 Establishing Secure Uplink...\`);
|
|
351
|
-
console.log(\`[Fleetbo] 🛑 DO NOT open the Fleetbo Studio yet. \`);
|
|
352
|
-
console.log(\`[Fleetbo] ⏳ Please wait green message...\`);
|
|
353
|
-
console.log('[Fleetbo] ---------------------------------------------------');
|
|
354
|
-
|
|
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] ---------------------------------------------------\`);
|
|
355
275
|
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
356
|
-
const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \`http://localhost
|
|
276
|
+
const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \`http://localhost:\${PORT}\`], { shell: true });
|
|
357
277
|
uplink.stderr.on('data', (chunk) => {
|
|
358
|
-
const match = chunk.toString().match(/https
|
|
278
|
+
const match = chunk.toString().match(/https:\\/\\/[a-zA-Z0-9-]+\\.trycloudflare\\.com/);
|
|
359
279
|
if (match) syncFirebase(keyApp, match[0], testerEmail);
|
|
360
280
|
});
|
|
361
281
|
}
|
|
362
282
|
});
|
|
363
283
|
}
|
|
364
|
-
|
|
365
|
-
runDevEnvironment();
|
|
366
|
-
`;
|
|
284
|
+
runDevEnvironment();`.replace(/`/g, '\\`').replace(/\${/g, '\\${');
|
|
367
285
|
|
|
368
286
|
|
|
369
287
|
const args = process.argv.slice(2);
|
|
@@ -375,7 +293,7 @@ const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
|
|
|
375
293
|
const userEmailArg = emailArg ? emailArg.split('=')[1] : null;
|
|
376
294
|
|
|
377
295
|
if (!projectNameArg || !bootstrapTokenArg || !userEmailArg) {
|
|
378
|
-
console.error('\n Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
|
|
296
|
+
console.error('\n ❌ Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
|
|
379
297
|
process.exit(1);
|
|
380
298
|
}
|
|
381
299
|
|
|
@@ -429,17 +347,17 @@ function downloadEngine(url, dest) {
|
|
|
429
347
|
}
|
|
430
348
|
|
|
431
349
|
async function setupProject() {
|
|
432
|
-
console.log(`\n
|
|
350
|
+
console.log(`\n⚡ Initializing Fleetbo Framework for "${projectName}"...`);
|
|
433
351
|
|
|
434
352
|
try {
|
|
435
353
|
if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
|
|
436
354
|
fs.mkdirSync(projectDir);
|
|
437
355
|
|
|
438
|
-
console.log(' [1/
|
|
356
|
+
console.log(' [1/7] 📥 Downloading Fleetbo Core Engine...');
|
|
439
357
|
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
440
358
|
await downloadEngine(archiveUrl, archivePath);
|
|
441
359
|
|
|
442
|
-
console.log(' [2/
|
|
360
|
+
console.log(' [2/7] 📦 Scaffolding project structure...');
|
|
443
361
|
try {
|
|
444
362
|
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
445
363
|
fs.unlinkSync(archivePath);
|
|
@@ -447,22 +365,23 @@ async function setupProject() {
|
|
|
447
365
|
|
|
448
366
|
process.chdir(projectDir);
|
|
449
367
|
|
|
450
|
-
console.log(' [3/
|
|
368
|
+
console.log(' [3/7] 🔑 Authenticating with Fleetbo Cloud...');
|
|
451
369
|
const keys = await fetchProjectKeys(bootstrapTokenArg);
|
|
452
370
|
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
453
371
|
|
|
454
|
-
console.log(' [4
|
|
372
|
+
console.log(' [4/7] ⚙️ Configuring environment & CLI...');
|
|
455
373
|
|
|
374
|
+
// Correction indentation .env
|
|
456
375
|
const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}
|
|
457
|
-
REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}
|
|
458
|
-
REACT_KEY_APP=${projectName}
|
|
459
|
-
REACT_APP_TESTER_EMAIL=${userEmailArg}
|
|
460
|
-
DANGEROUSLY_DISABLE_HOST_CHECK=true
|
|
461
|
-
WDS_SOCKET_PORT=0`;
|
|
376
|
+
REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}
|
|
377
|
+
REACT_KEY_APP=${projectName}
|
|
378
|
+
REACT_APP_TESTER_EMAIL=${userEmailArg}
|
|
379
|
+
DANGEROUSLY_DISABLE_HOST_CHECK=true
|
|
380
|
+
WDS_SOCKET_PORT=0`;
|
|
462
381
|
|
|
463
382
|
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
464
383
|
|
|
465
|
-
console.log(' [
|
|
384
|
+
console.log(' [5/7] 🛡️ Securing environment (adding .gitignore)...');
|
|
466
385
|
const gitignoreContent = `# Fleetbo Security\n.env\n.env.local\nnode_modules/\ndist/\nbuild/\n.DS_Store\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n`;
|
|
467
386
|
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent, 'utf8');
|
|
468
387
|
|
|
@@ -471,22 +390,22 @@ WDS_SOCKET_PORT=0`;
|
|
|
471
390
|
fs.writeFileSync(path.join(scriptsDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
|
|
472
391
|
try { fs.chmodSync(path.join(scriptsDir, 'cli.js'), '755'); } catch (e) {}
|
|
473
392
|
|
|
474
|
-
console.log(' [
|
|
393
|
+
console.log(' [6/7] 📚 Installing dependencies...');
|
|
475
394
|
execSync('npm install', { stdio: 'inherit' });
|
|
476
395
|
execSync('npm install cloudflared dotenv axios archiver --save-dev', { stdio: 'ignore' });
|
|
477
396
|
|
|
478
|
-
console.log(' [
|
|
397
|
+
console.log(' [7/7] ✨ Finalizing setup...');
|
|
479
398
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
480
399
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
481
400
|
packageJson.name = projectName;
|
|
482
401
|
packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node scripts/cli.js", "dev": "node scripts/cli.js" };
|
|
483
402
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
484
403
|
|
|
485
|
-
console.log('\n [Fleetbo] Project successfully created!');
|
|
486
|
-
console.log(`\
|
|
404
|
+
console.log('\n [Fleetbo] ✅ Project successfully created!');
|
|
405
|
+
console.log(`\n👉 Run: cd ${projectName} && npm run fleetbo`);
|
|
487
406
|
|
|
488
407
|
} catch (error) {
|
|
489
|
-
console.error('\
|
|
408
|
+
console.error('\n❌ Setup failed:', error.message);
|
|
490
409
|
if (fs.existsSync(projectDir)) try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
491
410
|
process.exit(1);
|
|
492
411
|
}
|