fleetbo-cockpit-cli 1.0.216 → 1.0.217

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 +81 -94
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -390,7 +390,6 @@ if (command === 'alex') {
390
390
  }
391
391
 
392
392
  if (actualMetalCode) {
393
- //const sourceText = fs.existsSync(localKtPath) ? "LOCAL" : "CACHE";
394
393
  process.stdout.write(` \x1b[32mFOUND METAL\x1b[0m\n`);
395
394
 
396
395
  // Extraction des schémas mémoires (uniquement dispo dans le cache cloud)
@@ -436,32 +435,24 @@ if (command === 'alex') {
436
435
 
437
436
  const promptWithTime = prompt + `\n\n[SYSTEM INFO: The exact current timestamp is ${exactTime}. Use it STRICTLY for your signature '// ⚡ Forged by Alex on...' at the bottom of your files.]`;
438
437
 
439
- const result = await axios.post(ALEX_ENGINE_URL, { prompt: promptWithTime, projectType: 'android', jsFramework: JS_FRAMEWORK }, {
440
- headers: { 'x-project-id': projectId }
441
- });
442
-
443
- let aiData = result.data;
444
-
445
- // 🛡️ ROBUST PARSER: Unwrap all nesting levels until we find a valid aiData object
438
+ // =================================================================
439
+ // 🚀 THE AUTOMATED 3-STEP WORKFLOW (Cross-Platform)
440
+ // =================================================================
441
+
446
442
  const deepUnwrap = (raw, depth = 0) => {
447
- if (depth > 5) return raw; // Safety: max 5 nesting levels
443
+ if (depth > 5) return raw;
448
444
  if (typeof raw === 'string') {
449
- // Try to extract a JSON object even if there's trailing garbage (truncation)
450
445
  const jsonMatch = raw.match(/\{[\s\S]*\}/);
451
446
  if (jsonMatch) {
452
- try {
453
- return deepUnwrap(JSON.parse(jsonMatch[0]), depth + 1);
454
- } catch (_) {}
447
+ try { return deepUnwrap(JSON.parse(jsonMatch[0]), depth + 1); } catch (_) {}
455
448
  }
456
449
  return raw;
457
450
  }
458
451
  if (typeof raw === 'object' && raw !== null) {
459
- // If the message field itself is a JSON string, unwrap it and merge moduleData
460
452
  if (typeof raw.message === 'string' && raw.message.trimStart().startsWith('{')) {
461
453
  try {
462
454
  const nested = JSON.parse(raw.message);
463
455
  if (nested && typeof nested === 'object' && nested.status) {
464
- // Merge: keep top-level moduleData if present, else take nested one
465
456
  if (!raw.moduleData && nested.moduleData) raw.moduleData = nested.moduleData;
466
457
  raw.message = nested.message || raw.message;
467
458
  if (!raw.status && nested.status) raw.status = nested.status;
@@ -473,45 +464,98 @@ if (command === 'alex') {
473
464
  return raw;
474
465
  };
475
466
 
476
- aiData = deepUnwrap(aiData);
467
+ const fetchStep = async (stepName, memory = {}) => {
468
+ const res = await axios.post(ALEX_ENGINE_URL, {
469
+ prompt: promptWithTime,
470
+ projectType: 'android',
471
+ jsFramework: JS_FRAMEWORK,
472
+ step: stepName,
473
+ memory: memory
474
+ }, { headers: { 'x-project-id': projectId } });
477
475
 
478
- // DISPLAY REASONING IN TERMINAL
479
- if (aiData.thinking_process) {
480
- // Show start of reasoning in gray for info
481
- console.log(` \x1b[90m🧠 Alex Analysis: ${aiData.thinking_process.substring(0, 150)}...\x1b[0m`);
482
- }
476
+ return deepUnwrap(res.data);
477
+ };
478
+
479
+ // Fonction pour animer les étapes silencieusement
480
+ const logStep = (icon, text) => {
481
+ // \x1b[2K efface la ligne courante, \r ramène au début
482
+ process.stdout.write(`\x1b[2K\r ${icon} ${text}`);
483
+ };
483
484
 
484
485
  process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
485
486
 
487
+ // --- ÉTAPE 1 : NATIF ---
488
+ logStep('🧠', '\x1b[33m1/3 - Alex forge les fondations natives...\x1b[0m');
489
+ let aiData = await fetchStep('native_only');
490
+
486
491
  if (aiData.status === 'quota_exceeded') {
487
492
  console.log(`\n\x1b[31m⛔ ARCHITECT QUOTA REACHED:\x1b[0m ${aiData.message}`);
488
493
  return;
489
494
  }
490
-
491
- if (aiData.status === 'success' || aiData.status === 'message' || aiData.status === 'complex_refusal') {
492
- let rawMsg = aiData.message || "I'm ready.";
493
495
 
496
+ let mergedModuleData = aiData.moduleData || {};
497
+ let aiDataMessage = aiData.message;
498
+
499
+ // 🛡️ RÉCTROCOMPATIBILITÉ BACKEND :
500
+ // Si le cloud renvoie déjà le Mock, c'est que ton index.js n'est pas encore mis à jour.
501
+ // On bypass la suite pour ne pas générer de doublons et consommer du token pour rien.
502
+ const isCloudUpdated = !mergedModuleData.mockCode;
503
+
504
+ if (isCloudUpdated && mergedModuleData.code) {
505
+ logStep('✅', '\x1b[32m1/3 - Code Natif généré.\x1b[0m\n');
506
+
507
+ // --- ÉTAPE 2 : MOCK ---
508
+ logStep('🎨', '\x1b[33m2/3 - Analyse du Natif et génération de l\'Interface (Mock)...\x1b[0m');
509
+ const aiData2 = await fetchStep('mock_only', { nativeCode: mergedModuleData.code });
510
+
511
+ if (aiData2.moduleData) {
512
+ mergedModuleData.mockCode = aiData2.moduleData.mockCode;
513
+ mergedModuleData.mockFileName = aiData2.moduleData.mockFileName;
514
+ }
515
+ logStep('✅', '\x1b[32m2/3 - Interface Mock générée.\x1b[0m\n');
516
+
517
+ // --- ÉTAPE 3 : JSON CONFIG ---
518
+ logStep('⚙️', '\x1b[33m3/3 - Finalisation de la plomberie JSON...\x1b[0m');
519
+ const aiData3 = await fetchStep('json_only', {
520
+ nativeCode: mergedModuleData.code,
521
+ mockCode: mergedModuleData.mockCode
522
+ });
523
+
524
+ if (aiData3.moduleData) {
525
+ mergedModuleData.config_offload = aiData3.moduleData.config_offload;
526
+ mergedModuleData.dataSchema = aiData3.moduleData.dataSchema;
527
+ mergedModuleData.uiSchema = aiData3.moduleData.uiSchema;
528
+ }
529
+ logStep('✅', '\x1b[32m3/3 - Configuration terminée.\x1b[0m\n');
530
+ } else {
531
+ // Le cloud n'est pas à jour, on efface le loader de l'étape 1 en silence
532
+ process.stdout.write(`\x1b[2K\r`);
533
+ }
534
+
535
+ // DISPLAY REASONING IN TERMINAL
536
+ if (aiData.thinking_process) {
537
+ console.log(` \x1b[90m🧠 Alex Analysis: ${aiData.thinking_process.substring(0, 150)}...\x1b[0m`);
538
+ }
539
+
540
+ // --- OUTPUT VERS XTERM ---
541
+ if (aiData.status === 'success' || aiData.status === 'message' || aiData.status === 'complex_refusal') {
542
+ let rawMsg = aiDataMessage || "I'm ready.";
494
543
  if (typeof rawMsg === 'object') {
495
544
  rawMsg = rawMsg.message || rawMsg.text || "Module generated.";
496
545
  }
497
-
498
- // ON DÉLÈGUE L'AFFICHAGE AU FRONTEND (Xterm.jsx)
499
- // On envoie le texte pur entouré de balises invisibles
500
546
  console.log(`[ALEX_START]${rawMsg}[ALEX_END]`);
501
547
  }
502
548
 
503
549
  // --- FILE CREATION LOGIC ---
504
- if (aiData.status === 'success' && aiData.moduleData) {
505
- let { fileName, code, mockFileName, mockCode, moduleName, instructions, config_offload, dataSchema, uiSchema } = aiData.moduleData;
550
+ if (aiData.status === 'success' && mergedModuleData && Object.keys(mergedModuleData).length > 0) {
551
+ let { fileName, code, mockFileName, mockCode, moduleName, config_offload, dataSchema, uiSchema } = mergedModuleData;
506
552
 
507
- // 🛡️ ANTI-DUMP SHIELD (Prevents terminal flooding)
508
553
  if (moduleName) {
509
- // Cleanup module name
510
554
  moduleName = moduleName.split('\n')[0].replace(/["'{}]/g, '').trim();
511
555
  if (moduleName.length > 40) moduleName = moduleName.substring(0, 40) + "...";
512
556
  }
513
557
 
514
- console.log(`\n \x1b[90mArchitecting: ${moduleName}\x1b[0m`);
558
+ console.log(`\n \x1b[90mArchitecting: ${moduleName || 'Unknown Module'}\x1b[0m`);
515
559
 
516
560
  const writeFile = (dir, name, content) => {
517
561
  const fullPath = path.join(process.cwd(), dir);
@@ -520,19 +564,6 @@ if (command === 'alex') {
520
564
  fs.writeFileSync(filePath, content);
521
565
  };
522
566
 
523
- // --- SUPPRIME CE BLOC ENTIER ---
524
- /*
525
- if (instructions && Array.isArray(instructions) && instructions.length > 0) {
526
- console.log('\n\x1b[33m--- GUIDE (MCI) ---\x1b[0m');
527
- instructions.forEach(line => {
528
- if (typeof line === 'string') {
529
- const formattedLine = line.replace(/ACTION|CAPTURE|PERSPECTIVE/g, '\x1b[1m$&\x1b[0m');
530
- console.log(` ${formattedLine}`);
531
- }
532
- });
533
- }
534
- */
535
-
536
567
  if (code && fileName) {
537
568
  const folder = fileName.endsWith('.kt') ? 'public/native/android/' : 'src/app/';
538
569
  writeFile(folder, fileName, code);
@@ -570,21 +601,14 @@ if (command === 'alex') {
570
601
  console.error(` ⚠️ OS sync failed: ${err.message}`);
571
602
  }
572
603
 
573
- // 💡 LES ASTUCES CONTEXTUELLES DYNAMIQUES
574
604
  console.log('');
575
-
576
- // Astuce 1 : Le Patch manuel
577
605
  console.log(`\x1b[90m If you manually edit the native code, sync it to the Cloud OS:\x1b[0m`);
578
606
  console.log(`\x1b[33m patch module\x1b[0m \x1b[36m${moduleName}\x1b[0m \x1b[33mandroid\x1b[0m \x1b[90m(or ios)\x1b[0m`);
579
-
580
- // Astuce 2 : L'éradication
581
607
  console.log(`\x1b[90m To eradicate this module later, open a new terminal and type:\x1b[0m`);
582
608
  console.log(`\x1b[31m npm run fleetbo rm\x1b[0m \x1b[36m${moduleName}\x1b[0m`);
583
609
 
584
-
585
- } else if (aiData.status === 'success' && !aiData.moduleData) {
586
- // SAFETY: If formatting is broken
587
- console.log(`\n\x1b[31m⚠️ Error: Alex replied, but source code could not be extracted. Try the command again.\x1b[0m\n`);
610
+ } else if (aiData.status === 'success' && (!mergedModuleData || Object.keys(mergedModuleData).length === 0)) {
611
+ console.log(`\n\x1b[31m⚠️ Error: Alex replied, but source code could not be extracted.\x1b[0m\n`);
588
612
  }
589
613
  } catch (error) {
590
614
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
@@ -593,12 +617,7 @@ if (command === 'alex') {
593
617
  };
594
618
 
595
619
  // ============================================================
596
- // CLI PATCH startAlexSession (cli.js)
597
- //
598
- // Remplace le bloc ENTIER de startAlexSession, depuis :
599
- // const startAlexSession = async () => {
600
- // Jusqu'à (NON INCLUS) :
601
- // const rl = readline.createInterface({
620
+ // DÉMARRAGE DE LA SESSION INTERACTIVE ALEX
602
621
  // ============================================================
603
622
 
604
623
  const startAlexSession = async () => {
@@ -619,8 +638,6 @@ if (command === 'alex') {
619
638
 
620
639
  if (validation.data?.isRunning) {
621
640
  isReady = true;
622
- // ASTUCE : On confirme que le moteur est allumé
623
- // process.stdout.write('[ALEX_ONLINE]\n');
624
641
  dynamicUsername = validation.data.username || 'Pilot';
625
642
  hasAiKey = validation.data.hasAiKey || false;
626
643
  aiProvider = validation.data.aiProvider || null;
@@ -643,9 +660,6 @@ if (command === 'alex') {
643
660
 
644
661
  process.stdout.write(' '.repeat(60) + '\r');
645
662
 
646
- // ═══════════════════════════════════════════════
647
- // AI KEY CHECK — Block if no key configured
648
- // ═══════════════════════════════════════════════
649
663
  if (!hasAiKey) {
650
664
  console.log('');
651
665
  console.log('\x1b[1m\x1b[33m AI Configuration Required\x1b[0m');
@@ -665,9 +679,6 @@ if (command === 'alex') {
665
679
  process.exit(0);
666
680
  }
667
681
 
668
- // ═══════════════════════════════════════════════
669
- // ALEX WELCOME — Key is configured, show capabilities
670
- // ═══════════════════════════════════════════════
671
682
  const providerLabel = aiProvider === 'anthropic' ? 'Anthropic' : 'Google AI';
672
683
  const modelLabel = aiModel && aiModel !== 'auto' ? aiModel : 'auto';
673
684
 
@@ -681,12 +692,10 @@ if (command === 'alex') {
681
692
  console.log('\x1b[90m│ │\x1b[0m');
682
693
  console.log('\x1b[90m└─────────────────────────────────────────────────────────┘\x1b[0m');
683
694
 
684
- // AI ENGINE STATUS
685
695
  console.log('');
686
696
  console.log(`\x1b[32m✓\x1b[0m ${providerLabel} \x1b[90m· ${modelLabel}\x1b[0m`);
687
697
  console.log(`\x1b[32m✓\x1b[0m ${JS_FRAMEWORK === 'vue' ? '\x1b[32mVue.js\x1b[0m' : '\x1b[36mReact\x1b[0m'} \x1b[90m· JavaScript Framework\x1b[0m`);
688
698
 
689
- // FORGE CAPABILITIES
690
699
  console.log('');
691
700
  console.log('\x1b[36mCAPABILITIES\x1b[0m');
692
701
  console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
@@ -696,7 +705,6 @@ if (command === 'alex') {
696
705
  console.log('\x1b[1mFleetbo View\x1b[0m\x1b[90m Full native tab (120 FPS)\x1b[0m');
697
706
  console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
698
707
 
699
- // TRIGGER SYNTAX
700
708
  console.log('');
701
709
  console.log('\x1b[36mCODE GENERATION SYNTAX\x1b[0m \x1b[90m(strict format required)\x1b[0m');
702
710
  console.log('');
@@ -704,31 +712,17 @@ if (command === 'alex') {
704
712
  console.log('\x1b[90mVerbs: \x1b[33mforge\x1b[0m \x1b[90m·\x1b[0m \x1b[33mcreate\x1b[0m \x1b[90m·\x1b[0m \x1b[33mupdate\x1b[0m \x1b[90m·\x1b[0m \x1b[33mmodify\x1b[0m \x1b[90m·\x1b[0m \x1b[33mfix\x1b[0m \x1b[90m·\x1b[0m \x1b[33medit\x1b[0m');
705
713
  console.log('\x1b[90mExample: \x1b[0m\x1b[33mupdate\x1b[0m module \x1b[36mProfileManager\x1b[0m \x1b[90mwith a red button\x1b[0m');
706
714
  console.log('');
707
- console.log('\x1b[90mWithout this exact structure, Alex answers but does not generate code.\x1b[0m');
708
-
709
- // EXAMPLES
710
- console.log('');
711
- console.log('\x1b[36mEXAMPLES\x1b[0m');
712
- console.log('');
713
- console.log('\x1b[33m›\x1b[0m \x1b[90m"\x1b[33mforge\x1b[90m a camera module to scan receipts for my expense tracker"\x1b[0m');
714
- console.log('\x1b[33m›\x1b[0m \x1b[90m"\x1b[33mcreate\x1b[90m a form module to add products with photos to my catalog"\x1b[0m');
715
- console.log('\x1b[33m›\x1b[0m \x1b[90m"\x1b[33mupdate\x1b[90m the CloudCamera module to save in the collection orders"\x1b[0m');
716
-
717
715
 
718
- // READY
719
- console.log('');
720
716
  console.log('\x1b[32mAlex ❯\x1b[0m Describe your feature using the Compose panel...');
721
717
 
722
- // 1. ON SUPPRIME LE PROMPT VISUEL (Plus de "Pilot ❯")
723
718
  const rl = readline.createInterface({
724
719
  input: process.stdin,
725
720
  output: process.stdout
726
721
  });
727
722
 
728
- // LE BOUCLIER ANTI CTRL+C (SIGINT)
729
723
  rl.on('SIGINT', () => {
730
724
  console.log('\n\x1b[90m Alex session aborted (Ctrl+C).\x1b[0m');
731
- process.stdout.write('[ALEX_OFFLINE]\n'); // Prévient React !
725
+ process.stdout.write('[ALEX_OFFLINE]\n');
732
726
  rl.close();
733
727
  process.exit(0);
734
728
  });
@@ -745,22 +739,15 @@ if (command === 'alex') {
745
739
  }
746
740
 
747
741
  if (text !== "") {
748
- if (text.length > 4000) {
749
- console.log(`\n\x1b[31m⛔ [Alex Safety] Mission rejected: Excessive size (${text.length}/4000 characters).\x1b[0m`);
750
- return;
751
- }
752
-
753
742
  isProcessing = true;
754
743
 
755
- // 2. On exécute la requête sans faire de "rl.setPrompt()"
756
744
  await processAlexRequest(text);
757
745
 
758
746
  isProcessing = false;
759
- console.log(''); // Petit saut de ligne esthétique quand Alex a fini
747
+ console.log('');
760
748
  }
761
749
  };
762
750
 
763
- // 3. RÉCEPTEUR SILENCIEUX (Plus de "rl.prompt()" qui pollue l'écran)
764
751
  let pasteTimer = null;
765
752
 
766
753
  rl.on('line', async (line) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbo-cockpit-cli",
3
- "version": "1.0.216",
3
+ "version": "1.0.217",
4
4
  "description": "Fleetbo CLI - Build native mobile apps with React",
5
5
  "author": "Fleetbo",
6
6
  "license": "MIT",