fleetbo-cockpit-cli 1.0.44 → 1.0.46
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 +101 -92
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -144,28 +144,49 @@ const showEnergyTransfer = async () => {
|
|
|
144
144
|
process.stdout.write('\n');
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
'erreur', 'error', 'fail', 'plante', 'problème'
|
|
153
|
-
];
|
|
154
|
-
const lower = text.toLowerCase();
|
|
155
|
-
return keywords.some(k => lower.includes(k));
|
|
147
|
+
// Détecte TOUS les mots en PascalCase (ex: GuestCreator, CameraModule, Tab2)
|
|
148
|
+
const extractPotentialModules = (text) => {
|
|
149
|
+
const regex = /\b[A-Z][a-zA-Z0-9_]{2,}\b/g;
|
|
150
|
+
const matches = text.match(regex) || [];
|
|
151
|
+
return [...new Set(matches)]; // Dédoublonne les résultats
|
|
156
152
|
};
|
|
157
153
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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`);
|
|
167
168
|
}
|
|
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
|
+
|
|
175
|
+
// Sert uniquement à définir le TON du contexte envoyé à Alex
|
|
176
|
+
const getContextIntent = (text) => {
|
|
177
|
+
const modifierKeywords = [
|
|
178
|
+
'modifier', 'corrige', 'ajoute', 'erreur', 'plante', 'problème', 'bug', 'change',
|
|
179
|
+
'update', 'fix', 'edit', 'error', 'fail', 'crash', 'issue', 'add'
|
|
180
|
+
];
|
|
181
|
+
const inspireKeywords = [
|
|
182
|
+
'inspire', 'base', 'comme', 'modèle', 'reference', 'reprends', 'copie',
|
|
183
|
+
'inspire', 'based on', 'model', 'reference', 'like', 'copy', 'similar'
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
const lower = text.toLowerCase();
|
|
187
|
+
if (modifierKeywords.some(k => lower.includes(k))) return "MODIFICATION";
|
|
188
|
+
if (inspireKeywords.some(k => lower.includes(k))) return "INSPIRATION";
|
|
189
|
+
return "REFERENCE";
|
|
169
190
|
};
|
|
170
191
|
|
|
171
192
|
const getModuleCache = async ({ projectId, moduleName }) => {
|
|
@@ -218,47 +239,46 @@ if (command === 'alex') {
|
|
|
218
239
|
console.log('\x1b[90mAlex prefers concise instructions. Please summarize.\x1b[0m');
|
|
219
240
|
return;
|
|
220
241
|
}
|
|
221
|
-
//process.stdout.write('\x1b[33m🧠 Alex is thinking...\x1b[0m');
|
|
222
242
|
console.log('\x1b[33m🧠 Alex is thinking...\x1b[0m');
|
|
223
243
|
|
|
224
244
|
try {
|
|
225
|
-
// ---
|
|
245
|
+
// --- SYSTÈME DE MÉMOIRE (SMART CACHE SCANNER) ---
|
|
226
246
|
let contextInjection = "";
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
247
|
+
const potentialModules = extractPotentialModules(prompt);
|
|
248
|
+
let referenceFound = false;
|
|
249
|
+
|
|
250
|
+
for (const modName of potentialModules) {
|
|
251
|
+
process.stdout.write(` \x1b[90m🔍 Checking kernel cache for ${modName}...\x1b[0m`);
|
|
252
|
+
const cache = await getModuleCache({ projectId, moduleName: modName });
|
|
231
253
|
|
|
232
|
-
if (
|
|
233
|
-
process.stdout.write(`
|
|
234
|
-
const cache = await getModuleCache({ projectId, moduleName });
|
|
254
|
+
if (cache.found) {
|
|
255
|
+
process.stdout.write(` \x1b[32mFOUND METAL\x1b[0m\n`);
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
257
|
+
const intent = getContextIntent(prompt);
|
|
258
|
+
const contextTitle = intent === "MODIFICATION"
|
|
259
|
+
? "MÉTAL EXISTANT (À MODIFIER)"
|
|
260
|
+
: "MÉTAL DE RÉFÉRENCE (POUR PARITÉ DES DONNÉES)";
|
|
261
|
+
|
|
262
|
+
contextInjection = `
|
|
263
|
+
|
|
264
|
+
--- CONTEXTE SOUVERAIN : ${contextTitle} (${modName}) ---
|
|
265
|
+
DOGME: Le Métal est la source de vérité absolue. Le Mock n'est qu'une illusion.
|
|
266
|
+
Tu DOIS analyser ce code Natif pour comprendre EXACTEMENT comment les données ont été structurées, nommées (clés JSON) et sauvegardées.
|
|
267
|
+
|
|
268
|
+
[CODE NATIF EXISTANT]
|
|
269
|
+
${cache.module.code}
|
|
270
|
+
|
|
271
|
+
--- FIN DU CONTEXTE ---
|
|
272
|
+
`;
|
|
273
|
+
|
|
274
|
+
prompt = contextInjection + "\n\n[INSTRUCTION DU PILOTE]\n" + prompt;
|
|
275
|
+
referenceFound = true;
|
|
276
|
+
break;
|
|
277
|
+
} else {
|
|
278
|
+
process.stdout.write(` \x1b[31mNOT FOUND\x1b[0m\n`);
|
|
259
279
|
}
|
|
260
280
|
}
|
|
261
|
-
// --- FIN MODIFICATION ---
|
|
281
|
+
// --- FIN MODIFICATION MÉMOIRE ---
|
|
262
282
|
|
|
263
283
|
const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
|
|
264
284
|
headers: { 'x-project-id': projectId }
|
|
@@ -270,7 +290,6 @@ if (command === 'alex') {
|
|
|
270
290
|
try { aiData = JSON.parse(aiData); } catch (_) {}
|
|
271
291
|
}
|
|
272
292
|
|
|
273
|
-
//process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
274
293
|
process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
|
|
275
294
|
|
|
276
295
|
if (aiData.status === 'quota_exceeded') {
|
|
@@ -282,11 +301,7 @@ if (command === 'alex') {
|
|
|
282
301
|
console.log('');
|
|
283
302
|
let rawMsg = aiData.message || "I'm ready.";
|
|
284
303
|
|
|
285
|
-
// 🛡️ FIX DÉFINITIF: Gemini imbrique parfois le JSON complet dans "message"
|
|
286
|
-
// Le JSON imbriqué peut être tronqué (4000 tokens), donc JSON.parse échoue.
|
|
287
|
-
// On extrait le vrai message par découpe de string.
|
|
288
304
|
if (typeof rawMsg === 'string' && rawMsg.trimStart().startsWith('{')) {
|
|
289
|
-
// Tentative 1: Parse complet (JSON non tronqué)
|
|
290
305
|
try {
|
|
291
306
|
const nested = JSON.parse(rawMsg);
|
|
292
307
|
if (nested && nested.message) {
|
|
@@ -296,7 +311,6 @@ if (command === 'alex') {
|
|
|
296
311
|
}
|
|
297
312
|
}
|
|
298
313
|
} catch (_) {
|
|
299
|
-
// Tentative 2: Extraction par regex (JSON tronqué)
|
|
300
314
|
const msgMatch = rawMsg.match(/"message"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
301
315
|
if (msgMatch && msgMatch[1]) {
|
|
302
316
|
rawMsg = msgMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"');
|
|
@@ -306,18 +320,15 @@ if (command === 'alex') {
|
|
|
306
320
|
}
|
|
307
321
|
}
|
|
308
322
|
|
|
309
|
-
// 🛡️ SAFETY: Si le message est un JSON stringifié ou contient du code brut,
|
|
310
|
-
// on extrait uniquement la partie lisible pour le terminal.
|
|
311
323
|
if (typeof rawMsg === 'object') {
|
|
312
324
|
rawMsg = rawMsg.message || rawMsg.text || "Module generated.";
|
|
313
325
|
}
|
|
314
|
-
// Détection d'un JSON brut accidentellement injecté dans le message
|
|
315
326
|
try {
|
|
316
327
|
const testParse = JSON.parse(rawMsg);
|
|
317
328
|
if (testParse && typeof testParse === 'object' && testParse.status) {
|
|
318
329
|
rawMsg = testParse.message || "Module generated.";
|
|
319
330
|
}
|
|
320
|
-
} catch (_) {
|
|
331
|
+
} catch (_) { }
|
|
321
332
|
|
|
322
333
|
const formattedMsg = wrapText(rawMsg, 85);
|
|
323
334
|
console.log('\x1b[32mAlex ❯\x1b[0m ' + formattedMsg);
|
|
@@ -371,22 +382,29 @@ if (command === 'alex') {
|
|
|
371
382
|
}
|
|
372
383
|
}
|
|
373
384
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
385
|
+
// --- CORRECTION : SYNCHRONISATION DU KERNEL (Cache + Dépendances INCONDITIONNELLE) ---
|
|
386
|
+
const depsCount = config_offload?.dependencies?.length || 0;
|
|
387
|
+
process.stdout.write(` \x1b[33m[Cloud Inject]\x1b[0m Archiving ${moduleName} to Kernel (${depsCount} libs)...`);
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
await axios.post(INJECT_DEPS_URL, {
|
|
391
|
+
projectId: projectId,
|
|
392
|
+
fileData: {
|
|
393
|
+
path: fileName,
|
|
394
|
+
moduleName: moduleName, // INDISPENSABLE POUR LE CACHE
|
|
395
|
+
fileName: fileName,
|
|
396
|
+
code: code, // LE MÉTAL
|
|
397
|
+
mockFileName: mockFileName,
|
|
398
|
+
mockCode: mockCode, // LE MOCK
|
|
399
|
+
config_offload: config_offload || { dependencies: [], permissions: [] } // Sécurité
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
process.stdout.write(` \x1b[32mOK\x1b[0m\n`);
|
|
403
|
+
} catch (err) {
|
|
404
|
+
process.stdout.write(` \x1b[31mFAILED\x1b[0m\n`);
|
|
405
|
+
console.error(` ⚠️ Kernel sync failed: ${err.message}`);
|
|
389
406
|
}
|
|
407
|
+
// --- FIN CORRECTION ---
|
|
390
408
|
}
|
|
391
409
|
} catch (error) {
|
|
392
410
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
@@ -439,7 +457,7 @@ if (command === 'alex') {
|
|
|
439
457
|
console.log(' \x1b[1m🎬 High-Perf\x1b[0m\x1b[90m Infinite Feeds, Video Players, Swipe Decks\x1b[0m');
|
|
440
458
|
console.log(' \x1b[1m🏗️ Sovereign\x1b[0m\x1b[90m Full screens: form + photo + save-to-cloud\x1b[0m');
|
|
441
459
|
|
|
442
|
-
// 3. LA COLLABORATION
|
|
460
|
+
// 3. LA COLLABORATION
|
|
443
461
|
console.log('\n\x1b[36m💡 TELL ME "WHAT + WHY":\x1b[0m');
|
|
444
462
|
console.log('\x1b[90m I will analyze your need and recommend the perfect module to forge.\x1b[0m');
|
|
445
463
|
console.log('');
|
|
@@ -459,37 +477,29 @@ if (command === 'alex') {
|
|
|
459
477
|
process.stdout.write('\n\x1b[F');
|
|
460
478
|
rl.prompt();
|
|
461
479
|
|
|
462
|
-
// --- NATURAL MULTI-LINE BUFFER (ZERO GLITCH V2) ---
|
|
463
480
|
let inputBuffer = "";
|
|
464
481
|
let isProcessing = false;
|
|
465
482
|
|
|
466
483
|
rl.on('line', async (line) => {
|
|
467
|
-
if (isProcessing) return;
|
|
484
|
+
if (isProcessing) return;
|
|
468
485
|
|
|
469
486
|
const trimmedLine = line.trim();
|
|
470
487
|
|
|
471
|
-
// 1. COMMANDES DE SORTIE
|
|
472
488
|
if (['exit', 'quit'].includes(trimmedLine.toLowerCase())) {
|
|
473
489
|
console.log('\n\x1b[90m Alex session closed.\x1b[0m');
|
|
474
490
|
rl.close();
|
|
475
491
|
return;
|
|
476
492
|
}
|
|
477
493
|
|
|
478
|
-
// 2. ACCUMULATION (Si la ligne n'est pas vide)
|
|
479
494
|
if (trimmedLine !== "") {
|
|
480
495
|
inputBuffer += (inputBuffer ? "\n" : "") + line;
|
|
481
|
-
|
|
482
|
-
// 🛡️ LE SECRET EST LÀ : On vide le préfixe pour les lignes suivantes.
|
|
483
|
-
// Quand tu colles, NodeJS arrêtera de répéter "jojo ❯" partout !
|
|
484
496
|
rl.setPrompt("");
|
|
485
497
|
}
|
|
486
|
-
// 3. VALIDATION (Touche Entrée sur une ligne vide)
|
|
487
498
|
else {
|
|
488
499
|
if (inputBuffer.trim() !== "") {
|
|
489
500
|
const finalPrompt = inputBuffer.trim();
|
|
490
501
|
inputBuffer = "";
|
|
491
502
|
|
|
492
|
-
// 🛑 COUPE-CIRCUIT STRICT
|
|
493
503
|
if (finalPrompt.length > 1000) {
|
|
494
504
|
console.log(`\n\x1b[31m⛔ [Alex Safety] Mission rejetée : Taille excessive (${finalPrompt.length}/1000 caractères).\x1b[0m`);
|
|
495
505
|
console.log(`\x1b[90m Veuillez être plus concis et vous concentrer sur l'essentiel.\x1b[0m\n`);
|
|
@@ -498,18 +508,17 @@ if (command === 'alex') {
|
|
|
498
508
|
return;
|
|
499
509
|
}
|
|
500
510
|
|
|
501
|
-
// ✅ ON LANCE LA FORGE
|
|
502
511
|
isProcessing = true;
|
|
503
|
-
rl.setPrompt("");
|
|
512
|
+
rl.setPrompt("");
|
|
504
513
|
await processAlexRequest(finalPrompt);
|
|
505
514
|
isProcessing = false;
|
|
506
515
|
|
|
507
516
|
console.log('');
|
|
508
517
|
rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
|
|
509
|
-
rl.prompt();
|
|
518
|
+
rl.prompt();
|
|
510
519
|
} else {
|
|
511
520
|
rl.setPrompt(`\x1b[34m${dynamicUsername} ❯ \x1b[0m`);
|
|
512
|
-
rl.prompt();
|
|
521
|
+
rl.prompt();
|
|
513
522
|
}
|
|
514
523
|
}
|
|
515
524
|
});
|
|
@@ -518,7 +527,7 @@ if (command === 'alex') {
|
|
|
518
527
|
if (!initialPrompt || initialPrompt === '?') startAlexSession();
|
|
519
528
|
else processAlexRequest(initialPrompt);
|
|
520
529
|
|
|
521
|
-
}
|
|
530
|
+
}
|
|
522
531
|
|
|
523
532
|
// ============================================
|
|
524
533
|
// COMMAND: rm (MODULE ANNIHILATION)
|