fleetbo-cockpit-cli 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/cli.js +122 -26
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -137,7 +137,7 @@ const showEnergyTransfer = async () => {
137
137
  for (let i = 0; i <= width; i++) {
138
138
  const dots = "█".repeat(i);
139
139
  const empty = "░".repeat(width - i);
140
- process.stdout.write(`\r \x1b[32m⚡ Alex Energy Sync:\x1b[0m [${dots}${empty}] ${Math.round((i / width) * 100)}%`);
140
+ process.stdout.write(`\r \x1b[32m⚡ Propulsion Sync:\x1b[0m [${dots}${empty}] ${Math.round((i / width) * 100)}%`);
141
141
  await new Promise(r => setTimeout(r, 45));
142
142
  }
143
143
  process.stdout.write('\n');
@@ -196,7 +196,7 @@ if (command === 'alex') {
196
196
  const filePath = path.join(fullPath, name);
197
197
  if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
198
198
  fs.writeFileSync(filePath, content);
199
- console.log(` \x1b[32m[Written]\x1b[0m ${dir}${name}`);
199
+ console.log(` \x1b[32m[Written]\x1b[0m ${dir}${name}`);
200
200
  };
201
201
 
202
202
  if (instructions && Array.isArray(instructions) && instructions.length > 0) {
@@ -225,7 +225,7 @@ if (command === 'alex') {
225
225
  }
226
226
 
227
227
  if (config_offload && (config_offload.dependencies?.length > 0 || config_offload.permissions?.length > 0)) {
228
- process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Syncing ${config_offload.dependencies.length} libs to Factory...`);
228
+ process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Syncing ${config_offload.dependencies?.length || 0} libs to Factory...`);
229
229
  try {
230
230
  await axios.post(INJECT_DEPS_URL, {
231
231
  projectId: projectId,
@@ -237,7 +237,7 @@ if (command === 'alex') {
237
237
  process.stdout.write(` \x1b[32mOK\x1b[0m\n`);
238
238
  } catch (err) {
239
239
  process.stdout.write(` \x1b[31mFAILED\x1b[0m\n`);
240
- console.error(` ⚠️ Config sync failed: ${err.message}`);
240
+ console.error(` ⚠️ Config sync failed: ${err.message}`);
241
241
  }
242
242
  }
243
243
  }
@@ -315,57 +315,153 @@ if (command === 'alex') {
315
315
 
316
316
  }
317
317
  // ============================================
318
- // COMMAND: android / ios
318
+ // COMMAND: android / ios (PROPULSION BUILD)
319
319
  // ============================================
320
320
  else if (command === 'android' || command === 'ios') {
321
321
  checkGitSecurity();
322
322
  const platform = command;
323
323
  const nativeDir = platform === 'android' ? 'public/native/android/' : 'public/native/ios/';
324
324
  const extension = platform === 'android' ? '.kt' : '.swift';
325
- const fullPath = path.join(process.cwd(), nativeDir);
325
+ const nativePath = path.join(process.cwd(), nativeDir);
326
326
 
327
+ // Vérification des modules natifs
327
328
  let hasNativeFiles = false;
328
- if (fs.existsSync(fullPath)) {
329
- const files = fs.readdirSync(fullPath);
330
- hasNativeFiles = files.some(file => file.endsWith(extension));
329
+ let nativeFileCount = 0;
330
+ if (fs.existsSync(nativePath)) {
331
+ const files = fs.readdirSync(nativePath);
332
+ const nativeFiles = files.filter(file => file.endsWith(extension));
333
+ hasNativeFiles = nativeFiles.length > 0;
334
+ nativeFileCount = nativeFiles.length;
331
335
  }
332
336
 
333
337
  if (!hasNativeFiles) {
334
338
  console.log(`\n\x1b[31m⚠️ ENGINE INCOMPLETE:\x1b[0m No native blueprints detected for \x1b[1m${platform.toUpperCase()}\x1b[0m.`);
335
- console.log(`\x1b[90mAlex must architect at least one ${extension} module before deployment.\x1b[0m\n`);
339
+ console.log(`\x1b[90mAlex must architect at least one ${extension} module before deployment.\x1b[0m`);
340
+ console.log(`\x1b[90mRun: npm run fleetbo alex\x1b[0m\n`);
336
341
  process.exit(1);
337
342
  }
338
343
 
339
344
  const targetUrl = platform === 'android' ? ANDROID_BUILD_URL : IOS_BUILD_URL;
340
345
 
341
346
  (async () => {
342
- console.log(`\n\x1b[36m⚡ FLEETBO ${platform.toUpperCase()} UPLINK\x1b[0m`);
347
+ console.log(`\n\x1b[36m⚡ FLEETBO ${platform.toUpperCase()} PROPULSION\x1b[0m`);
348
+ console.log(`\x1b[90m ${nativeFileCount} native module(s) detected\x1b[0m\n`);
349
+
343
350
  try {
351
+ // ==========================================================
352
+ // 🟢 NOUVEAU : ÉTAPE 0 (PRE-FLIGHT CHECK QUOTAS)
353
+ // ==========================================================
354
+ process.stdout.write(`\x1b[33m[0/3]\x1b[0m Checking Propulsion Quotas... `);
355
+ try {
356
+ await axios.post(targetUrl, {}, {
357
+ headers: {
358
+ 'x-project-id': projectId,
359
+ 'x-preflight': 'true' // Signale au serveur de ne pas chercher de ZIP
360
+ }
361
+ });
362
+ process.stdout.write(`\x1b[32mOK\x1b[0m\n\n`);
363
+ } catch (preflightError) {
364
+ process.stdout.write(`\x1b[31mDENIED\x1b[0m\n`);
365
+ // Fait sauter le code directement au "catch" global du bas sans jamais builder
366
+ throw preflightError;
367
+ }
368
+
369
+ // Étape 1: Build React
370
+ console.log(`\x1b[33m[1/3]\x1b[0m Building React bundle...`);
344
371
  execSync('npm run build', { stdio: 'inherit' });
372
+
373
+ // Déterminer le dossier de build
345
374
  let buildDir = fs.existsSync(path.join(process.cwd(), 'dist')) ? 'dist' : 'build';
375
+ const buildPath = path.join(process.cwd(), buildDir);
376
+
377
+ if (!fs.existsSync(buildPath)) {
378
+ throw new Error(`Build directory not found: ${buildDir}`);
379
+ }
380
+
381
+ // Étape 2: Créer le ZIP avec le bundle ET les modules natifs
382
+ console.log(`\n\x1b[33m[2/3]\x1b[0m Packaging bundle + native modules...`);
346
383
 
347
384
  const zipBuffer = await new Promise((resolve, reject) => {
348
385
  const chunks = [];
349
386
  const archive = archiver('zip', { zlib: { level: 9 } });
387
+
350
388
  archive.on('data', chunk => chunks.push(chunk));
351
389
  archive.on('end', () => resolve(Buffer.concat(chunks)));
352
390
  archive.on('error', reject);
353
- archive.directory(path.join(process.cwd(), buildDir), false);
391
+ archive.on('warning', (err) => {
392
+ if (err.code !== 'ENOENT') reject(err);
393
+ });
394
+
395
+ // ============================================================
396
+ // 📦 STRUCTURE DU ZIP (Compatible Fastfile + Cloud Function)
397
+ // ============================================================
398
+ // build.zip
399
+ // └── build/
400
+ // ├── index.html ← Pour assets/ (WebView)
401
+ // ├── static/
402
+ // └── native/
403
+ // └── android/ ← Pour modules/ (.kt)
404
+ // └── CameraModule.kt
405
+ // ============================================================
406
+
407
+ // 1. Ajouter le bundle React (dans build/)
408
+ archive.directory(buildPath, 'build');
409
+ console.log(` \x1b[32m✓\x1b[0m React bundle included`);
410
+
411
+ // 2. Ajouter les modules natifs (dans build/native/android/ ou build/native/ios/)
412
+ // Le Fastfile cherche: temp/build/native/android/*.kt
413
+ // La Cloud Function cherche: native/android/*.kt (dans le ZIP)
414
+ if (fs.existsSync(nativePath)) {
415
+ archive.directory(nativePath, `build/native/${platform}`);
416
+ console.log(` \x1b[32m✓\x1b[0m Native modules included (${nativeFileCount} files)`);
417
+ }
418
+
354
419
  archive.finalize();
355
420
  });
356
421
 
357
- console.log(`\n\x1b[33mSyncing ${platform} logic bundle...\x1b[0m`);
422
+ const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
423
+ console.log(` \x1b[32m✓\x1b[0m Bundle ready: ${sizeMB} MB`);
424
+
425
+ // Étape 3: Upload vers Cloud Function
426
+ console.log(`\n\x1b[33m[3/3]\x1b[0m Uploading to Fleetbo Factory...`);
358
427
  await showEnergyTransfer();
359
428
 
360
429
  const res = await axios.post(targetUrl, zipBuffer, {
361
- headers: { 'Content-Type': 'application/zip', 'x-project-id': projectId }
430
+ headers: {
431
+ 'Content-Type': 'application/zip',
432
+ 'x-project-id': projectId
433
+ },
434
+ maxContentLength: Infinity,
435
+ maxBodyLength: Infinity,
436
+ timeout: 120000 // 2 minutes timeout
362
437
  });
363
438
 
364
439
  if (res.data.success) {
365
- console.log(`\n\x1b[1m${platform.toUpperCase()} DEPLOYED\x1b[0m | \x1b[32mAlex ❯\x1b[0m Runtime updated.`);
440
+ console.log(`\n\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
441
+ console.log(`\x1b[32m✓ ${platform.toUpperCase()} PROPULSION SUCCESSFUL\x1b[0m`);
442
+ console.log(`\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
443
+ console.log(`\x1b[90m Build ID: ${res.data.buildId || 'N/A'}\x1b[0m`);
444
+ console.log(`\x1b[90m Your ${platform} bundle is now in the Factory queue.\x1b[0m\n`);
445
+ } else {
446
+ throw new Error(res.data.error || 'Unknown error from Factory');
366
447
  }
367
- } catch (error) {
368
- console.error(`\n\x1b[31m Build Error:\x1b[0m ${error.response?.data?.error || error.message}`);
448
+
449
+ } catch (error) {
450
+ console.log(`\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
451
+ console.log(`\x1b[31m✗ PROPULSION FAILED\x1b[0m`);
452
+ console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
453
+
454
+ const errorMsg = error.response?.data?.error || error.message;
455
+ console.error(`\x1b[31m Error:\x1b[0m ${errorMsg}`);
456
+
457
+ // Messages d'aide selon l'erreur
458
+ if (errorMsg.includes('Propulsion Limit')) {
459
+ console.log(`\n\x1b[33m 💡 Tip:\x1b[0m Upgrade to Senior Pilot for more builds.`);
460
+ } else if (errorMsg.includes('No') && errorMsg.includes('blueprint')) {
461
+ console.log(`\n\x1b[33m 💡 Tip:\x1b[0m Run "npm run fleetbo alex" to create native modules first.`);
462
+ }
463
+
464
+ console.log('');
369
465
  process.exit(1);
370
466
  }
371
467
  })();
@@ -433,10 +529,10 @@ else {
433
529
  try {
434
530
  await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
435
531
  console.log('\n\x1b[32mEngine started successfully\x1b[0m');
436
- console.log(`\n\x1b[32m[Fleetbo]\x1b[1m -------------------------------------------------------------`);
437
- console.log('\x1b[32m[Fleetbo] \x1b[1mGO GO GO ! FLEETBO COCKPIT IS READY\x1b[0m');
438
- console.log('\x1b[32m[Fleetbo] Start coding and previewing in Cockpit. 🚀\x1b[0m');
439
- console.log(`\x1b[32m[Fleetbo]\x1b[1m -------------------------------------------------------------`);
532
+ console.log(`\n\x1b[32m[Fleetbo]\x1b[0m -------------------------------------------------------------`);
533
+ console.log('\x1b[32m[Fleetbo] \x1b[1mGO GO GO ! FLEETBO COCKPIT IS READY\x1b[0m');
534
+ console.log('\x1b[32m[Fleetbo] You can now start coding and previewing in Studio. 🚀\x1b[0m');
535
+ console.log(`\x1b[32m[Fleetbo]\x1b[0m -------------------------------------------------------------`);
440
536
  console.log(`\x1b[34mPilot Instruction ❯\x1b[0m Switch to your Fleetbo Cockpit tab to begin.\n`);
441
537
  } catch (err) {
442
538
  console.error(`[Fleetbo] Sync Error: ${err.message}`);
@@ -445,13 +541,13 @@ else {
445
541
 
446
542
  async function runDevEnvironment() {
447
543
  console.log(`[Fleetbo] 🛡️ Initializing Dev Environment...`);
544
+
545
+ // Mise à jour silencieuse de browserslist
448
546
  try {
449
- // Mise à jour silencieuse de la DB browserslist pour éliminer le warning "Old Data"
450
547
  const npxExec = process.platform === 'win32' ? 'npx.cmd' : 'npx';
451
548
  execSync(`${npxExec} -y update-browserslist-db@latest`, { stdio: 'ignore' });
452
- } catch (e) {
453
- // Si ça échoue (pas d'internet), on continue sans bloquer le démarrage
454
- }
549
+ } catch (e) {}
550
+
455
551
  killNetworkService();
456
552
  killProcessOnPort(PORT);
457
553
 
@@ -511,4 +607,4 @@ else {
511
607
  }
512
608
 
513
609
  runDevEnvironment();
514
- }
610
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbo-cockpit-cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Fleetbo CLI - Build native mobile apps with React",
5
5
  "author": "Fleetbo",
6
6
  "license": "MIT",