fleetbo-cockpit-cli 1.0.218 → 1.0.219
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 +141 -235
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
// \x1b[1A remonte d'une ligne, \x1b[2K efface la ligne. On le fait 4 fois pour écraser le bloc npm.
|
|
3
|
+
// ABSOLUTE SILENCE: Executes in 0.1ms to kill NPM plumbing
|
|
5
4
|
process.stdout.write("\x1b[1A\x1b[2K".repeat(4));
|
|
6
5
|
console.clear();
|
|
7
6
|
|
|
@@ -31,13 +30,13 @@ const args = process.argv.slice(2);
|
|
|
31
30
|
const command = args[0];
|
|
32
31
|
|
|
33
32
|
// ============================================
|
|
34
|
-
// CONFIGURATION (.env
|
|
33
|
+
// CONFIGURATION (.env of dev project)
|
|
35
34
|
// ============================================
|
|
36
35
|
process.env.DOTENV_SILENT = 'true';
|
|
37
36
|
const envPath = path.join(process.cwd(), '.env');
|
|
38
37
|
|
|
39
38
|
if (!fs.existsSync(envPath)) {
|
|
40
|
-
console.error('\x1b[31m%s\x1b[0m', '
|
|
39
|
+
console.error('\x1b[31m%s\x1b[0m', 'Error: Configuration file (.env) not found.');
|
|
41
40
|
console.error('\x1b[90m%s\x1b[0m', 'Make sure you are in a Fleetbo project directory.\n');
|
|
42
41
|
process.exit(1);
|
|
43
42
|
}
|
|
@@ -48,10 +47,9 @@ const projectId = process.env.VITE_FLEETBO_ENTERPRISE_ID;
|
|
|
48
47
|
const keyApp = process.env.VITE_FLEETBO_KEY_APP;
|
|
49
48
|
const testerEmail = process.env.VITE_FLEETBO_TESTER_EMAIL;
|
|
50
49
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// ═══════════════════════════════════════════════════════
|
|
50
|
+
// =======================================================
|
|
51
|
+
// AUTOMATIC JS FRAMEWORK DETECTION (React or Vue)
|
|
52
|
+
// =======================================================
|
|
55
53
|
const detectFramework = () => {
|
|
56
54
|
try {
|
|
57
55
|
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
@@ -67,7 +65,7 @@ const detectFramework = () => {
|
|
|
67
65
|
const JS_FRAMEWORK = detectFramework();
|
|
68
66
|
|
|
69
67
|
if (!projectId) {
|
|
70
|
-
console.error('\
|
|
68
|
+
console.error('\x1b[31m[Error] Project ID missing in .env.\x1b[0m\n');
|
|
71
69
|
process.exit(1);
|
|
72
70
|
}
|
|
73
71
|
|
|
@@ -80,25 +78,24 @@ const checkGitSecurity = () => {
|
|
|
80
78
|
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
81
79
|
if (fs.existsSync(gitDir)) {
|
|
82
80
|
if (!fs.existsSync(gitignorePath)) {
|
|
83
|
-
console.error('\
|
|
81
|
+
console.error('\x1b[31m[SECURITY ALERT]\x1b[0m .git detected but no .gitignore found.');
|
|
84
82
|
process.exit(1);
|
|
85
83
|
}
|
|
86
84
|
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
87
85
|
if (!gitignoreContent.includes('.env')) {
|
|
88
|
-
console.error('\
|
|
86
|
+
console.error('\x1b[31m[CRITICAL RISK]\x1b[0m .env is NOT ignored by Git.');
|
|
89
87
|
process.exit(1);
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
};
|
|
93
91
|
|
|
94
92
|
const injectRouteIntoAppJs = (moduleName, subPath = '') => {
|
|
95
|
-
// 🔍 DÉTERMINATION DU FICHIER CIBLE SELON LE FRAMEWORK
|
|
96
93
|
const targetFile = JS_FRAMEWORK === 'vue'
|
|
97
|
-
? path.join(process.cwd(), 'src', 'router.js')
|
|
98
|
-
: path.join(process.cwd(), 'src', 'App.jsx');
|
|
94
|
+
? path.join(process.cwd(), 'src', 'router.js')
|
|
95
|
+
: path.join(process.cwd(), 'src', 'App.jsx');
|
|
99
96
|
|
|
100
97
|
if (!fs.existsSync(targetFile)) {
|
|
101
|
-
console.error(
|
|
98
|
+
console.error(`\x1b[31m[Safety Stop]\x1b[0m ${path.basename(targetFile)} missing.`);
|
|
102
99
|
return false;
|
|
103
100
|
}
|
|
104
101
|
|
|
@@ -109,12 +106,11 @@ const injectRouteIntoAppJs = (moduleName, subPath = '') => {
|
|
|
109
106
|
: '{/* FLEETBO_DYNAMIC ROUTES */}';
|
|
110
107
|
|
|
111
108
|
if (!content.includes(importAnchor) || !content.includes(routeAnchor)) {
|
|
112
|
-
console.log(
|
|
109
|
+
console.log(`\x1b[33m[Skipped]\x1b[0m Anchors missing in ${path.basename(targetFile)}. Manual injection required.`);
|
|
113
110
|
return false;
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
const cleanSubPath = subPath ? `${subPath}/` : '';
|
|
117
|
-
// 🟢 IMPORT UNIFIÉ — Vite résout l'extension automatiquement (React & Vue)
|
|
118
114
|
const importLine = `import ${moduleName} from './app/${cleanSubPath}${moduleName}';`;
|
|
119
115
|
let routeLine;
|
|
120
116
|
if (JS_FRAMEWORK === 'vue') {
|
|
@@ -125,7 +121,6 @@ const injectRouteIntoAppJs = (moduleName, subPath = '') => {
|
|
|
125
121
|
|
|
126
122
|
let modified = false;
|
|
127
123
|
|
|
128
|
-
// 1. Injection de l'import (Capture l'indentation d'origine)
|
|
129
124
|
if (!content.includes(importLine)) {
|
|
130
125
|
const importMatch = content.match(new RegExp(`(\\n[ \\t]*)${importAnchor.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}`));
|
|
131
126
|
const importIndent = importMatch ? importMatch[1] : '\n';
|
|
@@ -133,7 +128,6 @@ const injectRouteIntoAppJs = (moduleName, subPath = '') => {
|
|
|
133
128
|
modified = true;
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
// 2. Injection de la route (Capture l'indentation d'origine)
|
|
137
131
|
if (!content.includes(routeLine)) {
|
|
138
132
|
const routeMatch = content.match(new RegExp(`(\\n[ \\t]*)${routeAnchor.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}`));
|
|
139
133
|
const routeIndent = routeMatch ? routeMatch[1] : '\n';
|
|
@@ -153,45 +147,31 @@ const showEnergyTransfer = async () => {
|
|
|
153
147
|
for (let i = 0; i <= width; i++) {
|
|
154
148
|
const dots = "█".repeat(i);
|
|
155
149
|
const empty = "░".repeat(width - i);
|
|
156
|
-
process.stdout.write(`\r
|
|
150
|
+
process.stdout.write(`\r\x1b[32m[Propulsion Sync]\x1b[0m [${dots}${empty}] ${Math.round((i / width) * 100)}%`);
|
|
157
151
|
await new Promise(r => setTimeout(r, 45));
|
|
158
152
|
}
|
|
159
153
|
process.stdout.write('\n');
|
|
160
154
|
};
|
|
161
155
|
|
|
162
|
-
// Détecte TOUS les mots en PascalCase (ex: GuestCreator, CameraModule, Tab2)
|
|
163
156
|
const extractPotentialModules = (text) => {
|
|
164
|
-
// Regex stricte : Majuscule + minuscules + Majuscule (ex: ProfileCreator, UserConfig)
|
|
165
157
|
const regex = /\b[A-Z][a-z]+[A-Z][a-zA-Z0-9_]*\b/g;
|
|
166
158
|
const matches = text.match(regex) || [];
|
|
167
159
|
return [...new Set(matches)];
|
|
168
160
|
};
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
// Sert uniquement à définir le TON du contexte envoyé à Alex
|
|
172
162
|
const getContextIntent = (text) => {
|
|
173
163
|
const modifierKeywords = [
|
|
174
|
-
// 🇫🇷 FRANÇAIS (Verbes d'action purs)
|
|
175
164
|
'modifier', 'modifie', 'corrige', 'ajoute', 'change', 'met a jour', 'mets à jour',
|
|
176
165
|
'optimise', 'améliore', 'refactorise', 'répare', 'adapte', 'remplace',
|
|
177
|
-
|
|
178
|
-
// 🇬🇧 ANGLAIS (Action verbs)
|
|
179
166
|
'update', 'fix', 'edit', 'add', 'modify', 'upgrade', 'optimize',
|
|
180
167
|
'improve', 'refactor', 'repair', 'adapt', 'replace', 'tweak',
|
|
181
|
-
|
|
182
|
-
// 🇪🇸 ESPAGNOL (Verbos de acción)
|
|
183
168
|
'modifica', 'edita', 'actualiza', 'añade', 'agrega', 'mejora',
|
|
184
169
|
'optimiza', 'refactoriza', 'repara', 'adapta', 'reemplaza'
|
|
185
170
|
];
|
|
186
171
|
|
|
187
172
|
const inspireKeywords = [
|
|
188
|
-
// 🇫🇷 FRANÇAIS
|
|
189
173
|
'inspire', 'base', 'comme', 'modèle', 'reference', 'reprends', 'copie', 'imite', 'basé sur',
|
|
190
|
-
|
|
191
|
-
// 🇬🇧 ANGLAIS
|
|
192
174
|
'based on', 'model', 'like', 'copy', 'similar', 'imitate', 'replicate',
|
|
193
|
-
|
|
194
|
-
// 🇪🇸 ESPAGNOL
|
|
195
175
|
'inspira', 'basado', 'como', 'modelo', 'referencia', 'copia', 'imita', 'básate'
|
|
196
176
|
];
|
|
197
177
|
|
|
@@ -205,7 +185,6 @@ const getModuleCache = async ({ projectId, moduleName }) => {
|
|
|
205
185
|
try {
|
|
206
186
|
const res = await axios.post(CACHE_URL, { projectId, moduleName });
|
|
207
187
|
if (res.data && res.data.found) {
|
|
208
|
-
// On renvoie à la fois "module" (pour un seul) et "modules" (pour la liste)
|
|
209
188
|
return { found: true, module: res.data.module, modules: res.data.modules };
|
|
210
189
|
}
|
|
211
190
|
return { found: false };
|
|
@@ -231,7 +210,6 @@ const removeRouteFromAppJs = (moduleName) => {
|
|
|
231
210
|
|
|
232
211
|
const originalContent = content;
|
|
233
212
|
|
|
234
|
-
// 🟢 REGEX INTELLIGENTE — supprime l'import peu importe l'extension (.vue, .jsx, ou aucune)
|
|
235
213
|
const importRegex = new RegExp(
|
|
236
214
|
`import\\s+${moduleName}\\s+from\\s+['"]\\./app/mocks/${moduleName}(?:\\.jsx|\\.vue)?['"];?\\n?`,
|
|
237
215
|
'g'
|
|
@@ -243,13 +221,12 @@ const removeRouteFromAppJs = (moduleName) => {
|
|
|
243
221
|
|
|
244
222
|
if (content !== originalContent) {
|
|
245
223
|
fs.writeFileSync(targetFile, content);
|
|
246
|
-
console.log(
|
|
224
|
+
console.log(`\x1b[32m[Unrouted]\x1b[0m ${moduleName} removed from ${path.basename(targetFile)}.`);
|
|
247
225
|
return true;
|
|
248
226
|
}
|
|
249
227
|
return false;
|
|
250
228
|
};
|
|
251
229
|
|
|
252
|
-
|
|
253
230
|
// ============================================
|
|
254
231
|
// COMMAND: alex
|
|
255
232
|
// ============================================
|
|
@@ -259,41 +236,36 @@ if (command === 'alex') {
|
|
|
259
236
|
|
|
260
237
|
const processAlexRequest = async (prompt) => {
|
|
261
238
|
if (prompt.length > 4000) {
|
|
262
|
-
console.log('\
|
|
239
|
+
console.log('\x1b[31m[Alex Safety] Request too long (' + prompt.length + '/4000 chars).\x1b[0m');
|
|
263
240
|
return;
|
|
264
241
|
}
|
|
265
242
|
|
|
266
243
|
/* =====================================================================
|
|
267
|
-
|
|
244
|
+
STRICT PATCH MANUAL COMMIT INTERCEPTION
|
|
268
245
|
===================================================================== */
|
|
269
246
|
|
|
270
|
-
// 1. Strict command detection (4 words required)
|
|
271
247
|
const patchMatch = prompt.match(/^patch\s+module\s+([a-zA-Z0-9_]+)\s+(android|ios)/i);
|
|
272
|
-
|
|
273
|
-
// 2. Capture human errors (missing platform)
|
|
274
248
|
const partialPatchMatch = prompt.match(/^patch\s+module\s+([a-zA-Z0-9_]+)$/i);
|
|
275
249
|
|
|
276
250
|
if (partialPatchMatch && !patchMatch) {
|
|
277
|
-
console.log(`\
|
|
278
|
-
console.log(
|
|
279
|
-
console.log(
|
|
251
|
+
console.log(`\x1b[31m[REJECTED]\x1b[0m Ambiguous command. Platform is mandatory to avoid Metal corruption.`);
|
|
252
|
+
console.log(`Type: patch module ${partialPatchMatch[1]} android`);
|
|
253
|
+
console.log(`Or: patch module ${partialPatchMatch[1]} ios`);
|
|
280
254
|
return;
|
|
281
255
|
}
|
|
282
256
|
|
|
283
257
|
if (patchMatch) {
|
|
284
258
|
const modName = patchMatch[1];
|
|
285
|
-
const targetPlatform = patchMatch[2].toLowerCase();
|
|
259
|
+
const targetPlatform = patchMatch[2].toLowerCase();
|
|
286
260
|
|
|
287
|
-
console.log(`\
|
|
261
|
+
console.log(`\x1b[33m[Integrity Check] Verifying ${modName} (${targetPlatform.toUpperCase()})...\x1b[0m`);
|
|
288
262
|
|
|
289
|
-
// Cloud Verification
|
|
290
263
|
const cacheRes = await getModuleCache({ projectId, moduleName: modName });
|
|
291
264
|
if (!cacheRes.found) {
|
|
292
|
-
console.log(`\x1b[31m
|
|
265
|
+
console.log(`\x1b[31m[REJECTED]\x1b[0m Module "${modName}" does not exist in the Cloud. The first draft must ALWAYS be forged by Alex.`);
|
|
293
266
|
return;
|
|
294
267
|
}
|
|
295
268
|
|
|
296
|
-
// Strict and absolute paths
|
|
297
269
|
const nativeExt = targetPlatform === 'android' ? 'kt' : 'swift';
|
|
298
270
|
const nativeFileName = `${modName}.${nativeExt}`;
|
|
299
271
|
const nativePathAPI = `public/native/${targetPlatform}/${nativeFileName}`;
|
|
@@ -302,17 +274,15 @@ if (command === 'alex') {
|
|
|
302
274
|
const mockExt = JS_FRAMEWORK === 'vue' ? 'vue' : 'jsx';
|
|
303
275
|
const mockPath = path.join(process.cwd(), 'src', 'app', 'mocks', `${modName}.${mockExt}`);
|
|
304
276
|
|
|
305
|
-
// Physical existence validation
|
|
306
277
|
if (!fs.existsSync(nativePathFull)) {
|
|
307
|
-
console.log(`\x1b[31m
|
|
278
|
+
console.log(`\x1b[31m[FATAL ERROR]\x1b[0m Target file not found on disk (${nativePathAPI}).`);
|
|
308
279
|
return;
|
|
309
280
|
}
|
|
310
281
|
|
|
311
282
|
const localNativeCode = fs.readFileSync(nativePathFull, 'utf8');
|
|
312
283
|
const localMockCode = fs.existsSync(mockPath) ? fs.readFileSync(mockPath, 'utf8') : null;
|
|
313
284
|
|
|
314
|
-
|
|
315
|
-
process.stdout.write(` \x1b[33m[Patch Sync]\x1b[0m Pushing ${targetPlatform.toUpperCase()} modifications to Cloud OS...`);
|
|
285
|
+
process.stdout.write(`\x1b[33m[Patch Sync]\x1b[0m Pushing ${targetPlatform.toUpperCase()} modifications to Cloud OS... `);
|
|
316
286
|
try {
|
|
317
287
|
await axios.post(INJECT_DEPS_URL, {
|
|
318
288
|
projectId: projectId,
|
|
@@ -326,38 +296,37 @@ if (command === 'alex') {
|
|
|
326
296
|
isPatch: true
|
|
327
297
|
}
|
|
328
298
|
});
|
|
329
|
-
process.stdout.write(
|
|
330
|
-
console.log(`\x1b[32m
|
|
299
|
+
process.stdout.write(`\x1b[32m[OK]\x1b[0m\n`);
|
|
300
|
+
console.log(`\x1b[32m[SUCCESS]\x1b[0m Local version of ${nativeFileName} is now the official reference in the Cloud.`);
|
|
331
301
|
} catch (err) {
|
|
332
|
-
process.stdout.write(
|
|
333
|
-
console.error(
|
|
302
|
+
process.stdout.write(`\x1b[31m[FAILED]\x1b[0m\n`);
|
|
303
|
+
console.error(`\x1b[31m[Error] OS sync failed: ${err.message}\x1b[0m`);
|
|
334
304
|
}
|
|
335
|
-
return;
|
|
305
|
+
return;
|
|
336
306
|
}
|
|
337
|
-
/* ===================================================================== */
|
|
338
307
|
|
|
339
|
-
//
|
|
308
|
+
// INTERCEPTION: LIST MODULES
|
|
340
309
|
const isListingRequest = /^(liste|list|listar|lista|quels modules|montre les modules|affiche les modules|show modules|what modules|display modules|qu[eé] m[oó]dulos|muestra los m[oó]dulos|ver m[oó]dulos)/i.test(prompt);
|
|
341
310
|
if (isListingRequest) {
|
|
342
|
-
process.stdout.write(
|
|
311
|
+
process.stdout.write(`\x1b[90m[Scan] Checking OS Cache...\x1b[0m\n`);
|
|
343
312
|
const cacheRes = await getModuleCache({ projectId, moduleName: null });
|
|
344
313
|
|
|
345
314
|
if (cacheRes.found !== false && cacheRes.modules && cacheRes.modules.length > 0) {
|
|
346
|
-
console.log(`\
|
|
315
|
+
console.log(`\x1b[36m[Fleetbo OS Modules] (${cacheRes.modules.length}):\x1b[0m`);
|
|
347
316
|
cacheRes.modules.forEach(m => {
|
|
348
317
|
const metalColor = m.platform === 'android' ? '\x1b[32m' : '\x1b[34m';
|
|
349
|
-
console.log(`
|
|
318
|
+
console.log(`[${m.platform}] \x1b[1m${m.moduleName}\x1b[0m`);
|
|
350
319
|
});
|
|
351
320
|
} else {
|
|
352
|
-
console.log(`\
|
|
321
|
+
console.log(`\x1b[90mNo modules found in the infrastructure of this project.\x1b[0m`);
|
|
353
322
|
}
|
|
354
323
|
return;
|
|
355
324
|
}
|
|
356
325
|
|
|
357
|
-
console.log('\x1b[33m
|
|
326
|
+
console.log('\x1b[33m[Alex] Processing request...\x1b[0m');
|
|
358
327
|
|
|
359
328
|
try {
|
|
360
|
-
// --- MEMORY SYSTEM (SMART CACHE SCANNER V3
|
|
329
|
+
// --- MEMORY SYSTEM (SMART CACHE SCANNER V3) ---
|
|
361
330
|
let contextInjection = "";
|
|
362
331
|
const potentialModules = extractPotentialModules(prompt);
|
|
363
332
|
|
|
@@ -367,7 +336,6 @@ if (command === 'alex') {
|
|
|
367
336
|
for (let modName of potentialModules) {
|
|
368
337
|
let isReferenceOnly = false;
|
|
369
338
|
|
|
370
|
-
// LE DÉTECTEUR D'INSPIRATION
|
|
371
339
|
if (modName.endsWith('Ref')) {
|
|
372
340
|
isReferenceOnly = true;
|
|
373
341
|
modName = modName.replace('Ref', '');
|
|
@@ -376,67 +344,59 @@ if (command === 'alex') {
|
|
|
376
344
|
modName = modName.replace('Schema', '');
|
|
377
345
|
}
|
|
378
346
|
|
|
379
|
-
process.stdout.write(
|
|
347
|
+
process.stdout.write(`\x1b[90mChecking Alex memory for ${modName} module...\x1b[0m `);
|
|
380
348
|
const cache = await getModuleCache({ projectId, moduleName: modName });
|
|
381
349
|
|
|
382
|
-
// LECTURE LOCALE DU VRAI CODE KOTLIN (La Vérité Absolue)
|
|
383
350
|
const localKtPath = path.join(process.cwd(), 'public', 'native', 'android', `${modName}.kt`);
|
|
384
351
|
let actualMetalCode = "";
|
|
385
352
|
|
|
386
353
|
if (fs.existsSync(localKtPath)) {
|
|
387
354
|
actualMetalCode = fs.readFileSync(localKtPath, 'utf8');
|
|
388
355
|
} else if (cache.found && cache.module.code) {
|
|
389
|
-
actualMetalCode = cache.module.code;
|
|
356
|
+
actualMetalCode = cache.module.code;
|
|
390
357
|
}
|
|
391
358
|
|
|
392
359
|
if (actualMetalCode) {
|
|
393
|
-
process.stdout.write(
|
|
360
|
+
process.stdout.write(`\x1b[32m[FOUND METAL]\x1b[0m\n`);
|
|
394
361
|
|
|
395
|
-
// Extraction des schémas mémoires (uniquement dispo dans le cache cloud)
|
|
396
362
|
let memoryScript = "";
|
|
397
|
-
if (cache.found && cache.module.dataSchema) memoryScript += `\n[
|
|
398
|
-
if (cache.found && cache.module.uiSchema) memoryScript += `\n[
|
|
363
|
+
if (cache.found && cache.module.dataSchema) memoryScript += `\n[DATA MEMORY SCRIPT]\n${cache.module.dataSchema}\n`;
|
|
364
|
+
if (cache.found && cache.module.uiSchema) memoryScript += `\n[NATIVE UI MEMORY SCRIPT]\n${cache.module.uiSchema}\n`;
|
|
399
365
|
|
|
400
|
-
if (!memoryScript) memoryScript = `\n[
|
|
366
|
+
if (!memoryScript) memoryScript = `\n[MODULE MEMORY SCRIPT ${modName}]\nNo schema registered for this module.\n`;
|
|
401
367
|
|
|
402
|
-
// 🤖 PRÉ-ANALYSE DÉTERMINISTE DU CLI (Le bouclier anti-amnésie d'Alex)
|
|
403
368
|
const isTabModule = actualMetalCode.includes('action == "tab"');
|
|
404
369
|
const levelWarning = isTabModule
|
|
405
|
-
? "\n
|
|
370
|
+
? "\n[CRITICAL SYSTEM ALERT]: This native code contains 'action == \"tab\"'. It is DEFINITIVELY a LEVEL 5 (Fleetbo View / Sovereign Tab). You are STRICTLY FORBIDDEN to propose Fleetbo.exec() for this module. You MUST use Fleetbo.openView('ModuleName', true) in a useEffect() to integrate it, otherwise it will destroy the application.\n"
|
|
406
371
|
: "";
|
|
407
372
|
|
|
408
373
|
if (isReferenceOnly) {
|
|
409
|
-
|
|
410
|
-
referenceContexts += `\n--- CONTEXTE : MODULE DE RÉFÉRENCE (${modName}) ---\nDOGME : Ne modifie pas ce module. Aligne-toi sur ses Scripts Mémoires et son Code Natif.\n${memoryScript}\n[CODE NATIF DE RÉFÉRENCE]\n${actualMetalCode}\n`;
|
|
374
|
+
referenceContexts += `\n--- CONTEXT: REFERENCE MODULE (${modName}) ---\nDOGMA: Do not modify this module. Align with its Memory Scripts and Native Code.\n${memoryScript}\n[REFERENCE NATIVE CODE]\n${actualMetalCode}\n`;
|
|
411
375
|
} else if (!targetModuleContext) {
|
|
412
|
-
// 🚨 CAS B : CIBLE PRINCIPALE
|
|
413
376
|
const intent = getContextIntent(prompt);
|
|
414
377
|
if (intent === "MODIFICATION") {
|
|
415
|
-
targetModuleContext = `\n---
|
|
378
|
+
targetModuleContext = `\n--- CONTEXT: EXISTING NATIVE CODE TO MODIFY (${modName}) ---\nDOGMA: You must modify this Native code. Then, you will forge a new JSX Mock based SOLELY on your new Native code.${levelWarning}\n${memoryScript}\n[EXISTING NATIVE CODE]\n${actualMetalCode}\n--- END OF CONTEXT ---\n`;
|
|
416
379
|
} else {
|
|
417
|
-
targetModuleContext = `\n---
|
|
380
|
+
targetModuleContext = `\n--- CONTEXT: READ ONLY (${modName}) ---\nFor information, here is the current state of the module. Do not modify it, use it to accurately answer the Pilot.${levelWarning}\n${memoryScript}\n[NATIVE CODE]\n${actualMetalCode}\n`;
|
|
418
381
|
}
|
|
419
382
|
}
|
|
420
383
|
} else {
|
|
421
|
-
process.stdout.write(
|
|
384
|
+
process.stdout.write(`\x1b[31m[NOT FOUND]\x1b[0m\n`);
|
|
422
385
|
}
|
|
423
386
|
}
|
|
424
387
|
|
|
425
388
|
contextInjection = referenceContexts + targetModuleContext;
|
|
426
389
|
if (contextInjection) {
|
|
427
|
-
prompt = contextInjection + "\n\n[INSTRUCTION
|
|
390
|
+
prompt = contextInjection + "\n\n[PILOT INSTRUCTION]\n" + prompt;
|
|
428
391
|
}
|
|
429
|
-
// --- END MEMORY MODIFICATION ---
|
|
430
392
|
|
|
431
|
-
// Real-time timestamp injection
|
|
432
393
|
const now = new Date();
|
|
433
|
-
// Clean format: YYYY-MM-DD at HH:MM:SS
|
|
434
394
|
const exactTime = now.toISOString().split('T')[0] + ' at ' + now.toTimeString().split(' ')[0];
|
|
435
395
|
|
|
436
|
-
const promptWithTime = prompt + `\n\n[SYSTEM INFO: The exact current timestamp is ${exactTime}. Use it STRICTLY for your signature '//
|
|
396
|
+
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.]`;
|
|
437
397
|
|
|
438
398
|
// =================================================================
|
|
439
|
-
//
|
|
399
|
+
// THE AUTOMATED 3-STEP WORKFLOW (Cross-Platform)
|
|
440
400
|
// =================================================================
|
|
441
401
|
|
|
442
402
|
const deepUnwrap = (raw, depth = 0) => {
|
|
@@ -476,46 +436,41 @@ if (command === 'alex') {
|
|
|
476
436
|
return deepUnwrap(res.data);
|
|
477
437
|
};
|
|
478
438
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
// \x1b[2K efface la ligne courante, \r ramène au début
|
|
482
|
-
process.stdout.write(`\x1b[2K\r ${icon} ${text}`);
|
|
439
|
+
const logStep = (prefix, text) => {
|
|
440
|
+
process.stdout.write(`\x1b[2K\r${prefix} ${text}`);
|
|
483
441
|
};
|
|
484
442
|
|
|
485
443
|
process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
|
|
486
444
|
|
|
487
|
-
// ---
|
|
488
|
-
logStep('
|
|
445
|
+
// --- STEP 1: NATIVE ---
|
|
446
|
+
logStep('[1/3]', '\x1b[33mAlex is forging native foundations...\x1b[0m');
|
|
489
447
|
let aiData = await fetchStep('native_only');
|
|
490
448
|
|
|
491
449
|
if (aiData.status === 'quota_exceeded') {
|
|
492
|
-
console.log(`\n\x1b[31m
|
|
450
|
+
console.log(`\n\x1b[31m[Architect Quota Reached]:\x1b[0m ${aiData.message}`);
|
|
493
451
|
return;
|
|
494
452
|
}
|
|
495
453
|
|
|
496
454
|
let mergedModuleData = aiData.moduleData || {};
|
|
497
455
|
let aiDataMessage = aiData.message;
|
|
498
456
|
|
|
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
457
|
const isCloudUpdated = !mergedModuleData.mockCode;
|
|
503
458
|
|
|
504
459
|
if (isCloudUpdated && mergedModuleData.code) {
|
|
505
|
-
logStep('
|
|
460
|
+
logStep('[1/3]', '\x1b[32mNative code generated.\x1b[0m\n');
|
|
506
461
|
|
|
507
|
-
// ---
|
|
508
|
-
logStep('
|
|
462
|
+
// --- STEP 2: MOCK ---
|
|
463
|
+
logStep('[2/3]', '\x1b[33mAnalyzing native code and generating Interface (Mock)...\x1b[0m');
|
|
509
464
|
const aiData2 = await fetchStep('mock_only', { nativeCode: mergedModuleData.code });
|
|
510
465
|
|
|
511
466
|
if (aiData2.moduleData) {
|
|
512
467
|
mergedModuleData.mockCode = aiData2.moduleData.mockCode;
|
|
513
468
|
mergedModuleData.mockFileName = aiData2.moduleData.mockFileName;
|
|
514
469
|
}
|
|
515
|
-
logStep('
|
|
470
|
+
logStep('[2/3]', '\x1b[32mInterface Mock generated.\x1b[0m\n');
|
|
516
471
|
|
|
517
|
-
// ---
|
|
518
|
-
logStep('
|
|
472
|
+
// --- STEP 3: JSON CONFIG ---
|
|
473
|
+
logStep('[3/3]', '\x1b[33mFinalizing JSON configuration...\x1b[0m');
|
|
519
474
|
const aiData3 = await fetchStep('json_only', {
|
|
520
475
|
nativeCode: mergedModuleData.code,
|
|
521
476
|
mockCode: mergedModuleData.mockCode
|
|
@@ -526,18 +481,15 @@ if (command === 'alex') {
|
|
|
526
481
|
mergedModuleData.dataSchema = aiData3.moduleData.dataSchema;
|
|
527
482
|
mergedModuleData.uiSchema = aiData3.moduleData.uiSchema;
|
|
528
483
|
}
|
|
529
|
-
logStep('
|
|
484
|
+
logStep('[3/3]', '\x1b[32mConfiguration completed.\x1b[0m\n');
|
|
530
485
|
} else {
|
|
531
|
-
// Le cloud n'est pas à jour, on efface le loader de l'étape 1 en silence
|
|
532
486
|
process.stdout.write(`\x1b[2K\r`);
|
|
533
487
|
}
|
|
534
488
|
|
|
535
|
-
// DISPLAY REASONING IN TERMINAL
|
|
536
489
|
if (aiData.thinking_process) {
|
|
537
|
-
console.log(
|
|
490
|
+
console.log(`\x1b[90m[Analysis] ${aiData.thinking_process.substring(0, 150)}...\x1b[0m`);
|
|
538
491
|
}
|
|
539
492
|
|
|
540
|
-
// --- OUTPUT VERS XTERM ---
|
|
541
493
|
if (aiData.status === 'success' || aiData.status === 'message' || aiData.status === 'complex_refusal') {
|
|
542
494
|
let rawMsg = aiDataMessage || "I'm ready.";
|
|
543
495
|
if (typeof rawMsg === 'object') {
|
|
@@ -555,7 +507,7 @@ if (command === 'alex') {
|
|
|
555
507
|
if (moduleName.length > 40) moduleName = moduleName.substring(0, 40) + "...";
|
|
556
508
|
}
|
|
557
509
|
|
|
558
|
-
console.log(`\
|
|
510
|
+
console.log(`\x1b[90mArchitecting: ${moduleName || 'Unknown Module'}\x1b[0m`);
|
|
559
511
|
|
|
560
512
|
const writeFile = (dir, name, content) => {
|
|
561
513
|
const fullPath = path.join(process.cwd(), dir);
|
|
@@ -578,7 +530,7 @@ if (command === 'alex') {
|
|
|
578
530
|
|
|
579
531
|
// --- KERNEL SYNCHRONIZATION ---
|
|
580
532
|
const depsCount = config_offload?.dependencies?.length || 0;
|
|
581
|
-
process.stdout.write(
|
|
533
|
+
process.stdout.write(`\x1b[33m[OS Sync]\x1b[0m Archiving ${moduleName} to OS (${depsCount} libs)... `);
|
|
582
534
|
|
|
583
535
|
try {
|
|
584
536
|
await axios.post(INJECT_DEPS_URL, {
|
|
@@ -595,33 +547,32 @@ if (command === 'alex') {
|
|
|
595
547
|
uiSchema: uiSchema || null
|
|
596
548
|
}
|
|
597
549
|
});
|
|
598
|
-
process.stdout.write(
|
|
550
|
+
process.stdout.write(`\x1b[32m[OK]\x1b[0m\n`);
|
|
599
551
|
} catch (err) {
|
|
600
|
-
process.stdout.write(
|
|
601
|
-
console.error(
|
|
552
|
+
process.stdout.write(`\x1b[31m[FAILED]\x1b[0m\n`);
|
|
553
|
+
console.error(`\x1b[31m[Error] OS sync failed: ${err.message}\x1b[0m`);
|
|
602
554
|
}
|
|
603
555
|
|
|
604
|
-
console.log('');
|
|
605
|
-
console.log(`\x1b[
|
|
606
|
-
console.log(
|
|
607
|
-
console.log(`\x1b[
|
|
608
|
-
console.log(`\x1b[31m npm run fleetbo rm\x1b[0m \x1b[36m${moduleName}\x1b[0m`);
|
|
556
|
+
console.log('\x1b[90mIf you manually edit the native code, sync it to the Cloud OS:\x1b[0m');
|
|
557
|
+
console.log(`\x1b[33mpatch module\x1b[0m \x1b[36m${moduleName}\x1b[0m \x1b[33mandroid\x1b[0m \x1b[90m(or ios)\x1b[0m`);
|
|
558
|
+
console.log('\x1b[90mTo eradicate this module later, open a new terminal and type:\x1b[0m');
|
|
559
|
+
console.log(`\x1b[31mnpm run fleetbo rm\x1b[0m \x1b[36m${moduleName}\x1b[0m`);
|
|
609
560
|
|
|
610
561
|
} else if (aiData.status === 'success' && (!mergedModuleData || Object.keys(mergedModuleData).length === 0)) {
|
|
611
|
-
console.log(
|
|
562
|
+
console.log('\x1b[31m[Error] Alex replied, but source code could not be extracted.\x1b[0m\n');
|
|
612
563
|
}
|
|
613
564
|
} catch (error) {
|
|
614
565
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
615
|
-
console.error('\
|
|
566
|
+
console.error('\x1b[31m[Alex Error]\x1b[0m ' + (error.response?.data?.message || error.message));
|
|
616
567
|
}
|
|
617
568
|
};
|
|
618
569
|
|
|
619
570
|
// ============================================================
|
|
620
|
-
//
|
|
571
|
+
// START INTERACTIVE SESSION
|
|
621
572
|
// ============================================================
|
|
622
573
|
|
|
623
574
|
const startAlexSession = async () => {
|
|
624
|
-
process.stdout.write('\x1b[33m
|
|
575
|
+
process.stdout.write('\x1b[33m[Alex] Checking runtime state...\x1b[0m\r');
|
|
625
576
|
let attempts = 0;
|
|
626
577
|
const maxAttempts = 5;
|
|
627
578
|
let isReady = false;
|
|
@@ -653,7 +604,7 @@ if (command === 'alex') {
|
|
|
653
604
|
}
|
|
654
605
|
|
|
655
606
|
if (!isReady) {
|
|
656
|
-
console.error('\
|
|
607
|
+
console.error('\x1b[31m[ENGINE OFFLINE]\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
|
|
657
608
|
console.error(`\x1b[90m(Ensure you are running the runtime for project: ${keyApp})\x1b[0m`);
|
|
658
609
|
process.exit(1);
|
|
659
610
|
}
|
|
@@ -661,21 +612,15 @@ if (command === 'alex') {
|
|
|
661
612
|
process.stdout.write(' '.repeat(60) + '\r');
|
|
662
613
|
|
|
663
614
|
if (!hasAiKey) {
|
|
664
|
-
console.log('');
|
|
665
|
-
console.log('\x1b[1m\x1b[33m AI Configuration Required\x1b[0m');
|
|
666
|
-
console.log('');
|
|
615
|
+
console.log('\x1b[1m\x1b[33mAI Configuration Required\x1b[0m\n');
|
|
667
616
|
console.log('Alex needs a powerful AI model to forge native code.');
|
|
668
|
-
console.log('Connect your own AI key to continue
|
|
669
|
-
console.log('');
|
|
617
|
+
console.log('Connect your own AI key to continue.\n');
|
|
670
618
|
console.log('\x1b[1mSupported providers:\x1b[0m');
|
|
671
|
-
console.log('\x1b[32m
|
|
672
|
-
console.log('\x1b[36mhttps://aistudio.google.com/app/apikey\x1b[0m');
|
|
673
|
-
console.log('');
|
|
674
|
-
console.log('\x1b[
|
|
675
|
-
console.log('\x1b[
|
|
676
|
-
console.log('');
|
|
677
|
-
console.log('\x1b[90m Click the \x1b[32m[+]\x1b[90m button in the Terminal top-right bar\x1b[0m');
|
|
678
|
-
console.log('');
|
|
619
|
+
console.log('\x1b[32m[+]\x1b[0m Google AI \x1b[90m(Gemini Pro)\x1b[0m');
|
|
620
|
+
console.log('\x1b[36mhttps://aistudio.google.com/app/apikey\x1b[0m\n');
|
|
621
|
+
console.log('\x1b[33m[+]\x1b[0m Anthropic \x1b[90m(Claude Sonnet / Opus)\x1b[0m');
|
|
622
|
+
console.log('\x1b[36mhttps://console.anthropic.com/settings/keys\x1b[0m\n');
|
|
623
|
+
console.log('\x1b[90mClick the \x1b[32m[+]\x1b[90m button in the Terminal top-right bar\x1b[0m\n');
|
|
679
624
|
process.exit(0);
|
|
680
625
|
}
|
|
681
626
|
|
|
@@ -686,32 +631,27 @@ if (command === 'alex') {
|
|
|
686
631
|
|
|
687
632
|
console.log('\x1b[90m┌─────────────────────────────────────────────────────────┐\x1b[0m');
|
|
688
633
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
689
|
-
console.log('\x1b[90m│\x1b[0m \x1b[1m\x1b[36m
|
|
634
|
+
console.log('\x1b[90m│\x1b[0m \x1b[1m\x1b[36m[ALEX]\x1b[0m \x1b[90m— System Architect · Fleetbo OS\x1b[0m \x1b[90m│\x1b[0m');
|
|
690
635
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
691
636
|
console.log('\x1b[90m│\x1b[0m \x1b[90mYour JS stays the brain. I forge the metal.\x1b[0m \x1b[90m│\x1b[0m');
|
|
692
637
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
693
638
|
console.log('\x1b[90m└─────────────────────────────────────────────────────────┘\x1b[0m');
|
|
694
639
|
|
|
695
|
-
console.log(
|
|
696
|
-
console.log(`\x1b[32m
|
|
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`);
|
|
640
|
+
console.log(`\x1b[32m[OK]\x1b[0m ${providerLabel} \x1b[90m· ${modelLabel}\x1b[0m`);
|
|
641
|
+
console.log(`\x1b[32m[OK]\x1b[0m ${JS_FRAMEWORK === 'vue' ? '\x1b[32mVue.js\x1b[0m' : '\x1b[36mReact\x1b[0m'} \x1b[90m· JavaScript Framework\x1b[0m\n`);
|
|
698
642
|
|
|
699
|
-
console.log('');
|
|
700
643
|
console.log('\x1b[36mCAPABILITIES\x1b[0m');
|
|
701
644
|
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
|
|
702
645
|
console.log('\x1b[1mHardware\x1b[0m\x1b[90m Camera · Scanner · GPS · Biometrics\x1b[0m');
|
|
703
646
|
console.log('\x1b[1mHigh-Perf\x1b[0m\x1b[90m Video Feed · Swipe Deck · Audio\x1b[0m');
|
|
704
647
|
console.log('\x1b[1mSovereign\x1b[0m\x1b[90m Form + Photo + Save-to-Cloud\x1b[0m');
|
|
705
648
|
console.log('\x1b[1mFleetbo View\x1b[0m\x1b[90m Full native tab (120 FPS)\x1b[0m');
|
|
706
|
-
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
|
|
649
|
+
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m\n');
|
|
707
650
|
|
|
708
|
-
console.log('');
|
|
709
651
|
console.log('\x1b[36mCODE GENERATION SYNTAX\x1b[0m \x1b[90m(strict format required)\x1b[0m');
|
|
710
|
-
console.log('');
|
|
711
652
|
console.log('\x1b[90mFormat: \x1b[33m<verb>\x1b[0m module \x1b[36m<ModuleName>\x1b[0m');
|
|
712
653
|
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');
|
|
713
|
-
console.log('\x1b[90mExample: \x1b[0m\x1b[33mupdate\x1b[0m module \x1b[36mProfileManager\x1b[0m \x1b[90mwith a red button\x1b[0m');
|
|
714
|
-
console.log('');
|
|
654
|
+
console.log('\x1b[90mExample: \x1b[0m\x1b[33mupdate\x1b[0m module \x1b[36mProfileManager\x1b[0m \x1b[90mwith a red button\x1b[0m\n');
|
|
715
655
|
|
|
716
656
|
console.log('\x1b[32mAlex ❯\x1b[0m Describe your feature using the Compose panel...');
|
|
717
657
|
|
|
@@ -721,7 +661,7 @@ if (command === 'alex') {
|
|
|
721
661
|
});
|
|
722
662
|
|
|
723
663
|
rl.on('SIGINT', () => {
|
|
724
|
-
console.log('\
|
|
664
|
+
console.log('\x1b[90mAlex session aborted (Ctrl+C).\x1b[0m');
|
|
725
665
|
process.stdout.write('[ALEX_OFFLINE]\n');
|
|
726
666
|
rl.close();
|
|
727
667
|
process.exit(0);
|
|
@@ -732,7 +672,7 @@ if (command === 'alex') {
|
|
|
732
672
|
|
|
733
673
|
const executePrompt = async (text) => {
|
|
734
674
|
if (['exit', 'quit'].includes(text.toLowerCase())) {
|
|
735
|
-
console.log('\
|
|
675
|
+
console.log('\x1b[90mAlex session closed.\x1b[0m');
|
|
736
676
|
process.stdout.write('[ALEX_OFFLINE]\n');
|
|
737
677
|
rl.close();
|
|
738
678
|
return;
|
|
@@ -740,9 +680,7 @@ if (command === 'alex') {
|
|
|
740
680
|
|
|
741
681
|
if (text !== "") {
|
|
742
682
|
isProcessing = true;
|
|
743
|
-
|
|
744
683
|
await processAlexRequest(text);
|
|
745
|
-
|
|
746
684
|
isProcessing = false;
|
|
747
685
|
console.log('');
|
|
748
686
|
}
|
|
@@ -781,16 +719,14 @@ if (command === 'alex') {
|
|
|
781
719
|
else if (command === 'rm') {
|
|
782
720
|
const moduleName = args[1];
|
|
783
721
|
if (!moduleName) {
|
|
784
|
-
console.error('\
|
|
722
|
+
console.error('\x1b[31m[Error] Module name required.\x1b[0m');
|
|
785
723
|
console.log('\x1b[90mUsage: npm run fleetbo rm [ModuleName]\x1b[0m\n');
|
|
786
724
|
process.exit(1);
|
|
787
725
|
}
|
|
788
726
|
|
|
789
|
-
console.log(`\
|
|
727
|
+
console.log(`\x1b[33m[Annihilation] Removing module: ${moduleName}...\x1b[0m`);
|
|
790
728
|
|
|
791
|
-
// 1. Define physical paths
|
|
792
729
|
const ktPath = path.join(process.cwd(), 'public', 'native', 'android', `${moduleName}.kt`);
|
|
793
|
-
// AGNOSTIQUE — cherche .jsx (React) puis .vue (Vue)
|
|
794
730
|
const jsxPath = path.join(process.cwd(), 'src', 'app', 'mocks', `${moduleName}.jsx`);
|
|
795
731
|
const vuePath = path.join(process.cwd(), 'src', 'app', 'mocks', `${moduleName}.vue`);
|
|
796
732
|
const mockPath = fs.existsSync(jsxPath) ? jsxPath : fs.existsSync(vuePath) ? vuePath : null;
|
|
@@ -798,28 +734,25 @@ else if (command === 'rm') {
|
|
|
798
734
|
|
|
799
735
|
let actionsDone = 0;
|
|
800
736
|
|
|
801
|
-
// 2. Eradicate Metal Engine (Kotlin)
|
|
802
737
|
if (fs.existsSync(ktPath)) {
|
|
803
738
|
fs.unlinkSync(ktPath);
|
|
804
|
-
console.log(
|
|
739
|
+
console.log(`\x1b[32m[Deleted]\x1b[0m Metal file (.kt) eradicated.`);
|
|
805
740
|
actionsDone++;
|
|
806
741
|
}
|
|
807
742
|
|
|
808
|
-
// 3. Eradicate Virtual Twin (Mock JSX or Vue)
|
|
809
743
|
if (mockPath) {
|
|
810
744
|
fs.unlinkSync(mockPath);
|
|
811
|
-
console.log(
|
|
745
|
+
console.log(`\x1b[32m[Deleted]\x1b[0m Virtual Twin (${mockExt}) eradicated.`);
|
|
812
746
|
actionsDone++;
|
|
813
747
|
}
|
|
814
748
|
|
|
815
|
-
// 4. Disinfect System Core (App.jsx)
|
|
816
749
|
const unrouted = removeRouteFromAppJs(moduleName);
|
|
817
750
|
if (unrouted) actionsDone++;
|
|
818
751
|
|
|
819
752
|
if (actionsDone === 0) {
|
|
820
|
-
console.log(`\
|
|
753
|
+
console.log(`\x1b[31m[Warning] No trace of module "${moduleName}" found in the OS.\x1b[0m\n`);
|
|
821
754
|
} else {
|
|
822
|
-
console.log(`\
|
|
755
|
+
console.log(`\x1b[32mModule ${moduleName} successfully eradicated from the OS.\x1b[0m\n`);
|
|
823
756
|
}
|
|
824
757
|
}
|
|
825
758
|
|
|
@@ -828,21 +761,17 @@ else if (command === 'rm') {
|
|
|
828
761
|
// ============================================
|
|
829
762
|
else if (command === 'android' || command === 'ios') {
|
|
830
763
|
|
|
831
|
-
// DÉBUT DE LA PROTECTION (Fonction Async Immédiate)
|
|
832
|
-
// Cela garantit que le code fonctionne partout, même via 'require()'
|
|
833
764
|
(async () => {
|
|
834
765
|
|
|
835
|
-
// 🛑 INTERCEPTION IOS : BLOQUAGE NET (MAINTENANCE/BETA)
|
|
836
766
|
if (command === 'ios') {
|
|
837
|
-
console.log(`\
|
|
767
|
+
console.log(`\x1b[36m[FLEETBO IOS PROPULSION]\x1b[0m`);
|
|
838
768
|
console.log(`\x1b[33m[0/3] Initializing Neural Uplink...\x1b[0m`);
|
|
839
769
|
|
|
840
|
-
// Ce 'await' est maintenant sécurisé
|
|
841
770
|
await new Promise(r => setTimeout(r, 800));
|
|
842
771
|
|
|
843
|
-
console.log(`\
|
|
844
|
-
console.log(`\x1b[
|
|
845
|
-
console.log(`\x1b[
|
|
772
|
+
console.log(`\x1b[31m[PROPULSION ABORTED] iOS Frequency Restricted.\x1b[0m`);
|
|
773
|
+
console.log(`\x1b[90mThis module is currently reserved for Vanguard Pilots (Closed Beta).\x1b[0m`);
|
|
774
|
+
console.log(`\x1b[90mPlease engage propulsion on Android frequency for now.\x1b[0m\n`);
|
|
846
775
|
|
|
847
776
|
process.exit(1);
|
|
848
777
|
}
|
|
@@ -853,7 +782,6 @@ else if (command === 'android' || command === 'ios') {
|
|
|
853
782
|
const extension = platform === 'android' ? '.kt' : '.swift';
|
|
854
783
|
const nativePath = path.join(process.cwd(), nativeDir);
|
|
855
784
|
|
|
856
|
-
// Vérification des modules natifs
|
|
857
785
|
let hasNativeFiles = false;
|
|
858
786
|
let nativeFileCount = 0;
|
|
859
787
|
if (fs.existsSync(nativePath)) {
|
|
@@ -864,7 +792,7 @@ else if (command === 'android' || command === 'ios') {
|
|
|
864
792
|
}
|
|
865
793
|
|
|
866
794
|
if (!hasNativeFiles) {
|
|
867
|
-
console.log(`\
|
|
795
|
+
console.log(`\x1b[31m[ENGINE INCOMPLETE]\x1b[0m No native blueprints detected for \x1b[1m${platform.toUpperCase()}\x1b[0m.`);
|
|
868
796
|
console.log(`\x1b[90mAlex must architect at least one ${extension} module before deployment.\x1b[0m`);
|
|
869
797
|
console.log(`\x1b[90mRun: npm run fleetbo alex\x1b[0m\n`);
|
|
870
798
|
process.exit(1);
|
|
@@ -872,23 +800,18 @@ else if (command === 'android' || command === 'ios') {
|
|
|
872
800
|
|
|
873
801
|
const targetUrl = platform === 'android' ? ANDROID_BUILD_URL : IOS_BUILD_URL;
|
|
874
802
|
|
|
875
|
-
console.log(`\
|
|
876
|
-
console.log(`\x1b[90m
|
|
803
|
+
console.log(`\x1b[36m[FLEETBO ${platform.toUpperCase()} PROPULSION]\x1b[0m`);
|
|
804
|
+
console.log(`\x1b[90m${nativeFileCount} fleetbo module(s) detected\x1b[0m\n`);
|
|
877
805
|
|
|
878
806
|
try {
|
|
879
|
-
// ==========================================================
|
|
880
|
-
// PRE-FLIGHT CHECK QUOTAS & TIER
|
|
881
|
-
// ==========================================================
|
|
882
807
|
process.stdout.write(`\x1b[33m[0/3]\x1b[0m Checking Propulsion Access... `);
|
|
883
808
|
try {
|
|
884
|
-
// On envoie le signal 'x-preflight' pour tester les droits sans builder
|
|
885
809
|
await axios.post(targetUrl, {}, {
|
|
886
810
|
headers: {
|
|
887
811
|
'x-project-id': projectId,
|
|
888
812
|
'x-preflight': 'true'
|
|
889
813
|
}
|
|
890
814
|
});
|
|
891
|
-
// Si ça passe (200 OK), c'est un Senior ou un Junior autorisé (si vous changez d'avis)
|
|
892
815
|
process.stdout.write(`\x1b[32mOK (Senior Pilot)\x1b[0m\n\n`);
|
|
893
816
|
|
|
894
817
|
} catch (preflightError) {
|
|
@@ -896,27 +819,21 @@ else if (command === 'android' || command === 'ios') {
|
|
|
896
819
|
|
|
897
820
|
const errData = preflightError.response?.data;
|
|
898
821
|
|
|
899
|
-
// 🛑 1. INTERCEPTION SPÉCIFIQUE JUNIOR
|
|
900
|
-
// C'est ici qu'on lit le code renvoyé par index.js
|
|
901
822
|
if (errData?.code === 'junior_restriction') {
|
|
902
|
-
console.log(`\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
903
|
-
console.log(`\x1b[31m⛔ ACCESS DENIED: JUNIOR PILOT DETECTED\x1b[0m`);
|
|
904
823
|
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
905
|
-
console.log(
|
|
906
|
-
console.log(
|
|
907
|
-
console.log(
|
|
908
|
-
console.log(
|
|
909
|
-
process.exit(1);
|
|
824
|
+
console.log(`\x1b[31m[ACCESS DENIED] JUNIOR PILOT DETECTED\x1b[0m`);
|
|
825
|
+
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n`);
|
|
826
|
+
console.log(`\x1b[33mThis feature is locked for Junior Pilots.\x1b[0m`);
|
|
827
|
+
console.log(`\x1b[32mUpgrade to Senior on fleetbo.io to unlock Propulsion.\x1b[0m\n`);
|
|
828
|
+
process.exit(1);
|
|
910
829
|
}
|
|
911
830
|
|
|
912
|
-
// 2. Gestion des autres erreurs (Quota Senior dépassé, Serveur HS, etc.)
|
|
913
831
|
if (errData && errData.error) {
|
|
914
832
|
throw new Error(errData.error);
|
|
915
833
|
}
|
|
916
834
|
throw preflightError;
|
|
917
835
|
}
|
|
918
836
|
|
|
919
|
-
// Étape 1: Build React
|
|
920
837
|
console.log(`\x1b[33m[1/3]\x1b[0m Synthesizing Fleetbo Core Logic...`);
|
|
921
838
|
execSync('npm run build', { stdio: 'inherit' });
|
|
922
839
|
|
|
@@ -927,8 +844,7 @@ else if (command === 'android' || command === 'ios') {
|
|
|
927
844
|
throw new Error(`Build directory not found: ${buildDir}`);
|
|
928
845
|
}
|
|
929
846
|
|
|
930
|
-
|
|
931
|
-
console.log(`\n\x1b[33m[2/3]\x1b[0m Packaging bundle + fleetbo modules...`);
|
|
847
|
+
console.log(`\x1b[33m[2/3]\x1b[0m Packaging bundle + fleetbo modules...`);
|
|
932
848
|
|
|
933
849
|
const zipBuffer = await new Promise((resolve, reject) => {
|
|
934
850
|
const chunks = [];
|
|
@@ -949,10 +865,9 @@ else if (command === 'android' || command === 'ios') {
|
|
|
949
865
|
});
|
|
950
866
|
|
|
951
867
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
952
|
-
console.log(
|
|
868
|
+
console.log(`\x1b[32m[Ready]\x1b[0m Bundle ready: ${sizeMB} MB`);
|
|
953
869
|
|
|
954
|
-
|
|
955
|
-
console.log(`\n\x1b[33m[3/3]\x1b[0m Uploading to Fleetbo OS...`);
|
|
870
|
+
console.log(`\x1b[33m[3/3]\x1b[0m Uploading to Fleetbo OS...`);
|
|
956
871
|
await showEnergyTransfer();
|
|
957
872
|
|
|
958
873
|
let uploadResponse;
|
|
@@ -975,28 +890,28 @@ else if (command === 'android' || command === 'ios') {
|
|
|
975
890
|
}
|
|
976
891
|
|
|
977
892
|
if (uploadResponse.data && uploadResponse.data.success) {
|
|
978
|
-
console.log(`\n\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
979
|
-
console.log(`\x1b[32m✓ ${platform.toUpperCase()} PROPULSION SUCCESSFUL\x1b[0m`);
|
|
980
893
|
console.log(`\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
981
|
-
console.log(`\x1b[
|
|
982
|
-
console.log(`\x1b[
|
|
894
|
+
console.log(`\x1b[32m[SUCCESS] ${platform.toUpperCase()} PROPULSION SUCCESSFUL\x1b[0m`);
|
|
895
|
+
console.log(`\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
896
|
+
console.log(`\x1b[90mDeployment ID: ${uploadResponse.data.deploymentId || 'N/A'}\x1b[0m`);
|
|
897
|
+
console.log(`\x1b[90m${uploadResponse.data.message || 'Complete.'}\x1b[0m\n`);
|
|
983
898
|
} else {
|
|
984
899
|
throw new Error(uploadResponse.data?.error || 'Unknown logical error from Factory');
|
|
985
900
|
}
|
|
986
901
|
|
|
987
902
|
} catch (error) {
|
|
988
|
-
console.log(`\
|
|
989
|
-
console.log(`\x1b[31m
|
|
903
|
+
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
904
|
+
console.log(`\x1b[31m[Propulsion Failed]\x1b[0m`);
|
|
990
905
|
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
991
906
|
|
|
992
|
-
console.error(`\x1b[31m
|
|
907
|
+
console.error(`\x1b[31m[Error]\x1b[0m ${error.message}`);
|
|
993
908
|
|
|
994
909
|
if (error.message.includes('Limit') || error.message.includes('Quota')) {
|
|
995
|
-
console.log(`\
|
|
910
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Upgrade to Senior Pilot for more builds.`);
|
|
996
911
|
} else if (error.message.includes('No native module')) {
|
|
997
|
-
console.log(`\
|
|
912
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Run "npm run fleetbo alex" to create native modules first.`);
|
|
998
913
|
} else if (error.message.includes('Trial Period Ended')) {
|
|
999
|
-
console.log(`\
|
|
914
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Your free sprint is over. Upgrade to Senior Pilot on fleetbo.io.`);
|
|
1000
915
|
}
|
|
1001
916
|
console.log('');
|
|
1002
917
|
process.exit(1);
|
|
@@ -1012,7 +927,7 @@ else if (['page', 'g', 'generate'].includes(command)) {
|
|
|
1012
927
|
try {
|
|
1013
928
|
require(pageGeneratorPath);
|
|
1014
929
|
} catch (e) {
|
|
1015
|
-
console.error('\x1b[31m
|
|
930
|
+
console.error('\x1b[31m[Page Generator Error]\x1b[0m', e.message);
|
|
1016
931
|
process.exit(1);
|
|
1017
932
|
}
|
|
1018
933
|
}
|
|
@@ -1053,10 +968,10 @@ else {
|
|
|
1053
968
|
async function cleanupAndExit(code = 0) {
|
|
1054
969
|
if (isExiting) return;
|
|
1055
970
|
isExiting = true;
|
|
1056
|
-
console.log('\
|
|
971
|
+
console.log('\x1b[33m[Fleetbo] Stopping environment & Cleaning Uplink...\x1b[0m');
|
|
1057
972
|
try {
|
|
1058
973
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail });
|
|
1059
|
-
console.log('\x1b[32m[Fleetbo]
|
|
974
|
+
console.log('\x1b[32m[Fleetbo] Network status reset to offline.\x1b[0m');
|
|
1060
975
|
} catch (e) {
|
|
1061
976
|
console.error('[Fleetbo] Network cleanup warning:', e.message);
|
|
1062
977
|
}
|
|
@@ -1066,7 +981,6 @@ else {
|
|
|
1066
981
|
process.exit(code);
|
|
1067
982
|
}
|
|
1068
983
|
|
|
1069
|
-
// 🪟 WINDOWS FIX : capture Ctrl+C via readline pour éviter l'invite "O/N"
|
|
1070
984
|
if (process.platform === 'win32') {
|
|
1071
985
|
const rlWin = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
1072
986
|
rlWin.on('SIGINT', () => cleanupAndExit(0));
|
|
@@ -1078,31 +992,30 @@ else {
|
|
|
1078
992
|
async function syncFirebase(keyApp, networkUrl, testerEmail) {
|
|
1079
993
|
try {
|
|
1080
994
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
1081
|
-
console.log('\
|
|
1082
|
-
console.log(`\
|
|
995
|
+
console.log('\x1b[32mEngine started successfully\x1b[0m');
|
|
996
|
+
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
1083
997
|
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m \x1b[1mProject [${keyApp}] running in OS\x1b[0m`);
|
|
1084
998
|
console.log('\x1b[32mFleetbo OS ❯\x1b[0m You can now start coding and previewing.');
|
|
1085
999
|
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
1086
|
-
console.log(`\
|
|
1000
|
+
console.log(`\x1b[34mPilot Instruction ❯\x1b[0m Return to the Workspace. The Engine is ready for your orders.\n`);
|
|
1087
1001
|
} catch (err) {
|
|
1088
1002
|
console.error(`\x1b[31mFleetbo OS ❯\x1b[0m Sync Error: ${err.message}`);
|
|
1089
1003
|
}
|
|
1090
1004
|
}
|
|
1091
1005
|
|
|
1092
1006
|
async function runDevEnvironment() {
|
|
1093
|
-
console.log(`[Fleetbo]
|
|
1007
|
+
console.log(`[Fleetbo] Initializing Universal Dev Environment...`);
|
|
1094
1008
|
|
|
1095
1009
|
killNetworkService();
|
|
1096
|
-
killProcessOnPort(PORT);
|
|
1010
|
+
killProcessOnPort(PORT);
|
|
1097
1011
|
|
|
1098
1012
|
if (!testerEmail) {
|
|
1099
|
-
console.error('\x1b[
|
|
1013
|
+
console.error('\x1b[31m[Error] FLEETBO_APP_TESTER_EMAIL missing in .env\x1b[0m');
|
|
1100
1014
|
process.exit(1);
|
|
1101
1015
|
}
|
|
1102
1016
|
|
|
1103
1017
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
1104
1018
|
|
|
1105
|
-
// Lancement agnostique du serveur de dev (React, Vue, etc.)
|
|
1106
1019
|
const devServer = spawn(`${npmCmd} run dev --silent -- --host 127.0.0.1 --port ${PORT}`, {
|
|
1107
1020
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1108
1021
|
shell: true,
|
|
@@ -1115,12 +1028,11 @@ else {
|
|
|
1115
1028
|
devServer.stderr.pipe(process.stderr);
|
|
1116
1029
|
|
|
1117
1030
|
let connectionStarted = false;
|
|
1118
|
-
let detectedPort = PORT;
|
|
1031
|
+
let detectedPort = PORT;
|
|
1119
1032
|
|
|
1120
1033
|
devServer.stdout.on('data', (data) => {
|
|
1121
1034
|
const output = data.toString();
|
|
1122
1035
|
|
|
1123
|
-
// FILTRE ANTI-PLOMBERIE FLEETBO
|
|
1124
1036
|
const lines = output.split('\n');
|
|
1125
1037
|
const forbiddenTerms = [
|
|
1126
1038
|
'Attempting to bind to HOST', 'If this was unintentional', 'Learn more here:',
|
|
@@ -1137,22 +1049,17 @@ else {
|
|
|
1137
1049
|
process.stdout.write(filteredOutput + '\n');
|
|
1138
1050
|
}
|
|
1139
1051
|
|
|
1140
|
-
// MAGIE 1 : DÉTECTION DU PORT RÉEL (Vite/Vue ou React)
|
|
1141
|
-
// Cherche un pattern comme http://localhost:5173 ou http://127.0.0.1:3000
|
|
1142
|
-
// LA SOLUTION : On déclenche UNIQUEMENT quand on a trouvé le port !
|
|
1143
|
-
// CAPTURE AGNOSTIQUE DU PORT (S'adapte dynamiquement à Vite)
|
|
1144
1052
|
const portMatch = output.match(/http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/);
|
|
1145
1053
|
|
|
1146
1054
|
if (portMatch) {
|
|
1147
|
-
detectedPort = portMatch[1];
|
|
1055
|
+
detectedPort = portMatch[1];
|
|
1148
1056
|
|
|
1149
|
-
// On lance le tunnel Cloudflare UNIQUEMENT sur ce port définitif
|
|
1150
1057
|
if (!connectionStarted) {
|
|
1151
1058
|
connectionStarted = true;
|
|
1152
1059
|
|
|
1153
|
-
console.log('\
|
|
1154
|
-
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m
|
|
1155
|
-
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m
|
|
1060
|
+
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
1061
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m Establishing Secure Uplink...`);
|
|
1062
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m Please wait for the green message...`);
|
|
1156
1063
|
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
1157
1064
|
|
|
1158
1065
|
const MAX_UPLINK_RETRIES = 5;
|
|
@@ -1165,10 +1072,9 @@ else {
|
|
|
1165
1072
|
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
1166
1073
|
|
|
1167
1074
|
if (attempt > 0) {
|
|
1168
|
-
console.log(`\x1b[33m[Fleetbo]
|
|
1075
|
+
console.log(`\x1b[33m[Fleetbo] Uplink reconnection ${attempt}/${MAX_UPLINK_RETRIES - 1}...\x1b[0m`);
|
|
1169
1076
|
}
|
|
1170
1077
|
|
|
1171
|
-
// LE TUNNEL CLOUDFLARE UTILISE LE PORT DÉTECTÉ DYNAMIQUEMENT !
|
|
1172
1078
|
const uplinkCommand = `${npxCmd} -y cloudflared tunnel --url http://127.0.0.1:${detectedPort} --http-host-header 127.0.0.1:${detectedPort}`;
|
|
1173
1079
|
uplinkProcess = spawn(uplinkCommand, { shell: true });
|
|
1174
1080
|
|
|
@@ -1189,7 +1095,7 @@ else {
|
|
|
1189
1095
|
|
|
1190
1096
|
uplinkProcess.on('error', () => {
|
|
1191
1097
|
if (uplinkFound) return;
|
|
1192
|
-
console.error(`\x1b[31m[Fleetbo]
|
|
1098
|
+
console.error(`\x1b[31m[Fleetbo] Uplink Connection failed to establish.\x1b[0m`);
|
|
1193
1099
|
});
|
|
1194
1100
|
|
|
1195
1101
|
uplinkProcess.on('close', () => {
|
|
@@ -1197,10 +1103,10 @@ else {
|
|
|
1197
1103
|
const nextAttempt = attempt + 1;
|
|
1198
1104
|
if (nextAttempt < MAX_UPLINK_RETRIES) {
|
|
1199
1105
|
const delay = RETRY_DELAYS[nextAttempt] || 30;
|
|
1200
|
-
console.log(`\x1b[33m[Fleetbo]
|
|
1106
|
+
console.log(`\x1b[33m[Fleetbo] Uplink interrupted. Fleetbo OS retrying in ${delay}s... (${nextAttempt}/${MAX_UPLINK_RETRIES - 1})\x1b[0m`);
|
|
1201
1107
|
setTimeout(() => startUplink(nextAttempt), delay * 1000);
|
|
1202
1108
|
} else {
|
|
1203
|
-
console.error(`\x1b[31m[Fleetbo]
|
|
1109
|
+
console.error(`\x1b[31m[Fleetbo] Secure Uplink could not be established.\x1b[0m`);
|
|
1204
1110
|
}
|
|
1205
1111
|
});
|
|
1206
1112
|
};
|