create-fleetbo-project 1.2.40 → 1.2.41

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.
@@ -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
- console.error('\\\\x1b[31m%s\\\\x1b[0m', ' Error: Configuration file (.env) not found.');
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;
@@ -47,40 +48,47 @@ const checkGitSecurity = () => {
47
48
  const gitignorePath = path.join(process.cwd(), '.gitignore');
48
49
  if (fs.existsSync(gitDir)) {
49
50
  if (!fs.existsSync(gitignorePath)) {
50
- console.error('\\\\n\\\\x1b[31m🚨 SECURITY ALERT:\\\\x1b[0m .git detected but no .gitignore found.');
51
+ console.error('\\\\n\\\\x1b[31m🚨 SECURITY ALERT:\\\\x1b[0m .git detected but no .gitignore found.');
51
52
  process.exit(1);
52
53
  }
53
54
  const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
54
55
  if (!gitignoreContent.includes('.env')) {
55
- console.error('\\\\n\\\\x1b[31m🚨 CRITICAL RISK:\\\\x1b[0m .env is NOT ignored by Git.');
56
+ console.error('\\\\n\\\\x1b[31m🚨 CRITICAL RISK:\\\\x1b[0m .env is NOT ignored by Git.');
56
57
  process.exit(1);
57
58
  }
58
59
  }
59
60
  };
60
61
 
