fleetbo-cockpit-cli 1.0.45 → 1.0.47

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 +52 -66
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -151,26 +151,6 @@ const extractPotentialModules = (text) => {
151
151
  return [...new Set(matches)]; // Dédoublonne les résultats
152
152
  };
153
153
 
154
- // 📋 INTERCEPTION : LISTER LES MODULES
155
- const isListingRequest = /^(liste|list|quels modules|montre les modules)/i.test(finalPrompt);
156
- if (isListingRequest) {
157
- process.stdout.write(` \x1b[90m🔍 Scanning Kernel Cache...\x1b[0m\n`);
158
- const cacheRes = await getModuleCache({ projectId, moduleName: null });
159
-
160
- if (cacheRes.found !== false && cacheRes.modules) { // Modification de la condition pour matcher l'API
161
- console.log(`\n\x1b[36m⚡ FLEETBO KERNEL MODULES (${cacheRes.modules.length}):\x1b[0m`);
162
- cacheRes.modules.forEach(m => {
163
- const metalColor = m.platform === 'android' ? '\x1b[32m' : '\x1b[34m'; // Vert Android, Bleu iOS
164
- console.log(` ${metalColor}■\x1b[0m \x1b[1m${m.moduleName}\x1b[0m \x1b[90m(${m.platform})\x1b[0m`);
165
- });
166
- } else {
167
- console.log(`\n \x1b[90mAucun module trouvé dans l'infrastructure de ce projet.\x1b[0m`);
168
- }
169
- console.log('');
170
- rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
171
- rl.prompt();
172
- return; // On arrête là, pas besoin d'appeler l'IA pour lister !
173
- }
174
154
 
175
155
  // Sert uniquement à définir le TON du contexte envoyé à Alex
