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.
- package/cli.js +52 -66
- 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
|
-
|
|
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
|
-
|
|
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
|
-
// ---
|
|
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 (_) {
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
|
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;
|
|
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("");
|
|
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();
|
|
517
|
+
rl.prompt();
|
|
532
518
|
} else {
|
|
533
519
|
rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
|
|
534
|
-
rl.prompt();
|
|
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)
|