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.
- package/cli.js +81 -94
- 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
|
-
|
|
440
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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' &&
|
|
505
|
-
let { fileName, code, mockFileName, mockCode, moduleName,
|
|
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
|
-
|
|
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
|
-
//
|
|
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');
|
|
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('');
|
|
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) => {
|