fleetbo-cockpit-cli 1.0.72 → 1.0.73
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 +134 -51
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -146,9 +146,10 @@ const showEnergyTransfer = async () => {
|
|
|
146
146
|
|
|
147
147
|
// Détecte TOUS les mots en PascalCase (ex: GuestCreator, CameraModule, Tab2)
|
|
148
148
|
const extractPotentialModules = (text) => {
|
|
149
|
-
|
|
149
|
+
// Regex stricte : Majuscule + minuscules + Majuscule (ex: ProfileCreator, UserConfig)
|
|
150
|
+
const regex = /\b[A-Z][a-z]+[A-Z][a-zA-Z0-9_]*\b/g;
|
|
150
151
|
const matches = text.match(regex) || [];
|
|
151
|
-
return [...new Set(matches)];
|
|
152
|
+
return [...new Set(matches)];
|
|
152
153
|
};
|
|
153
154
|
|
|
154
155
|
|
|
@@ -219,8 +220,8 @@ if (command === 'alex') {
|
|
|
219
220
|
return;
|
|
220
221
|
}
|
|
221
222
|
|
|
222
|
-
// 📋 INTERCEPTION
|
|
223
|
-
const isListingRequest = /^(liste|list|quels modules|montre les modules)/i.test(prompt);
|
|
223
|
+
// 📋 INTERCEPTION: LIST MODULES
|
|
224
|
+
const isListingRequest = /^(liste|list|quels modules|montre les modules|show modules)/i.test(prompt);
|
|
224
225
|
if (isListingRequest) {
|
|
225
226
|
process.stdout.write(` \x1b[90m🔍 Scanning OS Cache...\x1b[0m\n`);
|
|
226
227
|
const cacheRes = await getModuleCache({ projectId, moduleName: null });
|
|
@@ -232,7 +233,7 @@ if (command === 'alex') {
|
|
|
232
233
|
console.log(` ${metalColor}■\x1b[0m \x1b[1m${m.moduleName}\x1b[0m \x1b[90m(${m.platform})\x1b[0m`);
|
|
233
234
|
});
|
|
234
235
|
} else {
|
|
235
|
-
console.log(`\n \x1b[
|
|
236
|
+
console.log(`\n \x1b[90mNo modules found in the infrastructure of this project.\x1b[0m`);
|
|
236
237
|
}
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
@@ -240,33 +241,70 @@ if (command === 'alex') {
|
|
|
240
241
|
console.log('\x1b[33m🧠 Alex is thinking...\x1b[0m');
|
|
241
242
|
|
|
242
243
|
try {
|
|
243
|
-
// ---
|
|
244
|
+
// --- MEMORY SYSTEM (SMART CACHE SCANNER V3 - SOUVERAINETÉ DU MÉTAL) ---
|
|
244
245
|
let contextInjection = "";
|
|
245
246
|
const potentialModules = extractPotentialModules(prompt);
|
|
247
|
+
|
|
248
|
+
let targetModuleContext = "";
|
|
249
|
+
let referenceContexts = "";
|
|
246
250
|
|
|
247
|
-
for (
|
|
248
|
-
|
|
251
|
+
for (let modName of potentialModules) {
|
|
252
|
+
let isReferenceOnly = false;
|
|
253
|
+
|
|
254
|
+
// 🟢 LE DÉTECTEUR D'INSPIRATION
|
|
255
|
+
if (modName.endsWith('Ref')) {
|
|
256
|
+
isReferenceOnly = true;
|
|
257
|
+
modName = modName.replace('Ref', '');
|
|
258
|
+
} else if (modName.endsWith('Schema')) {
|
|
259
|
+
isReferenceOnly = true;
|
|
260
|
+
modName = modName.replace('Schema', '');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
process.stdout.write(` \x1b[90m🔍 Checking OS cache for ${modName}...\x1b[0m`);
|
|
249
264
|
const cache = await getModuleCache({ projectId, moduleName: modName });
|
|
250
265
|
|
|
251
266
|
if (cache.found) {
|
|
252
267
|
process.stdout.write(` \x1b[32mFOUND METAL\x1b[0m\n`);
|
|
253
268
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
contextInjection = `\n--- CONTEXTE SOUVERAIN : ${contextTitle} (${modName}) ---\nDOGME: Le Métal est la source de vérité absolue. Le Mock n'est qu'une illusion.\nTu DOIS analyser ce code Natif pour comprendre EXACTEMENT comment les données ont été structurées, nommées (clés JSON) et sauvegardées.\n\n[CODE NATIF EXISTANT]\n${cache.module.code}\n--- FIN DU CONTEXTE ---\n`;
|
|
269
|
+
// ✅ NOUVEAU BLOC : INJECTION DE LA DOUBLE MÉMOIRE
|
|
270
|
+
let memoryScript = "";
|
|
271
|
+
if (cache.module.dataSchema) memoryScript += `\n[SCRIPT MÉMOIRE DONNÉES]\n${cache.module.dataSchema}\n`;
|
|
272
|
+
if (cache.module.uiSchema) memoryScript += `\n[SCRIPT MÉMOIRE UI NATIF]\n${cache.module.uiSchema}\n`;
|
|
260
273
|
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
if (!memoryScript) memoryScript = `\n[SCRIPT MÉMOIRE DU MODULE ${modName}]\nAucun schéma enregistré pour ce module.\n`;
|
|
275
|
+
|
|
276
|
+
if (isReferenceOnly) {
|
|
277
|
+
// 🚨 CAS A : INSPIRATION (Lecture seule)
|
|
278
|
+
referenceContexts += `\n--- CONTEXTE : MODULE DE RÉFÉRENCE (${modName}) ---\nDOGME : Ne modifie pas ce module (Lecture seule). Tu dois l'utiliser comme modèle. Aligne-toi sur ses Scripts Mémoires (Données et/ou UI) et sur son Code Natif en fonction de ce que le Pilote te demande d'imiter.\n${memoryScript}\n[CODE NATIF DE RÉFÉRENCE]\n${cache.module.code}\n`;
|
|
279
|
+
} else if (!targetModuleContext) {
|
|
280
|
+
// 🚨 CAS B : CIBLE PRINCIPALE (Modification)
|
|
281
|
+
// LE MOCK EST BANI ! Le Métal est la seule source de vérité.
|
|
282
|
+
const intent = getContextIntent(prompt);
|
|
283
|
+
if (intent === "MODIFICATION") {
|
|
284
|
+
targetModuleContext = `\n--- CONTEXTE : MÉTAL EXISTANT À MODIFIER (${modName}) ---\nDOGME: Tu dois modifier ce code Natif. Ensuite, tu forgeras un Mock JSX entièrement neuf basé UNIQUEMENT sur ton nouveau code Natif.\n${memoryScript}\n[CODE NATIF EXISTANT]\n${cache.module.code}\n--- FIN DU CONTEXTE ---\n`;
|
|
285
|
+
} else {
|
|
286
|
+
targetModuleContext = `\n--- CONTEXTE : BASE DE TRAVAIL (${modName}) ---\n${memoryScript}\n`;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
263
289
|
} else {
|
|
264
290
|
process.stdout.write(` \x1b[31mNOT FOUND\x1b[0m\n`);
|
|
265
291
|
}
|
|
266
292
|
}
|
|
267
|
-
// --- FIN MODIFICATION MÉMOIRE ---
|
|
268
293
|
|
|
269
|
-
|
|
294
|
+
contextInjection = referenceContexts + targetModuleContext;
|
|
295
|
+
if (contextInjection) {
|
|
296
|
+
prompt = contextInjection + "\n\n[INSTRUCTION DU PILOTE]\n" + prompt;
|
|
297
|
+
}
|
|
298
|
+
// --- END MEMORY MODIFICATION ---
|
|
299
|
+
|
|
300
|
+
// 🟢 NEW: Real-time timestamp injection
|
|
301
|
+
const now = new Date();
|
|
302
|
+
// Clean format: YYYY-MM-DD at HH:MM:SS
|
|
303
|
+
const exactTime = now.toISOString().split('T')[0] + ' at ' + now.toTimeString().split(' ')[0];
|
|
304
|
+
|
|
305
|
+
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.]`;
|
|
306
|
+
|
|
307
|
+
const result = await axios.post(ALEX_ENGINE_URL, { prompt: promptWithTime, projectType: 'android' }, {
|
|
270
308
|
headers: { 'x-project-id': projectId }
|
|
271
309
|
});
|
|
272
310
|
|
|
@@ -276,6 +314,12 @@ if (command === 'alex') {
|
|
|
276
314
|
try { aiData = JSON.parse(aiData); } catch (_) {}
|
|
277
315
|
}
|
|
278
316
|
|
|
317
|
+
// 🟢 DISPLAY REASONING IN TERMINAL
|
|
318
|
+
if (aiData.thinking_process) {
|
|
319
|
+
// Show start of reasoning in gray for info
|
|
320
|
+
console.log(` \x1b[90m🧠 Alex Analysis: ${aiData.thinking_process.substring(0, 150)}...\x1b[0m`);
|
|
321
|
+
}
|
|
322
|
+
|
|
279
323
|
process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
|
|
280
324
|
|
|
281
325
|
if (aiData.status === 'quota_exceeded') {
|
|
@@ -312,21 +356,21 @@ if (command === 'alex') {
|
|
|
312
356
|
const formattedMsg = wrapText(rawMsg, 85);
|
|
313
357
|
console.log('\x1b[32mAlex ❯\x1b[0m ' + formattedMsg);
|
|
314
358
|
|
|
315
|
-
if (aiData.remainingConsultations !== undefined) {
|
|
316
|
-
const remaining = aiData.remainingConsultations;
|
|
317
|
-
const limit = aiData.consultationLimit || 7;
|
|
318
|
-
const tierLabel = aiData.tier === 'senior' ? 'SENIOR' : aiData.tier === 'expert' ? 'EXPERT' : 'JUNIOR';
|
|
319
|
-
const percent = Math.round((remaining / limit) * 100);
|
|
320
|
-
const energyColor = percent > 20 ? '\x1b[32m' : '\x1b[31m';
|
|
321
|
-
console.log(`\n\x1b[36m⚡ Architect Fuel:\x1b[0m ${energyColor}${percent}%\x1b[0m (${remaining}/${limit} instructions left) [${tierLabel}]\n`);
|
|
322
|
-
}
|
|
323
359
|
}
|
|
324
360
|
|
|
325
|
-
// ---
|
|
361
|
+
// --- FILE CREATION LOGIC ---
|
|
326
362
|
if (aiData.status === 'success' && aiData.moduleData) {
|
|
327
|
-
|
|
363
|
+
let { fileName, code, mockFileName, mockCode, moduleName, instructions, config_offload, dataSchema, uiSchema } = aiData.moduleData;
|
|
364
|
+
|
|
365
|
+
// 🛡️ ANTI-DUMP SHIELD (Prevents terminal flooding)
|
|
366
|
+
if (moduleName) {
|
|
367
|
+
// Cleanup module name
|
|
368
|
+
moduleName = moduleName.split('\n')[0].replace(/["'{}]/g, '').trim();
|
|
369
|
+
if (moduleName.length > 40) moduleName = moduleName.substring(0, 40) + "...";
|
|
370
|
+
}
|
|
371
|
+
|
|
328
372
|
console.log(` \x1b[90m Architecting: ${moduleName}\x1b[0m`);
|
|
329
|
-
|
|
373
|
+
|
|
330
374
|
const writeFile = (dir, name, content) => {
|
|
331
375
|
const fullPath = path.join(process.cwd(), dir);
|
|
332
376
|
const filePath = path.join(fullPath, name);
|
|
@@ -360,7 +404,7 @@ if (command === 'alex') {
|
|
|
360
404
|
}
|
|
361
405
|
}
|
|
362
406
|
|
|
363
|
-
// ---
|
|
407
|
+
// --- KERNEL SYNCHRONIZATION ---
|
|
364
408
|
const depsCount = config_offload?.dependencies?.length || 0;
|
|
365
409
|
process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Archiving ${moduleName} to OS (${depsCount} libs)...`);
|
|
366
410
|
|
|
@@ -374,7 +418,9 @@ if (command === 'alex') {
|
|
|
374
418
|
code: code,
|
|
375
419
|
mockFileName: mockFileName,
|
|
376
420
|
mockCode: mockCode,
|
|
377
|
-
config_offload: config_offload || { dependencies: [], permissions: [] }
|
|
421
|
+
config_offload: config_offload || { dependencies: [], permissions: [] },
|
|
422
|
+
dataSchema: dataSchema || null, // 👈 LA LIGNE MAGIQUE
|
|
423
|
+
uiSchema: uiSchema || null
|
|
378
424
|
}
|
|
379
425
|
});
|
|
380
426
|
process.stdout.write(` \x1b[32mOK\x1b[0m\n`);
|
|
@@ -382,9 +428,18 @@ if (command === 'alex') {
|
|
|
382
428
|
process.stdout.write(` \x1b[31mFAILED\x1b[0m\n`);
|
|
383
429
|
console.error(` ⚠️ OS sync failed: ${err.message}`);
|
|
384
430
|
}
|
|
431
|
+
// 🟢 LE FUEL EST DÉPLACÉ ICI (TOUTE FIN DU TRY)
|
|
432
|
+
if (aiData.remainingConsultations !== undefined) {
|
|
433
|
+
const remaining = aiData.remainingConsultations;
|
|
434
|
+
const limit = aiData.consultationLimit || 7;
|
|
435
|
+
const tierLabel = aiData.tier.toUpperCase();
|
|
436
|
+
const percent = Math.round((remaining / limit) * 100);
|
|
437
|
+
const energyColor = percent > 20 ? '\x1b[32m' : '\x1b[31m';
|
|
438
|
+
console.log(`\n\x1b[36m⚡ Architect Fuel:\x1b[0m ${energyColor}${percent}%\x1b[0m (${remaining}/${limit} instructions left) [${tierLabel}]\n`);
|
|
439
|
+
}
|
|
385
440
|
} else if (aiData.status === 'success' && !aiData.moduleData) {
|
|
386
|
-
//
|
|
387
|
-
console.log(`\n\x1b[31m⚠️
|
|
441
|
+
// SAFETY: If formatting is broken
|
|
442
|
+
console.log(`\n\x1b[31m⚠️ Error: Alex replied, but source code could not be extracted. Try the command again.\x1b[0m\n`);
|
|
388
443
|
}
|
|
389
444
|
} catch (error) {
|
|
390
445
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
@@ -426,25 +481,25 @@ if (command === 'alex') {
|
|
|
426
481
|
|
|
427
482
|
process.stdout.write(' '.repeat(60) + '\r');
|
|
428
483
|
|
|
429
|
-
// 1.
|
|
484
|
+
// 1. IDENTITY
|
|
430
485
|
console.log('\n\x1b[32m🤖 Alex is online.\x1b[0m');
|
|
431
486
|
console.log('\x1b[90m Your JS stays the brain. I forge the native muscle.\x1b[0m');
|
|
432
487
|
|
|
433
|
-
// 2.
|
|
488
|
+
// 2. FORGE CAPABILITIES
|
|
434
489
|
console.log('\n\x1b[36m⚡ WHAT I CAN FORGE:\x1b[0m');
|
|
435
490
|
console.log('');
|
|
436
491
|
console.log(' \x1b[1m📷 Hardware\x1b[0m\x1b[90m Camera, Scanner, GPS, Biometrics, Sensors\x1b[0m');
|
|
437
492
|
console.log(' \x1b[1m🎬 High-Perf\x1b[0m\x1b[90m Infinite Feeds, Video Players, Swipe Decks\x1b[0m');
|
|
438
493
|
console.log(' \x1b[1m🏗️ Sovereign\x1b[0m\x1b[90m Full screens: form + photo + save-to-cloud\x1b[0m');
|
|
439
494
|
|
|
440
|
-
// 3.
|
|
495
|
+
// 3. COLLABORATION
|
|
441
496
|
console.log('\n\x1b[36m💡 TELL ME "WHAT + WHY":\x1b[0m');
|
|
442
497
|
console.log('\x1b[90m I will analyze your need and recommend the perfect module to forge.\x1b[0m');
|
|
443
498
|
console.log('');
|
|
444
499
|
console.log(' \x1b[33m"I need a camera [WHAT] to scan receipts for my expense tracker [WHY]"\x1b[0m');
|
|
445
500
|
console.log(' \x1b[33m"I need a form [WHAT] to add products with photos to my catalog [WHY]"\x1b[0m');
|
|
446
501
|
|
|
447
|
-
// 4.
|
|
502
|
+
// 4. READINESS
|
|
448
503
|
console.log('\n\x1b[32mAlex ❯\x1b[0m I am ready. Describe your feature, and I will architect the solution.');
|
|
449
504
|
console.log('');
|
|
450
505
|
|
|
@@ -481,7 +536,7 @@ if (command === 'alex') {
|
|
|
481
536
|
inputBuffer = "";
|
|
482
537
|
|
|
483
538
|
if (finalPrompt.length > 1000) {
|
|
484
|
-
console.log(`\n\x1b[31m⛔ [Alex Safety] Mission
|
|
539
|
+
console.log(`\n\x1b[31m⛔ [Alex Safety] Mission rejected: Excessive size (${finalPrompt.length}/1000 characters).\x1b[0m`);
|
|
485
540
|
rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
|
|
486
541
|
rl.prompt();
|
|
487
542
|
return;
|
|
@@ -796,13 +851,13 @@ else {
|
|
|
796
851
|
try {
|
|
797
852
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
798
853
|
console.log('\n\x1b[32mEngine started successfully\x1b[0m');
|
|
799
|
-
console.log(`\x1b[
|
|
800
|
-
console.log('\x1b[
|
|
801
|
-
console.log('\x1b[
|
|
802
|
-
console.log(`\x1b[
|
|
803
|
-
console.log(`\x1b[34mPilot Instruction ❯\x1b[0m
|
|
854
|
+
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
855
|
+
console.log('\x1b[32mFleetbo OS ❯\x1b[0m \x1b[1mGO GO GO ! OS IS READY\x1b[0m');
|
|
856
|
+
console.log('\x1b[32mFleetbo OS ❯\x1b[0m You can now start coding and previewing. 🚀');
|
|
857
|
+
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
858
|
+
console.log(`\x1b[34mPilot Instruction ❯\x1b[0m Return to the Workspace. The Engine is ready for your orders.\n`);
|
|
804
859
|
} catch (err) {
|
|
805
|
-
console.error(
|
|
860
|
+
console.error(`\x1b[31mFleetbo OS ❯\x1b[0m Sync Error: ${err.message}`);
|
|
806
861
|
}
|
|
807
862
|
}
|
|
808
863
|
|
|
@@ -839,7 +894,7 @@ else {
|
|
|
839
894
|
}
|
|
840
895
|
});
|
|
841
896
|
|
|
842
|
-
devServer.stdout.pipe(process.stdout);
|
|
897
|
+
//devServer.stdout.pipe(process.stdout);
|
|
843
898
|
devServer.stderr.pipe(process.stderr);
|
|
844
899
|
|
|
845
900
|
let connectionStarted = false;
|
|
@@ -847,13 +902,39 @@ else {
|
|
|
847
902
|
devServer.stdout.on('data', (data) => {
|
|
848
903
|
const output = data.toString();
|
|
849
904
|
|
|
905
|
+
// 🛡️ FILTRE ANTI-PLOMBERIE FLEETBO
|
|
906
|
+
const lines = output.split('\n');
|
|
907
|
+
const forbiddenTerms = [
|
|
908
|
+
'Attempting to bind to HOST',
|
|
909
|
+
'If this was unintentional',
|
|
910
|
+
'Learn more here:',
|
|
911
|
+
'Starting the development server',
|
|
912
|
+
'You can now view',
|
|
913
|
+
'Local:',
|
|
914
|
+
'On Your Network:',
|
|
915
|
+
'Note that the development build',
|
|
916
|
+
'To create a production build',
|
|
917
|
+
'webpack compiled successfully'
|
|
918
|
+
];
|
|
919
|
+
|
|
920
|
+
// On filtre les lignes pour ne garder que le vrai code/debug
|
|
921
|
+
const filteredOutput = lines.filter(line => {
|
|
922
|
+
return !forbiddenTerms.some(term => line.includes(term));
|
|
923
|
+
}).join('\n');
|
|
924
|
+
|
|
925
|
+
// S'il reste quelque chose d'utile (un console.log du dev, un warning, une vraie erreur), on l'affiche
|
|
926
|
+
if (filteredOutput.trim() !== '') {
|
|
927
|
+
process.stdout.write(filteredOutput + '\n');
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// 🚀 DÉTECTION DU DÉMARRAGE ET LANCEMENT DE L'UPLINK
|
|
850
931
|
if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
851
932
|
connectionStarted = true;
|
|
852
933
|
|
|
853
|
-
console.log('\
|
|
854
|
-
console.log(
|
|
855
|
-
console.log(
|
|
856
|
-
console.log('[
|
|
934
|
+
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
935
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m 🔗 Establishing Secure Uplink...`);
|
|
936
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m ⏳ Please wait for the green message...`);
|
|
937
|
+
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
857
938
|
|
|
858
939
|
// ============================================
|
|
859
940
|
// UPLINK avec auto-retry (Fleetbo OS Resilience)
|
|
@@ -885,11 +966,13 @@ else {
|
|
|
885
966
|
const match = text.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/);
|
|
886
967
|
if (match) {
|
|
887
968
|
uplinkFound = true;
|
|
888
|
-
|
|
969
|
+
// ⚡ Stabilisation du noyau : on attend 1.5s
|
|
970
|
+
setTimeout(() => {
|
|
971
|
+
syncFirebase(process.env.REACT_KEY_APP, match[0], process.env.REACT_APP_TESTER_EMAIL);
|
|
972
|
+
}, 1500);
|
|
889
973
|
}
|
|
890
974
|
};
|
|
891
975
|
|
|
892
|
-
// Écoute sur les deux flux (stdout + stderr)
|
|
893
976
|
uplinkProcess.stdout.on('data', handleUplinkOutput);
|
|
894
977
|
uplinkProcess.stderr.on('data', handleUplinkOutput);
|
|
895
978
|
|