176
156
  const getContextIntent = (text) => {
@@ -191,10 +171,10 @@ const getContextIntent = (text) => {
191
171
 
192
172
  const getModuleCache = async ({ projectId, moduleName }) => {
193
173
  try {
194
- if (!moduleName) return { found: false };
195
174
  const res = await axios.post(CACHE_URL, { projectId, moduleName });
196
175
  if (res.data && res.data.found) {
197
- return { found: true, module: res.data.module };
176
+ // On renvoie à la fois "module" (pour un seul) et "modules" (pour la liste)
177
+ return { found: true, module: res.data.module, modules: res.data.modules };
198
178
  }
199
179
  return { found: false };
200
180
  } catch (e) {
@@ -239,11 +219,29 @@ if (command === 'alex') {
239
219
  console.log('\x1b[90mAlex prefers concise instructions. Please summarize.\x1b[0m');
240
220
  return;
241
221
  }
242
- //process.stdout.write('\x1b[33m🧠 Alex is thinking...\x1b[0m');
222
+
223
+ // 📋 INTERCEPTION : LISTER LES MODULES (Maintenant dans un bloc Async sécurisé !)
224
+ const isListingRequest = /^(liste|list|quels modules|montre les modules)/i.test(prompt);
225
+ if (isListingRequest) {
226
+ process.stdout.write(` \x1b[90m🔍 Scanning Kernel Cache...\x1b[0m\n`);
227
+ const cacheRes = await getModuleCache({ projectId, moduleName: null });
228
+
229
+ if (cacheRes.found !== false && cacheRes.modules && cacheRes.modules.length > 0) {
230
+ console.log(`\n\x1b[36m⚡ FLEETBO KERNEL MODULES (${cacheRes.modules.length}):\x1b[0m`);
231
+ cacheRes.modules.forEach(m => {
232
+ const metalColor = m.platform === 'android' ? '\x1b[32m' : '\x1b[34m';
233
+ console.log(` ${metalColor}■\x1b[0m \x1b[1m${m.moduleName}\x1b[0m \x1b[90m(${m.platform})\x1b[0m`);
234
+ });
235
+ } else {
236
+ console.log(`\n \x1b[90mAucun module trouvé dans l'infrastructure de ce projet.\x1b[0m`);
237
+ }
238
+ return; // 🛑 On arrête l'exécution ici, on ne gaspille pas le quota d'Alex
239
+ }
240
+
243
241
  console.log('\x1b[33m🧠 Alex is thinking...\x1b[0m');
244
242
 
245
243
  try {
246
- // --- DÉBUT MODIFICATION : SYSTÈME DE MÉMOIRE (SMART CACHE SCANNER) ---
244
+ // --- SYSTÈME DE MÉMOIRE (SMART CACHE SCANNER) ---
247
245
  let contextInjection = "";
248
246
  const potentialModules = extractPotentialModules(prompt);
249
247
  let referenceFound = false;
@@ -260,7 +258,6 @@ if (command === 'alex') {
260
258
  ? "MÉTAL EXISTANT (À MODIFIER)"
261
259
  : "MÉTAL DE RÉFÉRENCE (POUR PARITÉ DES DONNÉES)";
262
260
 
263
- // ⚠️ CHANGEMENT MAJEUR : ON N'ENVOIE QUE LE CODE NATIF
264
261
  contextInjection = `
265
262
 
266
263
  --- CONTEXTE SOUVERAIN : ${contextTitle} (${modName}) ---
@@ -280,7 +277,7 @@ if (command === 'alex') {
280
277
  process.stdout.write(` \x1b[31mNOT FOUND\x1b[0m\n`);
281
278
  }
282
279
  }
283
- // --- FIN MODIFICATION ---
280
+ // --- FIN MODIFICATION MÉMOIRE ---
284
281
 
285
282
  const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
286
283
  headers: { 'x-project-id': projectId }
@@ -292,7 +289,6 @@ if (command === 'alex') {
292
289
  try { aiData = JSON.parse(aiData); } catch (_) {}
293
290
  }
294
291
 
295
- //process.stdout.write('\r' + ' '.repeat(50) + '\r');
296
292
  process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
297
293
 
298
294
  if (aiData.status === 'quota_exceeded') {
@@ -304,11 +300,7 @@ if (command === 'alex') {
304
300
  console.log('');
305
301
  let rawMsg = aiData.message || "I'm ready.";
306
302
 
307
- // 🛡️ FIX DÉFINITIF: Gemini imbrique parfois le JSON complet dans "message"
308
- // Le JSON imbriqué peut être tronqué (4000 tokens), donc JSON.parse échoue.
309
- // On extrait le vrai message par découpe de string.
310
303
  if (typeof rawMsg === 'string' && rawMsg.trimStart().startsWith('{')) {
311
- // Tentative 1: Parse complet (JSON non tronqué)
312
304
  try {
313
305
  const nested = JSON.parse(rawMsg);
314
306
  if (nested && nested.message) {
@@ -318,7 +310,6 @@ if (command === 'alex') {
318
310
  }
319
311
  }
320
312
  } catch (_) {
321
- // Tentative 2: Extraction par regex (JSON tronqué)
322
313
  const msgMatch = rawMsg.match(/"message"\s*:\s*"((?:[^"\\]|\\.)*)"/);
323
314
  if (msgMatch && msgMatch[1]) {
324
315
  rawMsg = msgMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"');
@@ -328,18 +319,15 @@ if (command === 'alex') {
328
319
  }
329
320
  }
330
321
 
331
- // 🛡️ SAFETY: Si le message est un JSON stringifié ou contient du code brut,
332
- // on extrait uniquement la partie lisible pour le terminal.
333
322
  if (typeof rawMsg === 'object') {
334
323
  rawMsg = rawMsg.message || rawMsg.text || "Module generated.";
335
324
  }
336
- // Détection d'un JSON brut accidentellement injecté dans le message
337
325
  try {
338
326
  const testParse = JSON.parse(rawMsg);
339
327
  if (testParse && typeof testParse === 'object' && testParse.status) {
340
328
  rawMsg = testParse.message || "Module generated.";
341
329
  }
342
- } catch (_) { /* Not JSON, continue normally */ }
330
+ } catch (_) { }
343
331
 
344
332
  const formattedMsg = wrapText(rawMsg, 85);
345
333
  console.log('\x1b[32mAlex ❯\x1b[0m ' + formattedMsg);
@@ -393,22 +381,29 @@ if (command === 'alex') {
393
381
  }
394
382
  }
395
383
 
396
- if (config_offload && (config_offload.dependencies?.length > 0 || config_offload.permissions?.length > 0)) {
397
- process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Syncing ${config_offload.dependencies?.length || 0} libs to OS...`);
398
- try {
399
- await axios.post(INJECT_DEPS_URL, {
400
- projectId: projectId,
401
- fileData: {
402
- path: fileName,
403
- config_offload: config_offload
404
- }
405
- });
406
- process.stdout.write(` \x1b[32mOK\x1b[0m\n`);
407
- } catch (err) {
408
- process.stdout.write(` \x1b[31mFAILED\x1b[0m\n`);
409
- console.error(` ⚠️ Config sync failed: ${err.message}`);
410
- }
384
+ // --- CORRECTION : SYNCHRONISATION DU KERNEL (Cache + Dépendances INCONDITIONNELLE) ---
385
+ const depsCount = config_offload?.dependencies?.length || 0;
386
+ process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Archiving ${moduleName} to Kernel (${depsCount} libs)...`);
387
+
388
+ try {
389
+ await axios.post(INJECT_DEPS_URL, {
390
+ projectId: projectId,
391
+ fileData: {
392
+ path: fileName,
393
+ moduleName: moduleName, // INDISPENSABLE POUR LE CACHE
394
+ fileName: fileName,
395
+ code: code, // LE MÉTAL
396
+ mockFileName: mockFileName,
397
+ mockCode: mockCode, // LE MOCK
398
+ config_offload: config_offload || { dependencies: [], permissions: [] } // Sécurité
399
+ }
400
+ });
401
+ process.stdout.write(` \x1b[32mOK\x1b[0m\n`);
402
+ } catch (err) {
403
+ process.stdout.write(` \x1b[31mFAILED\x1b[0m\n`);
404
+ console.error(` ⚠️ Kernel sync failed: ${err.message}`);
411
405
  }
406
+ // --- FIN CORRECTION ---
412
407
  }
413
408
  } catch (error) {
414
409
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
@@ -461,7 +456,7 @@ if (command === 'alex') {
461
456
  console.log(' \x1b[1m🎬 High-Perf\x1b[0m\x1b[90m Infinite Feeds, Video Players, Swipe Decks\x1b[0m');
462
457
  console.log(' \x1b[1m🏗️ Sovereign\x1b[0m\x1b[90m Full screens: form + photo + save-to-cloud\x1b[0m');
463
458
 
464
- // 3. LA COLLABORATION (Mise à jour V2)
459
+ // 3. LA COLLABORATION
465
460
  console.log('\n\x1b[36m💡 TELL ME "WHAT + WHY":\x1b[0m');
466
461
  console.log('\x1b[90m I will analyze your need and recommend the perfect module to forge.\x1b[0m');
467
462
  console.log('');
@@ -481,37 +476,29 @@ if (command === 'alex') {
481
476
  process.stdout.write('\n\x1b[F');
482
477
  rl.prompt();
483
478
 
484
- // --- NATURAL MULTI-LINE BUFFER (ZERO GLITCH V2) ---
485
479
  let inputBuffer = "";
486
480
  let isProcessing = false;
487
481
 
488
482
  rl.on('line', async (line) => {
489
- if (isProcessing) return; // Verrou pendant la forge
483
+ if (isProcessing) return;
490
484
 
491
485
  const trimmedLine = line.trim();
492
486
 
493
- // 1. COMMANDES DE SORTIE
494
487
  if (['exit', 'quit'].includes(trimmedLine.toLowerCase())) {
495
488
  console.log('\n\x1b[90m Alex session closed.\x1b[0m');
496
489
  rl.close();
497
490
  return;
498
491
  }
499
492
 
500
- // 2. ACCUMULATION (Si la ligne n'est pas vide)
501
493
  if (trimmedLine !== "") {
502
494
  inputBuffer += (inputBuffer ? "\n" : "") + line;
503
-
504
- // 🛡️ LE SECRET EST LÀ : On vide le préfixe pour les lignes suivantes.
505
- // Quand tu colles, NodeJS arrêtera de répéter "jojo ❯" partout !
506
495
  rl.setPrompt("");
507
496
  }
508
- // 3. VALIDATION (Touche Entrée sur une ligne vide)
509
497
  else {
510
498
  if (inputBuffer.trim() !== "") {
511
499
  const finalPrompt = inputBuffer.trim();
512
500
  inputBuffer = "";
513
501
 
514
- // 🛑 COUPE-CIRCUIT STRICT
515
502
  if (finalPrompt.length > 1000) {
516
503
  console.log(`\n\x1b[31m⛔ [Alex Safety] Mission rejetée : Taille excessive (${finalPrompt.length}/1000 caractères).\x1b[0m`);
517
504
  console.log(`\x1b[90m Veuillez être plus concis et vous concentrer sur l'essentiel.\x1b[0m\n`);
@@ -520,18 +507,17 @@ if (command === 'alex') {
520
507
  return;
521
508
  }
522
509
 
523
- // ✅ ON LANCE LA FORGE
524
510
  isProcessing = true;
525
- rl.setPrompt(""); // On masque le prompt pendant qu'Alex réfléchit
511
+ rl.setPrompt("");
526
512
  await processAlexRequest(finalPrompt);
527
513
  isProcessing = false;
528
514
 
529
515
  console.log('');
530
516
  rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
531
- rl.prompt(); // On remet le prompt "jojo ❯" pour la mission suivante
517
+ rl.prompt();
532
518
  } else {
533
519
  rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
534
- rl.prompt(); // Si appui sur Entrée dans le vide
520
+ rl.prompt();
535
521
  }
536
522
  }
537
523
  });
@@ -540,7 +526,7 @@ if (command === 'alex') {
540
526
  if (!initialPrompt || initialPrompt === '?') startAlexSession();
541
527
  else processAlexRequest(initialPrompt);
542
528
 
543
- }
529
+ }
544
530
 
545
531
  // ============================================
546
532
  // COMMAND: rm (MODULE ANNIHILATION)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbo-cockpit-cli",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "Fleetbo CLI - Build native mobile apps with React",
5
5
  "author": "Fleetbo",
6
6
  "license": "MIT",