61
62
  if (!projectId) {
62
- console.error('\\\\n❌ Error: Project ID missing in .env.\\\\n');
63
+ console.error('\\\\n⌠Error: Project ID missing in .env.\\\\n');
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
- if (!content.includes('// FLEETBO_IMPORTS') || !content.includes('{/* FLEETBO_ROUTES */}')) return false;
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 importLine = \`import \\\${pageName} from './pages/\\\${pageName}';\`;
72
- const routeLine = \`<Route path="/\\\${pageName.toLowerCase()}" element=<\\\${pageName} /> />\`;
76
+ // Chemin dynamique selon si c'est une page ou un mock
77
+ const pathPrefix = subPath ? \`\\\${subPath}/\` : '';
78
+ const importLine = \`import \\\${pageName} from './pages/\\\${pathPrefix}\\\${pageName}';\`;
79
+ const routeLine = \`<Route path="/\\\${pathPrefix.toLowerCase()}\\\${pageName.toLowerCase()}" element=<\\\${\\\${pageName} />} />\`;
73
80
 
74
81
  let injected = false;
75
- if (!content.includes(importLine) && !content.includes(\`import \\\${pageName} from\`)) {
76
- content = content.replace('// FLEETBO_IMPORTS', \`\\\${importLine}\\\\n// FLEETBO_IMPORTS\`);
82
+ if (!content.includes(importLine)) {
83
+ content = content.replace(importAnchor, \`\\\${importLine}\\\\n\\\${importAnchor}\`);
77
84
  injected = true;
78
85
  }
79
- if (!content.includes(routeLine) && !content.includes(\`path="/\\\${pageName.toLowerCase()}"\`)) {
80
- const anchor = content.includes('{/* FLEETBO_ROUTES */}') ? '{/* FLEETBO_ROUTES */}' : '// FLEETBO_ROUTES';
81
- content = content.replace(anchor, \`\\\${routeLine}\\\\n \\\${anchor}\`);
86
+ if (!content.includes(routeLine)) {
87
+ // Injection précise dans l'ancre Alex
88
+ content = content.replace(alexRouteAnchor, \`\\\${routeLine}\\\\n \\\${alexRouteAnchor}\`);
82
89
  injected = true;
83
90
  }
91
+
84
92
  if (injected) fs.writeFileSync(APP_JS_PATH, content);
85
93
  return injected;
86
94
  };
@@ -88,8 +96,8 @@ const injectRouteIntoAppJs = (pageName) => {
88
96
  const showEnergyTransfer = async () => {
89
97
  const width = 30;
90
98
  for (let i = 0; i <= width; i++) {
91
- const dots = "".repeat(i); const empty = "".repeat(width - i);
92
- process.stdout.write(\`\\\\r \\\\x1b[32m Alex Energy Sync:\\\\x1b[0m [\\\${dots}\\\${empty}] \\\${Math.round((i / width) * 100)}%\`);
99
+ const dots = "â–ˆ".repeat(i); const empty = "â–'".repeat(width - i);
100
+ process.stdout.write(\`\\\\r \\\\x1b[32mâš¡ Alex Energy Sync:\\\\x1b[0m [\\\${dots}\\\${empty}] \\\${Math.round((i / width) * 100)}%\`);
93
101
  await new Promise(r => setTimeout(r, 45));
94
102
  }
95
103
  process.stdout.write('\\\\n');
@@ -100,68 +108,136 @@ if (command === 'alex') {
100
108
  const initialPrompt = args.slice(1).join(' ');
101
109
 
102
110
  const processAlexRequest = async (prompt) => {
103
- process.stdout.write('\\\\n\\\\x1b[33m🧠 Alex is architecting...\\\\x1b[0m\\\\r');
111
+ process.stdout.write('\\\\x1b[33m🧠Alex is thinking...\\\\x1b[0m');
104
112
  try {
105
113
  const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
106
114
  headers: { 'x-project-id': projectId }
107
115
  });
108
- const aiData = result.data.data || result.data;
109
- process.stdout.write(' '.repeat(30) + '\\\\r');
116
+
117
+ const aiData = result.data;
118
+ // 1. On efface "Alex is thinking..."
119
+ process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
120
+
121
+ // --- 1. GESTION DU QUOTA (EN ROUGE) ---
122
+ if (aiData.status === 'quota_exceeded') {
123
+ console.log(\`\\\\n\\\\x1b[31mâ›" ENGINE OUT OF FUEL:\\\\x1b[0m \\\${aiData.message}\`);
124
+ return;
125
+ }
126
+
127
+ // --- 2. AFFICHAGE DE LA RÉPONSE ---
128
+ if (aiData.status === 'success' || aiData.status === 'message') {
129
+ // ✅ AJOUT : Saut de ligne pour séparer de votre commande "username â¯"
130
+ console.log('');
131
+
132
+ // Alex répond en Vert
133
+ console.log(\`\\\\x1b[32mAlex â¯\\\\x1b[0m \\\${aiData.message || "I'm ready."}\`);
134
+
135
+ // Jauge d'énergie (Bleu/Vert/Rouge)
136
+ if (aiData.remainingTokens !== undefined) {
137
+ const remaining = aiData.remainingTokens;
138
+ const limit = aiData.limit || 2500;
139
+ const percent = Math.round((remaining / limit) * 100);
140
+ const energyColor = percent > 30 ? '\\\\x1b[32m' : '\\\\x1b[31m';
141
+ console.log(\`\\\\x1b[36mðŸ"Š Energy:\\\\x1b[0m \\\${energyColor}\\\${percent}%\\\\x1b[0m (\\\${remaining}/\\\${limit} tokens left)\`);
142
+ }
143
+ }
110
144
 
145
+ // --- 4. TRAITEMENT DES FICHIERS (SI PRÉSENTS) ---
111
146
  if (aiData.status === 'success' && aiData.moduleData) {
112
- const { fileName, code, mockFileName, mockCode } = aiData.moduleData;
147
+ const { fileName, code, mockFileName, mockCode, moduleName } = aiData.moduleData;
148
+ console.log(\` \\\\x1b[90mâš™ï¸ Architecting: \\\${moduleName}\\\\x1b[0m\`);
149
+
113
150
  const writeFile = (dir, name, content) => {
114
151
  const fullPath = path.join(process.cwd(), dir);
115
152
  const filePath = path.join(fullPath, name);
116
153
  if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
117
-
118
- if (fs.existsSync(filePath)) {
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
- }
154
+ fs.writeFileSync(filePath, content);
155
+ console.log(\` ✅ \\\\x1b[32m[Written]\\\\x1b[0m \\\${dir}\\\${name}\`);
127
156
  };
128
- console.log('');
129
- if (code && fileName && (fileName.endsWith('.kt') || fileName.endsWith('.swift'))) {
130
- writeFile(fileName.endsWith('.kt') ? 'public/native/android/' : 'public/native/ios/', fileName, code);
157
+
158
+ if (code && fileName) {
159
+ const folder = fileName.endsWith('.kt') ? 'public/native/android/' : 'src/pages/';
160
+ writeFile(folder, fileName, code);
161
+ if (fileName.endsWith('.jsx')) injectRouteIntoAppJs(fileName.replace('.jsx', ''));
131
162
  }
132
163
  if (mockCode && mockFileName) {
133
164
  writeFile('src/pages/mocks/', mockFileName, mockCode);
134
- writeFile('src/pages/mocks/', 'Quick.js', mockCode);
135
- }
136
- if (code && fileName && fileName.endsWith('.jsx') && !mockCode) {
137
- const pageName = fileName.replace('.jsx', '');
138
- writeFile('src/pages/', fileName, code);
139
- injectRouteIntoAppJs(pageName);
165
+ writeFile('src/pages/mocks/', 'Quick.jsx', mockCode);
140
166
  }
141
- console.log('\\\\n\\\\x1b[32mAlex ❯\\\\x1b[0m ' + aiData.message);
142
167
  }
143
- } catch (error) { console.error('\\\\n\\\\x1b[31m❌ Alex Error:\\\\x1b[0m ' + error.message); }
168
+
169
+ } catch (error) {
170
+ process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
171
+ console.error('\\\\n\\\\x1b[31m⌠Alex Error:\\\\x1b[0m ' + (error.response?.data?.message || error.message));
172
+ }
144
173
  };
145
174
 
146
175
  const startAlexSession = async () => {
147
- process.stdout.write('\\\\n\\\\x1b[33m🛡️ Alex is checking runtime state...\\\\x1b[0m\\\\r');
148
- try {
149
- const validation = await axios.post(ALEX_ENGINE_URL, { validateProject: true, checkNetwork: true, projectKey: keyApp, testerEmail: testerEmail }, { headers: { 'x-project-id': projectId } });
150
- if (!validation.data.exists || !validation.data.isRunning) {
151
- console.error('\\\\n\\\\x1b[31m⚠️ ENGINE OFFLINE:\\\\x1b[0m Alex needs a live Uplink.');
152
- process.exit(1);
176
+ process.stdout.write('\\\\x1b[33mðŸ›¡ï¸ Alex is checking runtime state...\\\\x1b[0m\\\\r');
177
+
178
+ let attempts = 0;
179
+ const maxAttempts = 5;
180
+ let isReady = false;
181
+
182
+ while (attempts < maxAttempts && !isReady) {
183
+ 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
+
188
+ if (validation.data?.isRunning) { isReady = true; break; }
189
+ attempts++;
190
+ if (attempts < maxAttempts) await new Promise(r => setTimeout(r, 2000));
191
+ } catch (error) {
192
+ attempts++;
193
+ await new Promise(r => setTimeout(r, 2000));
153
194
  }
154
- process.stdout.write(' '.repeat(60) + '\\\\r');
155
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: '\\\\x1b[32mAlex ❯ \\\\x1b[0m' });
156
- console.log('\\\\n\\\\x1b[32m🤖 Alex is now online.\\\\x1b[0m Pilot safely.');
195
+ }
196
+
197
+ if (!isReady) {
198
+ console.error('\\\\n\\\\x1b[31mâš ï¸ ENGINE OFFLINE:\\\\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
199
+ process.exit(1);
200
+ }
201
+
202
+ // --- INITIALISATION DE L'INTERFACE VISUELLE ---
203
+ process.stdout.write(' '.repeat(60) + '\\\\r'); // Efface le status de validation
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)
213
+ 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
+
220
+ rl.prompt();
221
+
222
+ rl.on('line', async (line) => {
223
+ if (['exit', 'quit'].includes(line.trim().toLowerCase())) {
224
+ console.log('\\\\n\\\\x1b[90mðŸ'‹ Alex session closed.\\\\x1b[0m');
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
+
157
235
  rl.prompt();
158
- rl.on('line', async (line) => {
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); }
236
+ }).on('close', () => {
237
+ process.exit(0);
238
+ });
164
239
  };
240
+
165
241
  if (!initialPrompt || initialPrompt === '?') startAlexSession();
166
242
  else processAlexRequest(initialPrompt);
167
243
  return;
@@ -170,7 +246,7 @@ if (command === 'alex') {
170
246
  if (command === 'deploy') {
171
247
  checkGitSecurity();
172
248
  (async () => {
173
- console.log('\\\\n\\\\x1b[36m FLEETBO CLOUD ENGINE\\\\x1b[0m');
249
+ console.log('\\\\n\\\\x1b[36mâš¡ FLEETBO CLOUD ENGINE\\\\x1b[0m');
174
250
  try {
175
251
  execSync('npm run build', { stdio: 'inherit' });
176
252
  let buildDir = fs.existsSync(path.join(process.cwd(), 'dist')) ? 'dist' : 'build';
@@ -181,28 +257,34 @@ if (command === 'deploy') {
181
257
  archive.directory(path.join(process.cwd(), buildDir), false);
182
258
  archive.finalize();
183
259
  });
184
- console.log('\\\\n📦 \\\\x1b[33mPreparing Neural Logic for Uplink...\\\\x1b[0m');
260
+ console.log('\\\\nðŸ"¦ \\\\x1b[33mPreparing Neural Logic for Uplink...\\\\x1b[0m');
185
261
  await showEnergyTransfer();
186
262
  await axios.post(CLOUD_ENGINE_URL, zipBuffer, { headers: { 'Content-Type': 'application/zip', 'x-project-id': projectId } });
187
- console.log('\\\\n \\\\x1b[1mDEPLOYMENT SUCCESSFUL\\\\x1b[0m | \\\\x1b[32mAlex ❯\\\\x1b[0m Runtime updated.');
263
+ console.log('\\\\n✅ \\\\x1b[1mDEPLOYMENT SUCCESSFUL\\\\x1b[0m | \\\\x1b[32mAlex â¯\\\\x1b[0m Runtime updated.');
188
264
  } catch (error) { process.exit(1); }
189
265
  })();
190
266
  return;
191
267
  }
192
268
 
269
+ // ==========================================
270
+ // 2. COMMANDE: GENERATE (Original préservé)
271
+ // ==========================================
193
272
  const GENERATOR_COMMANDS = ['page', 'g', 'generate', 'android', 'ios'];
194
273
  if (GENERATOR_COMMANDS.includes(command)) {
195
- try { require('./page.js'); } catch (error) { process.exit(1); }
274
+ try { require('./page.js'); } catch (e) { console.error(e.message); process.exit(1); }
196
275
  return;
197
276
  }
198
277
 
278
+ // ==========================================
279
+ // 3. COMMANDE: DEV / CLEANUP (Original préservé)
280
+ // ==========================================
199
281
  const NULL_DEV = process.platform === 'win32' ? '>nul 2>&1' : '2>/dev/null';
200
282
 
201
283
  function killProcessOnPort(port) {
202
284
  try {
203
285
  if (process.platform !== 'win32') {
204
286
  const pid = execSync(\`lsof -ti:\\\${port} \\\${NULL_DEV}\`).toString().trim();
205
- if (pid) { execSync(\`kill -9 \\\${pid.split('\\\\n').join(' ')} \\\${NULL_DEV}\`); }
287
+ if (pid) execSync(\`kill -9 \\\${pid.split('\\\\n').join(' ')} \\\${NULL_DEV}\`);
206
288
  }
207
289
  } catch (e) {}
208
290
  }
@@ -218,8 +300,9 @@ let isExiting = false;
218
300
  async function cleanupAndExit(code = 0) {
219
301
  if (isExiting) return;
220
302
  isExiting = true;
303
+ console.log('\\\\n[Fleetbo] ðŸ›' Stopping environment...');
221
304
  try {
222
- await axios.post(UPDATE_NETWORK_URL, { keyApp: keyApp, networkUrl: '', tester: testerEmail });
305
+ await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail });
223
306
  } catch (e) {}
224
307
  killNetworkService();
225
308
  killProcessOnPort(PORT);
@@ -231,28 +314,54 @@ process.on('SIGTERM', () => cleanupAndExit(0));
231
314
  async function syncFirebase(keyApp, networkUrl, testerEmail) {
232
315
  try {
233
316
  await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
234
- console.log(\`\\\\n[Fleetbo] ✅ Uplink Status: Online for \\\${testerEmail}\`);
235
- console.log(\`[Fleetbo] 🚀 Fleetbo Studio: https://fleetbo.io/studio/\\\${keyApp}\`);
236
- } catch (err) { console.error(\`[Fleetbo] ⚠️ Sync Error: \\\${err.message}\`); }
317
+
318
+ console.log(\`\\\\n\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\`);
319
+ console.log('\\\\x1b[32m[Fleetbo] GO GO GO ! FLEETBO STUDIO IS READY \\\\x1b[0m');
320
+ console.log(\`\\\\x1b[32m[Fleetbo] Link: https://fleetbo.io/studio/\\\${keyApp}\\\\x1b[0m\`);
321
+ console.log('\\\\x1b[32m[Fleetbo] You can now start coding and previewing in Studio. 🚀\\\\x1b[0m');
322
+ console.log(\`\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\`);
323
+ } catch (err) {
324
+ console.error(\`[Fleetbo] ⌠Sync Error: \\\${err.message}\`);
325
+ }
237
326
  }
238
327
 
239
328
  async function runDevEnvironment() {
329
+ console.log(\`[Fleetbo] ðŸ›¡ï¸ Initializing Dev Environment...\`);
240
330
  killNetworkService();
241
331
  killProcessOnPort(PORT);
332
+ if (!testerEmail) { console.error('Error: REACT_APP_TESTER_EMAIL missing'); process.exit(1); }
333
+
242
334
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
243
- const devServer = spawn(npmCmd, ['start'], { stdio: ['ignore', 'pipe', 'pipe'], shell: true, env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() } });
335
+ const devServer = spawn(npmCmd, ['start'], {
336
+ stdio: ['ignore', 'pipe', 'pipe'],
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
+
343
+ let connectionStarted = false;
244
344
  devServer.stdout.on('data', (data) => {
245
345
  const output = data.toString();
246
- if (output.includes('Local:') || output.includes('Compiled successfully')) {
346
+ if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
347
+ connectionStarted = true;
348
+
349
+ console.log('\\\\n[Fleetbo] ---------------------------------------------------');
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
+
247
355
  const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
248
356
  const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \`http://localhost:\\\${PORT}\`], { shell: true });
249
357
  uplink.stderr.on('data', (chunk) => {
250
- const match = log.match(/https:[//][//][a-zA-Z0-9-]+[.]trycloudflare[.]com/);
358
+ const match = chunk.toString().match(/https:\\\\/\\\\/[a-zA-Z0-9-]+\\\\.trycloudflare\\\\.com/);
251
359
  if (match) syncFirebase(keyApp, match[0], testerEmail);
252
360
  });
253
361
  }
254
362
  });
255
363
  }
364
+
256
365
  runDevEnvironment();
257
366
  `;
258
367
 
@@ -266,7 +375,7 @@ const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
266
375
  const userEmailArg = emailArg ? emailArg.split('=')[1] : null;
267
376
 
268
377
  if (!projectNameArg || !bootstrapTokenArg || !userEmailArg) {
269
- console.error('\n Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
378
+ console.error('\n Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
270
379
  process.exit(1);
271
380
  }
272
381
 
@@ -320,17 +429,17 @@ function downloadEngine(url, dest) {
320
429
  }
321
430
 
322
431
  async function setupProject() {
323
- console.log(`\n Initializing Fleetbo Framework for "${projectName}"...`);
432
+ console.log(`\nâš¡ Initializing Fleetbo Framework for "${projectName}"...`);
324
433
 
325
434
  try {
326
435
  if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
327
436
  fs.mkdirSync(projectDir);
328
437
 
329
- console.log(' [1/6] 📥 Downloading Fleetbo Core Engine...');
438
+ console.log(' [1/6]---------- Downloading Fleetbo Core Engine...');
330
439
  const archivePath = path.join(projectDir, 'engine.tar.gz');
331
440
  await downloadEngine(archiveUrl, archivePath);
332
441
 
333
- console.log(' [2/6] 📦 Scaffolding project structure...');
442
+ console.log(' [2/6]---------- Scaffolding project structure...');
334
443
  try {
335
444
  execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
336
445
  fs.unlinkSync(archivePath);
@@ -338,23 +447,22 @@ async function setupProject() {
338
447
 
339
448
  process.chdir(projectDir);
340
449
 
341
- console.log(' [3/6] 🔑 Authenticating with Fleetbo Cloud...');
450
+ console.log(' [3/6]---------- Authenticating with Fleetbo Cloud...');
342
451
  const keys = await fetchProjectKeys(bootstrapTokenArg);
343
452
  if (!keys.enterpriseId) throw new Error("Invalid keys.");
344
453
 
345
- console.log(' [4.1/6] ⚙️ Configuring environment & CLI...');
454
+ console.log(' [4.1/6]---------- Configuring environment & CLI...');
346
455
 
347
- // Correction indentation .env
348
456
  const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}
349
- REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}
350
- REACT_KEY_APP=${projectName}
351
- REACT_APP_TESTER_EMAIL=${userEmailArg}
352
- DANGEROUSLY_DISABLE_HOST_CHECK=true
353
- WDS_SOCKET_PORT=0`;
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`;
354
462
 
355
463
  fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
356
464
 
357
- console.log(' [4.2/6] 🛡️ Securing environment (adding .gitignore)...');
465
+ console.log(' [4.2/6]---------- Securing environment (adding .gitignore)...');
358
466
  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`;
359
467
  fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent, 'utf8');
360
468
 
@@ -363,22 +471,22 @@ async function setupProject() {
363
471
  fs.writeFileSync(path.join(scriptsDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
364
472
  try { fs.chmodSync(path.join(scriptsDir, 'cli.js'), '755'); } catch (e) {}
365
473
 
366
- console.log(' [5/6] 📚 Installing dependencies...');
474
+ console.log(' [5/6]---------- Installing dependencies...');
367
475
  execSync('npm install', { stdio: 'inherit' });
368
476
  execSync('npm install cloudflared dotenv axios archiver --save-dev', { stdio: 'ignore' });
369
477
 
370
- console.log(' [6/6] Finalizing setup...');
478
+ console.log(' [6/6]---------- Finalizing setup...');
371
479
  const packageJsonPath = path.join(projectDir, 'package.json');
372
480
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
373
481
  packageJson.name = projectName;
374
482
  packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node scripts/cli.js", "dev": "node scripts/cli.js" };
375
483
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
376
484
 
377
- console.log('\n [Fleetbo] Project successfully created!');
378
- console.log(`\n👉 Run: cd ${projectName} && npm run fleetbo`);
485
+ console.log('\n [Fleetbo] Project successfully created!');
486
+ console.log(`\nðŸ'‰ Run: cd ${projectName} && npm run fleetbo`);
379
487
 
380
488
  } catch (error) {
381
- console.error('\n❌ Setup failed:', error.message);
489
+ console.error('\n⌠Setup failed:', error.message);
382
490
  if (fs.existsSync(projectDir)) try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
383
491
  process.exit(1);
384
492
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fleetbo-project",
3
- "version": "1.2.40",
3
+ "version": "1.2.41",
4
4
  "description": "Creates a new Fleetbo project.",
5
5
  "main": "install-react-template.js",
6
6
  "bin": {