fleetbo-cockpit-cli 1.0.218 → 1.0.220
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 +151 -236
- 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,36 @@ 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
|
-
|
|
349
|
-
console.log(` ${metalColor}■\x1b[0m \x1b[1m${m.moduleName}\x1b[0m \x1b[90m(${m.platform})\x1b[0m`);
|
|
317
|
+
console.log(`[${m.platform}] \x1b[1m${m.moduleName}\x1b[0m`);
|
|
350
318
|
});
|
|
351
319
|
} else {
|
|
352
|
-
console.log(`\
|
|
320
|
+
console.log(`\x1b[90mNo modules found in the infrastructure of this project.\x1b[0m`);
|
|
353
321
|
}
|
|
354
322
|
return;
|
|
355
323
|
}
|
|
356
324
|
|
|
357
|
-
console.log('\x1b[33m
|
|
325
|
+
console.log('\x1b[33m[Alex] Processing request...\x1b[0m');
|
|
358
326
|
|
|
359
327
|
try {
|
|
360
|
-
// --- MEMORY SYSTEM (SMART CACHE SCANNER V3
|
|
328
|
+
// --- MEMORY SYSTEM (SMART CACHE SCANNER V3) ---
|
|
361
329
|
let contextInjection = "";
|
|
362
330
|
const potentialModules = extractPotentialModules(prompt);
|
|
363
331
|
|
|
@@ -367,7 +335,6 @@ if (command === 'alex') {
|
|
|
367
335
|
for (let modName of potentialModules) {
|
|
368
336
|
let isReferenceOnly = false;
|
|
369
337
|
|
|
370
|
-
// LE DÉTECTEUR D'INSPIRATION
|
|
371
338
|
if (modName.endsWith('Ref')) {
|
|
372
339
|
isReferenceOnly = true;
|
|
373
340
|
modName = modName.replace('Ref', '');
|
|
@@ -376,67 +343,59 @@ if (command === 'alex') {
|
|
|
376
343
|
modName = modName.replace('Schema', '');
|
|
377
344
|
}
|
|
378
345
|
|
|
379
|
-
process.stdout.write(
|
|
346
|
+
process.stdout.write(`\x1b[90mChecking Alex memory for ${modName} module...\x1b[0m `);
|
|
380
347
|
const cache = await getModuleCache({ projectId, moduleName: modName });
|
|
381
348
|
|
|
382
|
-
// LECTURE LOCALE DU VRAI CODE KOTLIN (La Vérité Absolue)
|
|
383
349
|
const localKtPath = path.join(process.cwd(), 'public', 'native', 'android', `${modName}.kt`);
|
|
384
350
|
let actualMetalCode = "";
|
|
385
351
|
|
|
386
352
|
if (fs.existsSync(localKtPath)) {
|
|
387
353
|
actualMetalCode = fs.readFileSync(localKtPath, 'utf8');
|
|
388
354
|
} else if (cache.found && cache.module.code) {
|
|
389
|
-
actualMetalCode = cache.module.code;
|
|
355
|
+
actualMetalCode = cache.module.code;
|
|
390
356
|
}
|
|
391
357
|
|
|
392
358
|
if (actualMetalCode) {
|
|
393
|
-
process.stdout.write(
|
|
359
|
+
process.stdout.write(`\x1b[32m[FOUND METAL]\x1b[0m\n`);
|
|
394
360
|
|
|
395
|
-
// Extraction des schémas mémoires (uniquement dispo dans le cache cloud)
|
|
396
361
|
let memoryScript = "";
|
|
397
|
-
if (cache.found && cache.module.dataSchema) memoryScript += `\n[
|
|
398
|
-
if (cache.found && cache.module.uiSchema) memoryScript += `\n[
|
|
362
|
+
if (cache.found && cache.module.dataSchema) memoryScript += `\n[DATA MEMORY SCRIPT]\n${cache.module.dataSchema}\n`;
|
|
363
|
+
if (cache.found && cache.module.uiSchema) memoryScript += `\n[NATIVE UI MEMORY SCRIPT]\n${cache.module.uiSchema}\n`;
|
|
399
364
|
|
|
400
|
-
if (!memoryScript) memoryScript = `\n[
|
|
365
|
+
if (!memoryScript) memoryScript = `\n[MODULE MEMORY SCRIPT ${modName}]\nNo schema registered for this module.\n`;
|
|
401
366
|
|
|
402
|
-
// 🤖 PRÉ-ANALYSE DÉTERMINISTE DU CLI (Le bouclier anti-amnésie d'Alex)
|
|
403
367
|
const isTabModule = actualMetalCode.includes('action == "tab"');
|
|
404
368
|
const levelWarning = isTabModule
|
|
405
|
-
? "\n
|
|
369
|
+
? "\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
370
|
: "";
|
|
407
371
|
|
|
408
372
|
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`;
|
|
373
|
+
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
374
|
} else if (!targetModuleContext) {
|
|
412
|
-
// 🚨 CAS B : CIBLE PRINCIPALE
|
|
413
375
|
const intent = getContextIntent(prompt);
|
|
414
376
|
if (intent === "MODIFICATION") {
|
|
415
|
-
targetModuleContext = `\n---
|
|
377
|
+
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
378
|
} else {
|
|
417
|
-
targetModuleContext = `\n---
|
|
379
|
+
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
380
|
}
|
|
419
381
|
}
|
|
420
382
|
} else {
|
|
421
|
-
process.stdout.write(
|
|
383
|
+
process.stdout.write(`\x1b[31m[NOT FOUND]\x1b[0m\n`);
|
|
422
384
|
}
|
|
423
385
|
}
|
|
424
386
|
|
|
425
387
|
contextInjection = referenceContexts + targetModuleContext;
|
|
426
388
|
if (contextInjection) {
|
|
427
|
-
prompt = contextInjection + "\n\n[INSTRUCTION
|
|
389
|
+
prompt = contextInjection + "\n\n[PILOT INSTRUCTION]\n" + prompt;
|
|
428
390
|
}
|
|
429
|
-
// --- END MEMORY MODIFICATION ---
|
|
430
391
|
|
|
431
|
-
// Real-time timestamp injection
|
|
432
392
|
const now = new Date();
|
|
433
|
-
// Clean format: YYYY-MM-DD at HH:MM:SS
|
|
434
393
|
const exactTime = now.toISOString().split('T')[0] + ' at ' + now.toTimeString().split(' ')[0];
|
|
435
394
|
|
|
436
|
-
const promptWithTime = prompt + `\n\n[SYSTEM INFO: The exact current timestamp is ${exactTime}. Use it STRICTLY for your signature '//
|
|
395
|
+
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
396
|
|
|
438
397
|
// =================================================================
|
|
439
|
-
//
|
|
398
|
+
// THE AUTOMATED 3-STEP WORKFLOW (Cross-Platform)
|
|
440
399
|
// =================================================================
|
|
441
400
|
|
|
442
401
|
const deepUnwrap = (raw, depth = 0) => {
|
|
@@ -476,46 +435,46 @@ if (command === 'alex') {
|
|
|
476
435
|
return deepUnwrap(res.data);
|
|
477
436
|
};
|
|
478
437
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
// \x1b[2K efface la ligne courante, \r ramène au début
|
|
482
|
-
process.stdout.write(`\x1b[2K\r ${icon} ${text}`);
|
|
438
|
+
const logStep = (prefix, text) => {
|
|
439
|
+
process.stdout.write(`\x1b[2K\r${prefix} ${text}`);
|
|
483
440
|
};
|
|
484
441
|
|
|
485
442
|
process.stdout.write('\x1b[A\r' + ' '.repeat(50) + '\r');
|
|
486
443
|
|
|
487
|
-
|
|
488
|
-
|
|
444
|
+
let totalTokens = 0;
|
|
445
|
+
|
|
446
|
+
// --- STEP 1: NATIVE ---
|
|
447
|
+
logStep('[1/3]', '\x1b[33mAlex is forging native foundations...\x1b[0m');
|
|
489
448
|
let aiData = await fetchStep('native_only');
|
|
490
449
|
|
|
491
450
|
if (aiData.status === 'quota_exceeded') {
|
|
492
|
-
console.log(`\n\x1b[31m
|
|
451
|
+
console.log(`\n\x1b[31m[Architect Quota Reached]:\x1b[0m ${aiData.message}`);
|
|
493
452
|
return;
|
|
494
453
|
}
|
|
495
454
|
|
|
455
|
+
totalTokens += aiData.tokensUsed || 0;
|
|
456
|
+
|
|
496
457
|
let mergedModuleData = aiData.moduleData || {};
|
|
497
458
|
let aiDataMessage = aiData.message;
|
|
498
459
|
|
|
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
460
|
const isCloudUpdated = !mergedModuleData.mockCode;
|
|
503
461
|
|
|
504
462
|
if (isCloudUpdated && mergedModuleData.code) {
|
|
505
|
-
logStep('
|
|
463
|
+
logStep('[1/3]', '\x1b[32mNative code generated.\x1b[0m\n');
|
|
506
464
|
|
|
507
|
-
// ---
|
|
508
|
-
logStep('
|
|
465
|
+
// --- STEP 2: MOCK ---
|
|
466
|
+
logStep('[2/3]', '\x1b[33mAnalyzing native code and generating Interface (Mock)...\x1b[0m');
|
|
509
467
|
const aiData2 = await fetchStep('mock_only', { nativeCode: mergedModuleData.code });
|
|
510
468
|
|
|
511
469
|
if (aiData2.moduleData) {
|
|
512
470
|
mergedModuleData.mockCode = aiData2.moduleData.mockCode;
|
|
513
471
|
mergedModuleData.mockFileName = aiData2.moduleData.mockFileName;
|
|
514
472
|
}
|
|
515
|
-
|
|
473
|
+
totalTokens += aiData2.tokensUsed || 0;
|
|
474
|
+
logStep('[2/3]', '\x1b[32mInterface Mock generated.\x1b[0m\n');
|
|
516
475
|
|
|
517
|
-
// ---
|
|
518
|
-
logStep('
|
|
476
|
+
// --- STEP 3: JSON CONFIG ---
|
|
477
|
+
logStep('[3/3]', '\x1b[33mFinalizing JSON configuration...\x1b[0m');
|
|
519
478
|
const aiData3 = await fetchStep('json_only', {
|
|
520
479
|
nativeCode: mergedModuleData.code,
|
|
521
480
|
mockCode: mergedModuleData.mockCode
|
|
@@ -526,18 +485,16 @@ if (command === 'alex') {
|
|
|
526
485
|
mergedModuleData.dataSchema = aiData3.moduleData.dataSchema;
|
|
527
486
|
mergedModuleData.uiSchema = aiData3.moduleData.uiSchema;
|
|
528
487
|
}
|
|
529
|
-
|
|
488
|
+
totalTokens += aiData3.tokensUsed || 0;
|
|
489
|
+
logStep('[3/3]', '\x1b[32mConfiguration completed.\x1b[0m\n');
|
|
530
490
|
} else {
|
|
531
|
-
// Le cloud n'est pas à jour, on efface le loader de l'étape 1 en silence
|
|
532
491
|
process.stdout.write(`\x1b[2K\r`);
|
|
533
492
|
}
|
|
534
493
|
|
|
535
|
-
// DISPLAY REASONING IN TERMINAL
|
|
536
494
|
if (aiData.thinking_process) {
|
|
537
|
-
console.log(
|
|
495
|
+
console.log(`\x1b[90m[Analysis] ${aiData.thinking_process.substring(0, 150)}...\x1b[0m`);
|
|
538
496
|
}
|
|
539
497
|
|
|
540
|
-
// --- OUTPUT VERS XTERM ---
|
|
541
498
|
if (aiData.status === 'success' || aiData.status === 'message' || aiData.status === 'complex_refusal') {
|
|
542
499
|
let rawMsg = aiDataMessage || "I'm ready.";
|
|
543
500
|
if (typeof rawMsg === 'object') {
|
|
@@ -555,7 +512,7 @@ if (command === 'alex') {
|
|
|
555
512
|
if (moduleName.length > 40) moduleName = moduleName.substring(0, 40) + "...";
|
|
556
513
|
}
|
|
557
514
|
|
|
558
|
-
console.log(`\
|
|
515
|
+
console.log(`\x1b[90mArchitecting: ${moduleName || 'Unknown Module'}\x1b[0m`);
|
|
559
516
|
|
|
560
517
|
const writeFile = (dir, name, content) => {
|
|
561
518
|
const fullPath = path.join(process.cwd(), dir);
|
|
@@ -578,7 +535,7 @@ if (command === 'alex') {
|
|
|
578
535
|
|
|
579
536
|
// --- KERNEL SYNCHRONIZATION ---
|
|
580
537
|
const depsCount = config_offload?.dependencies?.length || 0;
|
|
581
|
-
process.stdout.write(
|
|
538
|
+
process.stdout.write(`\x1b[33m[OS Sync]\x1b[0m Archiving ${moduleName} to OS (${depsCount} libs)... `);
|
|
582
539
|
|
|
583
540
|
try {
|
|
584
541
|
await axios.post(INJECT_DEPS_URL, {
|
|
@@ -595,33 +552,36 @@ if (command === 'alex') {
|
|
|
595
552
|
uiSchema: uiSchema || null
|
|
596
553
|
}
|
|
597
554
|
});
|
|
598
|
-
process.stdout.write(
|
|
555
|
+
process.stdout.write(`\x1b[32m[OK]\x1b[0m\n`);
|
|
599
556
|
} catch (err) {
|
|
600
|
-
process.stdout.write(
|
|
601
|
-
console.error(
|
|
557
|
+
process.stdout.write(`\x1b[31m[FAILED]\x1b[0m\n`);
|
|
558
|
+
console.error(`\x1b[31m[Error] OS sync failed: ${err.message}\x1b[0m`);
|
|
602
559
|
}
|
|
603
560
|
|
|
604
|
-
console.log('');
|
|
605
|
-
console.log(`\x1b[
|
|
606
|
-
console.log(
|
|
607
|
-
console.log(`\x1b[
|
|
608
|
-
|
|
561
|
+
console.log('\x1b[90mIf you manually edit the native code, sync it to the Cloud OS:\x1b[0m');
|
|
562
|
+
console.log(`\x1b[33mpatch module\x1b[0m \x1b[36m${moduleName}\x1b[0m \x1b[33mandroid\x1b[0m \x1b[90m(or ios)\x1b[0m`);
|
|
563
|
+
console.log('\x1b[90mTo eradicate this module later, open a new terminal and type:\x1b[0m');
|
|
564
|
+
console.log(`\x1b[31mnpm run fleetbo rm\x1b[0m \x1b[36m${moduleName}\x1b[0m`);
|
|
565
|
+
|
|
566
|
+
if (totalTokens > 0) {
|
|
567
|
+
console.log(`\x1b[90m[Telemetry] Total tokens consumed for this forge: ${totalTokens}\x1b[0m\n`);
|
|
568
|
+
}
|
|
609
569
|
|
|
610
570
|
} else if (aiData.status === 'success' && (!mergedModuleData || Object.keys(mergedModuleData).length === 0)) {
|
|
611
|
-
console.log(
|
|
571
|
+
console.log('\x1b[31m[Error] Alex replied, but source code could not be extracted.\x1b[0m\n');
|
|
612
572
|
}
|
|
613
573
|
} catch (error) {
|
|
614
574
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
615
|
-
console.error('\
|
|
575
|
+
console.error('\x1b[31m[Alex Error]\x1b[0m ' + (error.response?.data?.message || error.message));
|
|
616
576
|
}
|
|
617
577
|
};
|
|
618
578
|
|
|
619
579
|
// ============================================================
|
|
620
|
-
//
|
|
580
|
+
// START INTERACTIVE SESSION
|
|
621
581
|
// ============================================================
|
|
622
582
|
|
|
623
583
|
const startAlexSession = async () => {
|
|
624
|
-
process.stdout.write('\x1b[33m
|
|
584
|
+
process.stdout.write('\x1b[33m[Alex] Checking runtime state...\x1b[0m\r');
|
|
625
585
|
let attempts = 0;
|
|
626
586
|
const maxAttempts = 5;
|
|
627
587
|
let isReady = false;
|
|
@@ -653,7 +613,7 @@ if (command === 'alex') {
|
|
|
653
613
|
}
|
|
654
614
|
|
|
655
615
|
if (!isReady) {
|
|
656
|
-
console.error('\
|
|
616
|
+
console.error('\x1b[31m[ENGINE OFFLINE]\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
|
|
657
617
|
console.error(`\x1b[90m(Ensure you are running the runtime for project: ${keyApp})\x1b[0m`);
|
|
658
618
|
process.exit(1);
|
|
659
619
|
}
|
|
@@ -661,21 +621,15 @@ if (command === 'alex') {
|
|
|
661
621
|
process.stdout.write(' '.repeat(60) + '\r');
|
|
662
622
|
|
|
663
623
|
if (!hasAiKey) {
|
|
664
|
-
console.log('');
|
|
665
|
-
console.log('\x1b[1m\x1b[33m AI Configuration Required\x1b[0m');
|
|
666
|
-
console.log('');
|
|
624
|
+
console.log('\x1b[1m\x1b[33mAI Configuration Required\x1b[0m\n');
|
|
667
625
|
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('');
|
|
626
|
+
console.log('Connect your own AI key to continue.\n');
|
|
670
627
|
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('');
|
|
628
|
+
console.log('\x1b[32m[+]\x1b[0m Google AI \x1b[90m(Gemini Pro)\x1b[0m');
|
|
629
|
+
console.log('\x1b[36mhttps://aistudio.google.com/app/apikey\x1b[0m\n');
|
|
630
|
+
console.log('\x1b[33m[+]\x1b[0m Anthropic \x1b[90m(Claude Sonnet / Opus)\x1b[0m');
|
|
631
|
+
console.log('\x1b[36mhttps://console.anthropic.com/settings/keys\x1b[0m\n');
|
|
632
|
+
console.log('\x1b[90mClick the \x1b[32m[+]\x1b[90m button in the Terminal top-right bar\x1b[0m\n');
|
|
679
633
|
process.exit(0);
|
|
680
634
|
}
|
|
681
635
|
|
|
@@ -686,32 +640,27 @@ if (command === 'alex') {
|
|
|
686
640
|
|
|
687
641
|
console.log('\x1b[90m┌─────────────────────────────────────────────────────────┐\x1b[0m');
|
|
688
642
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
689
|
-
console.log('\x1b[90m│\x1b[0m \x1b[1m\x1b[36m
|
|
643
|
+
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
644
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
691
645
|
console.log('\x1b[90m│\x1b[0m \x1b[90mYour JS stays the brain. I forge the metal.\x1b[0m \x1b[90m│\x1b[0m');
|
|
692
646
|
console.log('\x1b[90m│ │\x1b[0m');
|
|
693
647
|
console.log('\x1b[90m└─────────────────────────────────────────────────────────┘\x1b[0m');
|
|
694
648
|
|
|
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`);
|
|
649
|
+
console.log(`\x1b[32m[OK]\x1b[0m ${providerLabel} \x1b[90m· ${modelLabel}\x1b[0m`);
|
|
650
|
+
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
651
|
|
|
699
|
-
console.log('');
|
|
700
652
|
console.log('\x1b[36mCAPABILITIES\x1b[0m');
|
|
701
653
|
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
|
|
702
654
|
console.log('\x1b[1mHardware\x1b[0m\x1b[90m Camera · Scanner · GPS · Biometrics\x1b[0m');
|
|
703
655
|
console.log('\x1b[1mHigh-Perf\x1b[0m\x1b[90m Video Feed · Swipe Deck · Audio\x1b[0m');
|
|
704
656
|
console.log('\x1b[1mSovereign\x1b[0m\x1b[90m Form + Photo + Save-to-Cloud\x1b[0m');
|
|
705
657
|
console.log('\x1b[1mFleetbo View\x1b[0m\x1b[90m Full native tab (120 FPS)\x1b[0m');
|
|
706
|
-
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m');
|
|
658
|
+
console.log('\x1b[90m─────────────────────────────────────────────────────\x1b[0m\n');
|
|
707
659
|
|
|
708
|
-
console.log('');
|
|
709
660
|
console.log('\x1b[36mCODE GENERATION SYNTAX\x1b[0m \x1b[90m(strict format required)\x1b[0m');
|
|
710
|
-
console.log('');
|
|
711
661
|
console.log('\x1b[90mFormat: \x1b[33m<verb>\x1b[0m module \x1b[36m<ModuleName>\x1b[0m');
|
|
712
662
|
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('');
|
|
663
|
+
console.log('\x1b[90mExample: \x1b[0m\x1b[33mupdate\x1b[0m module \x1b[36mProfileManager\x1b[0m \x1b[90mwith a red button\x1b[0m\n');
|
|
715
664
|
|
|
716
665
|
console.log('\x1b[32mAlex ❯\x1b[0m Describe your feature using the Compose panel...');
|
|
717
666
|
|
|
@@ -721,7 +670,7 @@ if (command === 'alex') {
|
|
|
721
670
|
});
|
|
722
671
|
|
|
723
672
|
rl.on('SIGINT', () => {
|
|
724
|
-
console.log('\
|
|
673
|
+
console.log('\x1b[90mAlex session aborted (Ctrl+C).\x1b[0m');
|
|
725
674
|
process.stdout.write('[ALEX_OFFLINE]\n');
|
|
726
675
|
rl.close();
|
|
727
676
|
process.exit(0);
|
|
@@ -732,7 +681,7 @@ if (command === 'alex') {
|
|
|
732
681
|
|
|
733
682
|
const executePrompt = async (text) => {
|
|
734
683
|
if (['exit', 'quit'].includes(text.toLowerCase())) {
|
|
735
|
-
console.log('\
|
|
684
|
+
console.log('\x1b[90mAlex session closed.\x1b[0m');
|
|
736
685
|
process.stdout.write('[ALEX_OFFLINE]\n');
|
|
737
686
|
rl.close();
|
|
738
687
|
return;
|
|
@@ -740,9 +689,7 @@ if (command === 'alex') {
|
|
|
740
689
|
|
|
741
690
|
if (text !== "") {
|
|
742
691
|
isProcessing = true;
|
|
743
|
-
|
|
744
692
|
await processAlexRequest(text);
|
|
745
|
-
|
|
746
693
|
isProcessing = false;
|
|
747
694
|
console.log('');
|
|
748
695
|
}
|
|
@@ -781,16 +728,14 @@ if (command === 'alex') {
|
|
|
781
728
|
else if (command === 'rm') {
|
|
782
729
|
const moduleName = args[1];
|
|
783
730
|
if (!moduleName) {
|
|
784
|
-
console.error('\
|
|
731
|
+
console.error('\x1b[31m[Error] Module name required.\x1b[0m');
|
|
785
732
|
console.log('\x1b[90mUsage: npm run fleetbo rm [ModuleName]\x1b[0m\n');
|
|
786
733
|
process.exit(1);
|
|
787
734
|
}
|
|
788
735
|
|
|
789
|
-
console.log(`\
|
|
736
|
+
console.log(`\x1b[33m[Annihilation] Removing module: ${moduleName}...\x1b[0m`);
|
|
790
737
|
|
|
791
|
-
// 1. Define physical paths
|
|
792
738
|
const ktPath = path.join(process.cwd(), 'public', 'native', 'android', `${moduleName}.kt`);
|
|
793
|
-
// AGNOSTIQUE — cherche .jsx (React) puis .vue (Vue)
|
|
794
739
|
const jsxPath = path.join(process.cwd(), 'src', 'app', 'mocks', `${moduleName}.jsx`);
|
|
795
740
|
const vuePath = path.join(process.cwd(), 'src', 'app', 'mocks', `${moduleName}.vue`);
|
|
796
741
|
const mockPath = fs.existsSync(jsxPath) ? jsxPath : fs.existsSync(vuePath) ? vuePath : null;
|
|
@@ -798,28 +743,25 @@ else if (command === 'rm') {
|
|
|
798
743
|
|
|
799
744
|
let actionsDone = 0;
|
|
800
745
|
|
|
801
|
-
// 2. Eradicate Metal Engine (Kotlin)
|
|
802
746
|
if (fs.existsSync(ktPath)) {
|
|
803
747
|
fs.unlinkSync(ktPath);
|
|
804
|
-
console.log(
|
|
748
|
+
console.log(`\x1b[32m[Deleted]\x1b[0m Metal file (.kt) eradicated.`);
|
|
805
749
|
actionsDone++;
|
|
806
750
|
}
|
|
807
751
|
|
|
808
|
-
// 3. Eradicate Virtual Twin (Mock JSX or Vue)
|
|
809
752
|
if (mockPath) {
|
|
810
753
|
fs.unlinkSync(mockPath);
|
|
811
|
-
console.log(
|
|
754
|
+
console.log(`\x1b[32m[Deleted]\x1b[0m Virtual Twin (${mockExt}) eradicated.`);
|
|
812
755
|
actionsDone++;
|
|
813
756
|
}
|
|
814
757
|
|
|
815
|
-
// 4. Disinfect System Core (App.jsx)
|
|
816
758
|
const unrouted = removeRouteFromAppJs(moduleName);
|
|
817
759
|
if (unrouted) actionsDone++;
|
|
818
760
|
|
|
819
761
|
if (actionsDone === 0) {
|
|
820
|
-
console.log(`\
|
|
762
|
+
console.log(`\x1b[31m[Warning] No trace of module "${moduleName}" found in the OS.\x1b[0m\n`);
|
|
821
763
|
} else {
|
|
822
|
-
console.log(`\
|
|
764
|
+
console.log(`\x1b[32mModule ${moduleName} successfully eradicated from the OS.\x1b[0m\n`);
|
|
823
765
|
}
|
|
824
766
|
}
|
|
825
767
|
|
|
@@ -828,21 +770,17 @@ else if (command === 'rm') {
|
|
|
828
770
|
// ============================================
|
|
829
771
|
else if (command === 'android' || command === 'ios') {
|
|
830
772
|
|
|
831
|
-
// DÉBUT DE LA PROTECTION (Fonction Async Immédiate)
|
|
832
|
-
// Cela garantit que le code fonctionne partout, même via 'require()'
|
|
833
773
|
(async () => {
|
|
834
774
|
|
|
835
|
-
// 🛑 INTERCEPTION IOS : BLOQUAGE NET (MAINTENANCE/BETA)
|
|
836
775
|
if (command === 'ios') {
|
|
837
|
-
console.log(`\
|
|
776
|
+
console.log(`\x1b[36m[FLEETBO IOS PROPULSION]\x1b[0m`);
|
|
838
777
|
console.log(`\x1b[33m[0/3] Initializing Neural Uplink...\x1b[0m`);
|
|
839
778
|
|
|
840
|
-
// Ce 'await' est maintenant sécurisé
|
|
841
779
|
await new Promise(r => setTimeout(r, 800));
|
|
842
780
|
|
|
843
|
-
console.log(`\
|
|
844
|
-
console.log(`\x1b[
|
|
845
|
-
console.log(`\x1b[
|
|
781
|
+
console.log(`\x1b[31m[PROPULSION ABORTED] iOS Frequency Restricted.\x1b[0m`);
|
|
782
|
+
console.log(`\x1b[90mThis module is currently reserved for Vanguard Pilots (Closed Beta).\x1b[0m`);
|
|
783
|
+
console.log(`\x1b[90mPlease engage propulsion on Android frequency for now.\x1b[0m\n`);
|
|
846
784
|
|
|
847
785
|
process.exit(1);
|
|
848
786
|
}
|
|
@@ -853,7 +791,6 @@ else if (command === 'android' || command === 'ios') {
|
|
|
853
791
|
const extension = platform === 'android' ? '.kt' : '.swift';
|
|
854
792
|
const nativePath = path.join(process.cwd(), nativeDir);
|
|
855
793
|
|
|
856
|
-
// Vérification des modules natifs
|
|
857
794
|
let hasNativeFiles = false;
|
|
858
795
|
let nativeFileCount = 0;
|
|
859
796
|
if (fs.existsSync(nativePath)) {
|
|
@@ -864,7 +801,7 @@ else if (command === 'android' || command === 'ios') {
|
|
|
864
801
|
}
|
|
865
802
|
|
|
866
803
|
if (!hasNativeFiles) {
|
|
867
|
-
console.log(`\
|
|
804
|
+
console.log(`\x1b[31m[ENGINE INCOMPLETE]\x1b[0m No native blueprints detected for \x1b[1m${platform.toUpperCase()}\x1b[0m.`);
|
|
868
805
|
console.log(`\x1b[90mAlex must architect at least one ${extension} module before deployment.\x1b[0m`);
|
|
869
806
|
console.log(`\x1b[90mRun: npm run fleetbo alex\x1b[0m\n`);
|
|
870
807
|
process.exit(1);
|
|
@@ -872,23 +809,18 @@ else if (command === 'android' || command === 'ios') {
|
|
|
872
809
|
|
|
873
810
|
const targetUrl = platform === 'android' ? ANDROID_BUILD_URL : IOS_BUILD_URL;
|
|
874
811
|
|
|
875
|
-
console.log(`\
|
|
876
|
-
console.log(`\x1b[90m
|
|
812
|
+
console.log(`\x1b[36m[FLEETBO ${platform.toUpperCase()} PROPULSION]\x1b[0m`);
|
|
813
|
+
console.log(`\x1b[90m${nativeFileCount} fleetbo module(s) detected\x1b[0m\n`);
|
|
877
814
|
|
|
878
815
|
try {
|
|
879
|
-
// ==========================================================
|
|
880
|
-
// PRE-FLIGHT CHECK QUOTAS & TIER
|
|
881
|
-
// ==========================================================
|
|
882
816
|
process.stdout.write(`\x1b[33m[0/3]\x1b[0m Checking Propulsion Access... `);
|
|
883
817
|
try {
|
|
884
|
-
// On envoie le signal 'x-preflight' pour tester les droits sans builder
|
|
885
818
|
await axios.post(targetUrl, {}, {
|
|
886
819
|
headers: {
|
|
887
820
|
'x-project-id': projectId,
|
|
888
821
|
'x-preflight': 'true'
|
|
889
822
|
}
|
|
890
823
|
});
|
|
891
|
-
// Si ça passe (200 OK), c'est un Senior ou un Junior autorisé (si vous changez d'avis)
|
|
892
824
|
process.stdout.write(`\x1b[32mOK (Senior Pilot)\x1b[0m\n\n`);
|
|
893
825
|
|
|
894
826
|
} catch (preflightError) {
|
|
@@ -896,27 +828,21 @@ else if (command === 'android' || command === 'ios') {
|
|
|
896
828
|
|
|
897
829
|
const errData = preflightError.response?.data;
|
|
898
830
|
|
|
899
|
-
// 🛑 1. INTERCEPTION SPÉCIFIQUE JUNIOR
|
|
900
|
-
// C'est ici qu'on lit le code renvoyé par index.js
|
|
901
831
|
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
832
|
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
905
|
-
console.log(
|
|
906
|
-
console.log(
|
|
907
|
-
console.log(
|
|
908
|
-
console.log(
|
|
909
|
-
process.exit(1);
|
|
833
|
+
console.log(`\x1b[31m[ACCESS DENIED] JUNIOR PILOT DETECTED\x1b[0m`);
|
|
834
|
+
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n`);
|
|
835
|
+
console.log(`\x1b[33mThis feature is locked for Junior Pilots.\x1b[0m`);
|
|
836
|
+
console.log(`\x1b[32mUpgrade to Senior on fleetbo.io to unlock Propulsion.\x1b[0m\n`);
|
|
837
|
+
process.exit(1);
|
|
910
838
|
}
|
|
911
839
|
|
|
912
|
-
// 2. Gestion des autres erreurs (Quota Senior dépassé, Serveur HS, etc.)
|
|
913
840
|
if (errData && errData.error) {
|
|
914
841
|
throw new Error(errData.error);
|
|
915
842
|
}
|
|
916
843
|
throw preflightError;
|
|
917
844
|
}
|
|
918
845
|
|
|
919
|
-
// Étape 1: Build React
|
|
920
846
|
console.log(`\x1b[33m[1/3]\x1b[0m Synthesizing Fleetbo Core Logic...`);
|
|
921
847
|
execSync('npm run build', { stdio: 'inherit' });
|
|
922
848
|
|
|
@@ -927,8 +853,7 @@ else if (command === 'android' || command === 'ios') {
|
|
|
927
853
|
throw new Error(`Build directory not found: ${buildDir}`);
|
|
928
854
|
}
|
|
929
855
|
|
|
930
|
-
|
|
931
|
-
console.log(`\n\x1b[33m[2/3]\x1b[0m Packaging bundle + fleetbo modules...`);
|
|
856
|
+
console.log(`\x1b[33m[2/3]\x1b[0m Packaging bundle + fleetbo modules...`);
|
|
932
857
|
|
|
933
858
|
const zipBuffer = await new Promise((resolve, reject) => {
|
|
934
859
|
const chunks = [];
|
|
@@ -949,10 +874,9 @@ else if (command === 'android' || command === 'ios') {
|
|
|
949
874
|
});
|
|
950
875
|
|
|
951
876
|
const sizeMB = (zipBuffer.length / 1024 / 1024).toFixed(2);
|
|
952
|
-
console.log(
|
|
877
|
+
console.log(`\x1b[32m[Ready]\x1b[0m Bundle ready: ${sizeMB} MB`);
|
|
953
878
|
|
|
954
|
-
|
|
955
|
-
console.log(`\n\x1b[33m[3/3]\x1b[0m Uploading to Fleetbo OS...`);
|
|
879
|
+
console.log(`\x1b[33m[3/3]\x1b[0m Uploading to Fleetbo OS...`);
|
|
956
880
|
await showEnergyTransfer();
|
|
957
881
|
|
|
958
882
|
let uploadResponse;
|
|
@@ -975,28 +899,28 @@ else if (command === 'android' || command === 'ios') {
|
|
|
975
899
|
}
|
|
976
900
|
|
|
977
901
|
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
902
|
console.log(`\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
981
|
-
console.log(`\x1b[
|
|
982
|
-
console.log(`\x1b[
|
|
903
|
+
console.log(`\x1b[32m[SUCCESS] ${platform.toUpperCase()} PROPULSION SUCCESSFUL\x1b[0m`);
|
|
904
|
+
console.log(`\x1b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
905
|
+
console.log(`\x1b[90mDeployment ID: ${uploadResponse.data.deploymentId || 'N/A'}\x1b[0m`);
|
|
906
|
+
console.log(`\x1b[90m${uploadResponse.data.message || 'Complete.'}\x1b[0m\n`);
|
|
983
907
|
} else {
|
|
984
908
|
throw new Error(uploadResponse.data?.error || 'Unknown logical error from Factory');
|
|
985
909
|
}
|
|
986
910
|
|
|
987
911
|
} catch (error) {
|
|
988
|
-
console.log(`\
|
|
989
|
-
console.log(`\x1b[31m
|
|
912
|
+
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
913
|
+
console.log(`\x1b[31m[Propulsion Failed]\x1b[0m`);
|
|
990
914
|
console.log(`\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m`);
|
|
991
915
|
|
|
992
|
-
console.error(`\x1b[31m
|
|
916
|
+
console.error(`\x1b[31m[Error]\x1b[0m ${error.message}`);
|
|
993
917
|
|
|
994
918
|
if (error.message.includes('Limit') || error.message.includes('Quota')) {
|
|
995
|
-
console.log(`\
|
|
919
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Upgrade to Senior Pilot for more builds.`);
|
|
996
920
|
} else if (error.message.includes('No native module')) {
|
|
997
|
-
console.log(`\
|
|
921
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Run "npm run fleetbo alex" to create native modules first.`);
|
|
998
922
|
} else if (error.message.includes('Trial Period Ended')) {
|
|
999
|
-
console.log(`\
|
|
923
|
+
console.log(`\x1b[33m[Tip]\x1b[0m Your free sprint is over. Upgrade to Senior Pilot on fleetbo.io.`);
|
|
1000
924
|
}
|
|
1001
925
|
console.log('');
|
|
1002
926
|
process.exit(1);
|
|
@@ -1012,7 +936,7 @@ else if (['page', 'g', 'generate'].includes(command)) {
|
|
|
1012
936
|
try {
|
|
1013
937
|
require(pageGeneratorPath);
|
|
1014
938
|
} catch (e) {
|
|
1015
|
-
console.error('\x1b[31m
|
|
939
|
+
console.error('\x1b[31m[Page Generator Error]\x1b[0m', e.message);
|
|
1016
940
|
process.exit(1);
|
|
1017
941
|
}
|
|
1018
942
|
}
|
|
@@ -1053,10 +977,10 @@ else {
|
|
|
1053
977
|
async function cleanupAndExit(code = 0) {
|
|
1054
978
|
if (isExiting) return;
|
|
1055
979
|
isExiting = true;
|
|
1056
|
-
console.log('\
|
|
980
|
+
console.log('\x1b[33m[Fleetbo] Stopping environment & Cleaning Uplink...\x1b[0m');
|
|
1057
981
|
try {
|
|
1058
982
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail });
|
|
1059
|
-
console.log('\x1b[32m[Fleetbo]
|
|
983
|
+
console.log('\x1b[32m[Fleetbo] Network status reset to offline.\x1b[0m');
|
|
1060
984
|
} catch (e) {
|
|
1061
985
|
console.error('[Fleetbo] Network cleanup warning:', e.message);
|
|
1062
986
|
}
|
|
@@ -1066,7 +990,6 @@ else {
|
|
|
1066
990
|
process.exit(code);
|
|
1067
991
|
}
|
|
1068
992
|
|
|
1069
|
-
// 🪟 WINDOWS FIX : capture Ctrl+C via readline pour éviter l'invite "O/N"
|
|
1070
993
|
if (process.platform === 'win32') {
|
|
1071
994
|
const rlWin = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
1072
995
|
rlWin.on('SIGINT', () => cleanupAndExit(0));
|
|
@@ -1078,31 +1001,30 @@ else {
|
|
|
1078
1001
|
async function syncFirebase(keyApp, networkUrl, testerEmail) {
|
|
1079
1002
|
try {
|
|
1080
1003
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
1081
|
-
console.log('\
|
|
1082
|
-
console.log(`\
|
|
1004
|
+
console.log('\x1b[32mEngine started successfully\x1b[0m');
|
|
1005
|
+
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
1083
1006
|
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m \x1b[1mProject [${keyApp}] running in OS\x1b[0m`);
|
|
1084
1007
|
console.log('\x1b[32mFleetbo OS ❯\x1b[0m You can now start coding and previewing.');
|
|
1085
1008
|
console.log(`\x1b[32mFleetbo OS ❯\x1b[0m -------------------------------------------------------------`);
|
|
1086
|
-
console.log(`\
|
|
1009
|
+
console.log(`\x1b[34mPilot Instruction ❯\x1b[0m Return to the Workspace. The Engine is ready for your orders.\n`);
|
|
1087
1010
|
} catch (err) {
|
|
1088
1011
|
console.error(`\x1b[31mFleetbo OS ❯\x1b[0m Sync Error: ${err.message}`);
|
|
1089
1012
|
}
|
|
1090
1013
|
}
|
|
1091
1014
|
|
|
1092
1015
|
async function runDevEnvironment() {
|
|
1093
|
-
console.log(`[Fleetbo]
|
|
1016
|
+
console.log(`[Fleetbo] Initializing Universal Dev Environment...`);
|
|
1094
1017
|
|
|
1095
1018
|
killNetworkService();
|
|
1096
|
-
killProcessOnPort(PORT);
|
|
1019
|
+
killProcessOnPort(PORT);
|
|
1097
1020
|
|
|
1098
1021
|
if (!testerEmail) {
|
|
1099
|
-
console.error('\x1b[
|
|
1022
|
+
console.error('\x1b[31m[Error] FLEETBO_APP_TESTER_EMAIL missing in .env\x1b[0m');
|
|
1100
1023
|
process.exit(1);
|
|
1101
1024
|
}
|
|
1102
1025
|
|
|
1103
1026
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
1104
1027
|
|
|
1105
|
-
// Lancement agnostique du serveur de dev (React, Vue, etc.)
|
|
1106
1028
|
const devServer = spawn(`${npmCmd} run dev --silent -- --host 127.0.0.1 --port ${PORT}`, {
|
|
1107
1029
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1108
1030
|
shell: true,
|
|
@@ -1115,12 +1037,11 @@ else {
|
|
|
1115
1037
|
devServer.stderr.pipe(process.stderr);
|
|
1116
1038
|
|
|
1117
1039
|
let connectionStarted = false;
|
|
1118
|
-
let detectedPort = PORT;
|
|
1040
|
+
let detectedPort = PORT;
|
|
1119
1041
|
|
|
1120
1042
|
devServer.stdout.on('data', (data) => {
|
|
1121
1043
|
const output = data.toString();
|
|
1122
1044
|
|
|
1123
|
-
// FILTRE ANTI-PLOMBERIE FLEETBO
|
|
1124
1045
|
const lines = output.split('\n');
|
|
1125
1046
|
const forbiddenTerms = [
|
|
1126
1047
|
'Attempting to bind to HOST', 'If this was unintentional', 'Learn more here:',
|
|
@@ -1137,22 +1058,17 @@ else {
|
|
|
1137
1058
|
process.stdout.write(filteredOutput + '\n');
|
|
1138
1059
|
}
|
|
1139
1060
|
|
|
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
1061
|
const portMatch = output.match(/http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/);
|
|
1145
1062
|
|
|
1146
1063
|
if (portMatch) {
|
|
1147
|
-
detectedPort = portMatch[1];
|
|
1064
|
+
detectedPort = portMatch[1];
|
|
1148
1065
|
|
|
1149
|
-
// On lance le tunnel Cloudflare UNIQUEMENT sur ce port définitif
|
|
1150
1066
|
if (!connectionStarted) {
|
|
1151
1067
|
connectionStarted = true;
|
|
1152
1068
|
|
|
1153
|
-
console.log('\
|
|
1154
|
-
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m
|
|
1155
|
-
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m
|
|
1069
|
+
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
1070
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m Establishing Secure Uplink...`);
|
|
1071
|
+
console.log(`\x1b[33mFleetbo OS ❯\x1b[0m Please wait for the green message...`);
|
|
1156
1072
|
console.log('\x1b[33mFleetbo OS ❯\x1b[0m ---------------------------------------------------');
|
|
1157
1073
|
|
|
1158
1074
|
const MAX_UPLINK_RETRIES = 5;
|
|
@@ -1165,10 +1081,9 @@ else {
|
|
|
1165
1081
|
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
1166
1082
|
|
|
1167
1083
|
if (attempt > 0) {
|
|
1168
|
-
console.log(`\x1b[33m[Fleetbo]
|
|
1084
|
+
console.log(`\x1b[33m[Fleetbo] Uplink reconnection ${attempt}/${MAX_UPLINK_RETRIES - 1}...\x1b[0m`);
|
|
1169
1085
|
}
|
|
1170
1086
|
|
|
1171
|
-
// LE TUNNEL CLOUDFLARE UTILISE LE PORT DÉTECTÉ DYNAMIQUEMENT !
|
|
1172
1087
|
const uplinkCommand = `${npxCmd} -y cloudflared tunnel --url http://127.0.0.1:${detectedPort} --http-host-header 127.0.0.1:${detectedPort}`;
|
|
1173
1088
|
uplinkProcess = spawn(uplinkCommand, { shell: true });
|
|
1174
1089
|
|
|
@@ -1189,7 +1104,7 @@ else {
|
|
|
1189
1104
|
|
|
1190
1105
|
uplinkProcess.on('error', () => {
|
|
1191
1106
|
if (uplinkFound) return;
|
|
1192
|
-
console.error(`\x1b[31m[Fleetbo]
|
|
1107
|
+
console.error(`\x1b[31m[Fleetbo] Uplink Connection failed to establish.\x1b[0m`);
|
|
1193
1108
|
});
|
|
1194
1109
|
|
|
1195
1110
|
uplinkProcess.on('close', () => {
|
|
@@ -1197,10 +1112,10 @@ else {
|
|
|
1197
1112
|
const nextAttempt = attempt + 1;
|
|
1198
1113
|
if (nextAttempt < MAX_UPLINK_RETRIES) {
|
|
1199
1114
|
const delay = RETRY_DELAYS[nextAttempt] || 30;
|
|
1200
|
-
console.log(`\x1b[33m[Fleetbo]
|
|
1115
|
+
console.log(`\x1b[33m[Fleetbo] Uplink interrupted. Fleetbo OS retrying in ${delay}s... (${nextAttempt}/${MAX_UPLINK_RETRIES - 1})\x1b[0m`);
|
|
1201
1116
|
setTimeout(() => startUplink(nextAttempt), delay * 1000);
|
|
1202
1117
|
} else {
|
|
1203
|
-
console.error(`\x1b[31m[Fleetbo]
|
|
1118
|
+
console.error(`\x1b[31m[Fleetbo] Secure Uplink could not be established.\x1b[0m`);
|
|
1204
1119
|
}
|
|
1205
1120
|
});
|
|
1206
1121
|
};
|