create-fleetbo-project 1.2.85 → 1.2.87
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/install-react-template.js +212 -571
- package/install-react-template1.js +572 -0
- package/package.json +4 -4
|
@@ -1,572 +1,213 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { execSync } = require('child_process');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const https = require('https');
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.log(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
} catch (err) {
|
|
214
|
-
process.stdout.write(\` \\x1b[31mFAILED\\x1b[0m\\n\`);
|
|
215
|
-
console.error(\` ⚠️ Config sync failed: \${err.message}\`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
} catch (error) {
|
|
220
|
-
process.stdout.write('\\r' + ' '.repeat(50) + '\\r');
|
|
221
|
-
console.error('\\n\\x1b[31m Alex Error:\\x1b[0m ' + (error.response?.data?.message || error.message));
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
const startAlexSession = async () => {
|
|
225
|
-
process.stdout.write('\\x1b[33m🛡️ Alex is checking runtime state...\\x1b[0m\\r');
|
|
226
|
-
let attempts = 0;
|
|
227
|
-
const maxAttempts = 5;
|
|
228
|
-
let isReady = false;
|
|
229
|
-
let dynamicUsername = 'Pilot';
|
|
230
|
-
while (attempts < maxAttempts && !isReady) {
|
|
231
|
-
try {
|
|
232
|
-
const validation = await axios.post(ALEX_ENGINE_URL, {
|
|
233
|
-
prompt: "ping", validateProject: true, checkNetwork: true, projectKey: keyApp
|
|
234
|
-
}, { headers: { 'x-project-id': projectId }, timeout: 5000 });
|
|
235
|
-
|
|
236
|
-
if (validation.data?.isRunning) { isReady = true; dynamicUsername = validation.data.username || 'Pilot'; break; }
|
|
237
|
-
attempts++;
|
|
238
|
-
if (attempts < maxAttempts) await new Promise(r => setTimeout(r, 2000));
|
|
239
|
-
} catch (error) {
|
|
240
|
-
attempts++;
|
|
241
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (!isReady) {
|
|
245
|
-
console.error('\\n\\x1b[31m⚠️ ENGINE OFFLINE:\\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
|
|
246
|
-
console.error(\`\\x1b[90m(Ensure you are running the runtime for project: \${keyApp})\\x1b[0m\`);
|
|
247
|
-
process.exit(1);
|
|
248
|
-
}
|
|
249
|
-
process.stdout.write(' '.repeat(60) + '\\r');
|
|
250
|
-
console.log('\\n\\x1b[32m🤖 Alex is now online.\\x1b[0m');
|
|
251
|
-
console.log('\\x1b[32mAlex ❯\\x1b[0m Infrastructure online. I am ready to forge. What module are we architecting today, Pilot?');
|
|
252
|
-
console.log('');
|
|
253
|
-
const rl = readline.createInterface({
|
|
254
|
-
input: process.stdin,
|
|
255
|
-
output: process.stdout,
|
|
256
|
-
prompt: \`\\x1b[34m\${dynamicUsername} ❯ \\x1b[0m\`
|
|
257
|
-
});
|
|
258
|
-
process.stdout.write('\\n\\x1b[F');
|
|
259
|
-
rl.prompt();
|
|
260
|
-
rl.on('line', async (line) => {
|
|
261
|
-
if (['exit', 'quit'].includes(line.trim().toLowerCase())) {
|
|
262
|
-
console.log('\\n\\x1b[90m Alex session closed.\\x1b[0m');
|
|
263
|
-
rl.close();
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (line.trim()) {
|
|
267
|
-
await processAlexRequest(line.trim());
|
|
268
|
-
console.log('');
|
|
269
|
-
}
|
|
270
|
-
process.stdout.write('\\n\\x1b[F');
|
|
271
|
-
rl.prompt();
|
|
272
|
-
}).on('close', () => {
|
|
273
|
-
process.exit(0);
|
|
274
|
-
});
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
if (!initialPrompt || initialPrompt === '?') startAlexSession();
|
|
278
|
-
else processAlexRequest(initialPrompt);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
if (command === 'android' || command === 'ios') {
|
|
282
|
-
checkGitSecurity();
|
|
283
|
-
const platform = command;
|
|
284
|
-
const nativeDir = platform === 'android' ? 'public/native/android/' : 'public/native/ios/';
|
|
285
|
-
const extension = platform === 'android' ? '.kt' : '.swift';
|
|
286
|
-
const fullPath = path.join(process.cwd(), nativeDir);
|
|
287
|
-
let hasNativeFiles = false;
|
|
288
|
-
if (fs.existsSync(fullPath)) {
|
|
289
|
-
const files = fs.readdirSync(fullPath);
|
|
290
|
-
hasNativeFiles = files.some(file => file.endsWith(extension));
|
|
291
|
-
}
|
|
292
|
-
if (!hasNativeFiles) {
|
|
293
|
-
console.log(\`\\n\\x1b[31m⚠️ ENGINE INCOMPLETE:\\x1b[0m No native blueprints detected for \\x1b[1m\${platform.toUpperCase()}\\x1b[0m.\`);
|
|
294
|
-
console.log(\`\\x1b[90mAlex must architect at least one \${extension} module before deployment.\\x1b[0m\\n\`);
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
const targetUrl = platform === 'android' ? ANDROID_BUILD_URL : IOS_BUILD_URL;
|
|
298
|
-
(async () => {
|
|
299
|
-
console.log(\`\\n\\x1b[36m⚡ FLEETBO \${platform.toUpperCase()} UPLINK\\x1b[0m\`);
|
|
300
|
-
try {
|
|
301
|
-
execSync('npm run build', { stdio: 'inherit' });
|
|
302
|
-
let buildDir = fs.existsSync(path.join(process.cwd(), 'dist')) ? 'dist' : 'build';
|
|
303
|
-
const zipBuffer = await new Promise((resolve, reject) => {
|
|
304
|
-
const chunks = []; const archive = archiver('zip', { zlib: { level: 9 } });
|
|
305
|
-
archive.on('data', chunk => chunks.push(chunk));
|
|
306
|
-
archive.on('end', () => resolve(Buffer.concat(chunks)));
|
|
307
|
-
archive.directory(path.join(process.cwd(), buildDir), false);
|
|
308
|
-
archive.finalize();
|
|
309
|
-
});
|
|
310
|
-
console.log(\`\\n\\x1b[33mSyncing \${platform} logic bundle...\\x1b[0m\`);
|
|
311
|
-
await showEnergyTransfer();
|
|
312
|
-
|
|
313
|
-
const res = await axios.post(targetUrl, zipBuffer, {
|
|
314
|
-
headers: { 'Content-Type': 'application/zip', 'x-project-id': projectId }
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
if (res.data.success) {
|
|
318
|
-
console.log(\`\\n\\x1b[1m\${platform.toUpperCase()} DEPLOYED\\x1b[0m | \\x1b[32mAlex ❯\\x1b[0m Runtime updated.\`);
|
|
319
|
-
}
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.error(\`\\n\\x1b[31m Build Error:\\x1b[0m \${error.response?.data?.error || error.message}\`);
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
})();
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
const GENERATOR_COMMANDS = ['page', 'g', 'generate', 'android', 'ios'];
|
|
328
|
-
if (GENERATOR_COMMANDS.includes(command)) {
|
|
329
|
-
try { require('./page.js'); } catch (e) { console.error(e.message); process.exit(1); }
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const NULL_DEV = process.platform === 'win32' ? '>nul 2>&1' : '2>/dev/null';
|
|
333
|
-
function killProcessOnPort(port) {
|
|
334
|
-
try {
|
|
335
|
-
if (process.platform !== 'win32') {
|
|
336
|
-
const pid = execSync(\`lsof -ti:\${port} \${NULL_DEV}\`).toString().trim();
|
|
337
|
-
if (pid) execSync(\`kill -9 \${pid.split('\\n').join(' ')} \${NULL_DEV}\`);
|
|
338
|
-
}
|
|
339
|
-
} catch (e) {}
|
|
340
|
-
}
|
|
341
|
-
const killNetworkService = () => {
|
|
342
|
-
if (uplinkProcess) {
|
|
343
|
-
try {
|
|
344
|
-
uplinkProcess.kill('SIGINT');
|
|
345
|
-
console.log('[Fleetbo] Engine closed.');
|
|
346
|
-
} catch (e) {
|
|
347
|
-
console.error('[Fleetbo] Error closing tunnel:', e.message);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
let isExiting = false;
|
|
352
|
-
async function cleanupAndExit(code = 0) {
|
|
353
|
-
if (isExiting) return;
|
|
354
|
-
isExiting = true;
|
|
355
|
-
console.log('\\n\\x1b[33m[Fleetbo] 🛑 Stopping environment & Cleaning Uplink...\\x1b[0m');
|
|
356
|
-
try {
|
|
357
|
-
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail });
|
|
358
|
-
console.log('\\x1b[32m[Fleetbo] Network status reset to offline.\\x1b[0m');
|
|
359
|
-
} catch (e) {
|
|
360
|
-
console.error('[Fleetbo] Network cleanup warning:', e.message);
|
|
361
|
-
}
|
|
362
|
-
killNetworkService();
|
|
363
|
-
killProcessOnPort(PORT);
|
|
364
|
-
console.log('[Fleetbo] Bye.');
|
|
365
|
-
process.exit(code);
|
|
366
|
-
}
|
|
367
|
-
process.on('SIGINT', () => cleanupAndExit(0));
|
|
368
|
-
process.on('SIGTERM', () => cleanupAndExit(0));
|
|
369
|
-
async function syncFirebase(keyApp, networkUrl, testerEmail) {
|
|
370
|
-
try {
|
|
371
|
-
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
|
|
372
|
-
console.log('\\n\\x1b[32mEngine started successfully\\x1b[0m');
|
|
373
|
-
console.log(\`\\n\\x1b[32m[Fleetbo] \\x1b[0m -------------------------------------------------------------\`);
|
|
374
|
-
console.log('\\x1b[32m[Fleetbo] \\x1b[1mGO GO GO ! FLEETBO STUDIO IS READY\\x1b[0m');
|
|
375
|
-
console.log('\\x1b[32m[Fleetbo] You can now start coding and previewing in Studio. 🚀');
|
|
376
|
-
console.log(\`\\x1b[32m[Fleetbo] \\x1b[0m -------------------------------------------------------------\`);
|
|
377
|
-
console.log(\`\\x1b[34mPilot Instruction ❯\\x1b[0m Switch to your Fleetbo Cockpit tab to begin.\\n\`);
|
|
378
|
-
} catch (err) {
|
|
379
|
-
console.error(\`[Fleetbo] Sync Error: \${err.message}\`);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
async function runDevEnvironment() {
|
|
383
|
-
console.log(\`[Fleetbo] 🛡️ Initializing Dev Environment...\`);
|
|
384
|
-
killNetworkService();
|
|
385
|
-
killProcessOnPort(PORT);
|
|
386
|
-
|
|
387
|
-
if (!testerEmail) { console.error('Error: REACT_APP_TESTER_EMAIL missing'); process.exit(1); }
|
|
388
|
-
|
|
389
|
-
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
390
|
-
const devServer = spawn(npmCmd, ['start'], {
|
|
391
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
392
|
-
shell: true,
|
|
393
|
-
env: {
|
|
394
|
-
...process.env,
|
|
395
|
-
BROWSER: 'none',
|
|
396
|
-
PORT: PORT.toString(),
|
|
397
|
-
DANGEROUSLY_DISABLE_HOST_CHECK: 'true',
|
|
398
|
-
HOST: '0.0.0.0',
|
|
399
|
-
WDS_SOCKET_HOST: 'localhost',
|
|
400
|
-
WDS_SOCKET_PORT: PORT.toString()
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
devServer.stdout.pipe(process.stdout);
|
|
405
|
-
devServer.stderr.pipe(process.stderr);
|
|
406
|
-
|
|
407
|
-
let connectionStarted = false;
|
|
408
|
-
devServer.stdout.on('data', (data) => {
|
|
409
|
-
const output = data.toString();
|
|
410
|
-
|
|
411
|
-
if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
412
|
-
connectionStarted = true;
|
|
413
|
-
|
|
414
|
-
console.log('\\n[Fleetbo] ---------------------------------------------------');
|
|
415
|
-
console.log(\`[Fleetbo] 🔗 Establishing Secure Uplink...\`);
|
|
416
|
-
console.log(\`[Fleetbo] ⏳ Please wait for the green message...\`);
|
|
417
|
-
console.log('[Fleetbo] ---------------------------------------------------');
|
|
418
|
-
|
|
419
|
-
const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
420
|
-
uplinkProcess = spawn(npxCmd, [
|
|
421
|
-
'cloudflared',
|
|
422
|
-
'tunnel',
|
|
423
|
-
'--url', \`http://127.0.0.1:\${PORT}\`,
|
|
424
|
-
'--http-host-header', \`127.0.0.1:\${PORT}\`
|
|
425
|
-
], { shell: true });
|
|
426
|
-
|
|
427
|
-
uplinkProcess.stderr.on('data', (chunk) => {
|
|
428
|
-
const text = chunk.toString();
|
|
429
|
-
const match = text.match(/https:\\/\\/[a-zA-Z0-9-]+\\.trycloudflare\\.com/);
|
|
430
|
-
if (match) syncFirebase(process.env.REACT_KEY_APP, match[0], process.env.REACT_APP_TESTER_EMAIL);
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
runDevEnvironment();
|
|
437
|
-
`;
|
|
438
|
-
const args = process.argv.slice(2);
|
|
439
|
-
const projectNameArg = args.find(arg => !arg.startsWith('--'));
|
|
440
|
-
const tokenArg = args.find(arg => arg.startsWith('--token='));
|
|
441
|
-
const emailArg = args.find(arg => arg.startsWith('--email='));
|
|
442
|
-
const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
|
|
443
|
-
const userEmailArg = emailArg ? emailArg.split('=')[1] : null;
|
|
444
|
-
if (!projectNameArg || !bootstrapTokenArg || !userEmailArg) {
|
|
445
|
-
console.error('\n Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
448
|
-
const projectName = projectNameArg;
|
|
449
|
-
const projectDir = path.join(process.cwd(), projectName);
|
|
450
|
-
|
|
451
|
-
function fetchProjectKeys(token) {
|
|
452
|
-
return new Promise((resolve, reject) => {
|
|
453
|
-
const postData = JSON.stringify({ token });
|
|
454
|
-
const uri = new URL(bootstrapUrl);
|
|
455
|
-
const options = {
|
|
456
|
-
hostname: uri.hostname, path: uri.pathname, method: 'POST',
|
|
457
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }
|
|
458
|
-
};
|
|
459
|
-
const req = https.request(options, (res) => {
|
|
460
|
-
let data = '';
|
|
461
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
462
|
-
res.on('end', () => {
|
|
463
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
464
|
-
try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Invalid response')); }
|
|
465
|
-
} else { reject(new Error(`Server error ${res.statusCode}`)); }
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
req.on('error', (e) => reject(e));
|
|
469
|
-
req.write(postData);
|
|
470
|
-
req.end();
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
function downloadEngine(url, dest) {
|
|
474
|
-
return new Promise((resolve, reject) => {
|
|
475
|
-
const uri = new URL(url);
|
|
476
|
-
const options = {
|
|
477
|
-
hostname: uri.hostname, path: uri.pathname + uri.search, method: 'GET',
|
|
478
|
-
headers: { 'User-Agent': 'Fleetbo-CLI-Installer', 'Accept': '*/*' }
|
|
479
|
-
};
|
|
480
|
-
const request = https.get(options, (response) => {
|
|
481
|
-
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
482
|
-
if (!response.headers.location) { reject(new Error("Redirect error")); return; }
|
|
483
|
-
downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
if (response.statusCode !== 200) { reject(new Error(`Status: ${response.statusCode}`)); return; }
|
|
487
|
-
const file = fs.createWriteStream(dest);
|
|
488
|
-
response.pipe(file);
|
|
489
|
-
file.on('finish', () => file.close(resolve));
|
|
490
|
-
file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
|
|
491
|
-
});
|
|
492
|
-
request.on('error', (err) => reject(err));
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
async function setupProject() {
|
|
496
|
-
console.log(`\n ⚡Initializing Fleetbo Framework for "${projectName}"...`);
|
|
497
|
-
|
|
498
|
-
try {
|
|
499
|
-
if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
|
|
500
|
-
fs.mkdirSync(projectDir);
|
|
501
|
-
|
|
502
|
-
console.log(' [1/7] Downloading Fleetbo Core Engine...');
|
|
503
|
-
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
504
|
-
await downloadEngine(archiveUrl, archivePath);
|
|
505
|
-
|
|
506
|
-
console.log(' [2/7] Scaffolding project structure...');
|
|
507
|
-
try {
|
|
508
|
-
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
509
|
-
fs.unlinkSync(archivePath);
|
|
510
|
-
} catch (e) { throw new Error("Extract failed."); }
|
|
511
|
-
|
|
512
|
-
process.chdir(projectDir);
|
|
513
|
-
|
|
514
|
-
console.log(' [2.5/7] Standardizing Architecture (src/app)...');
|
|
515
|
-
const oldPath = path.join(projectDir, 'src/pages');
|
|
516
|
-
const newPath = path.join(projectDir, 'src/app');
|
|
517
|
-
|
|
518
|
-
if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
519
|
-
try {
|
|
520
|
-
fs.renameSync(oldPath, newPath);
|
|
521
|
-
const appJsPath = path.join(projectDir, 'src/App.js');
|
|
522
|
-
if (fs.existsSync(appJsPath)) {
|
|
523
|
-
let appContent = fs.readFileSync(appJsPath, 'utf8');
|
|
524
|
-
appContent = appContent.replace(/from "\.\/pages\//g, 'from "./app/');
|
|
525
|
-
appContent = appContent.replace(/from '\.\/pages\//g, "from './app/");
|
|
526
|
-
fs.writeFileSync(appJsPath, appContent);
|
|
527
|
-
}
|
|
528
|
-
} catch (err) {
|
|
529
|
-
console.warn(' Architecture migration warning:', err.message);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
console.log(' [3/7] Authenticating with Fleetbo Cloud...');
|
|
534
|
-
const keys = await fetchProjectKeys(bootstrapTokenArg);
|
|
535
|
-
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
536
|
-
|
|
537
|
-
console.log(' [4/7] Configuring environment & CLI...');
|
|
538
|
-
const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}
|
|
539
|
-
REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}
|
|
540
|
-
REACT_KEY_APP=${projectName}
|
|
541
|
-
REACT_APP_TESTER_EMAIL=${userEmailArg}
|
|
542
|
-
DANGEROUSLY_DISABLE_HOST_CHECK=true
|
|
543
|
-
WDS_SOCKET_PORT=0`;
|
|
544
|
-
|
|
545
|
-
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
546
|
-
console.log(' [5/7] Securing environment (adding .gitignore)...');
|
|
547
|
-
const gitignoreContent = `# Fleetbo Security\n.env\n.env.local\nnode_modules/\ndist/\nbuild/\n.DS_Store\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n`;
|
|
548
|
-
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent, 'utf8');
|
|
549
|
-
const scriptsDir = path.join(projectDir, 'scripts');
|
|
550
|
-
if (!fs.existsSync(scriptsDir)) fs.mkdirSync(scriptsDir, { recursive: true });
|
|
551
|
-
fs.writeFileSync(path.join(scriptsDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
|
|
552
|
-
try { fs.chmodSync(path.join(scriptsDir, 'cli.js'), '755'); } catch (e) {}
|
|
553
|
-
console.log(' [6/7] Installing dependencies...');
|
|
554
|
-
execSync('npm install', { stdio: 'inherit' });
|
|
555
|
-
execSync('npm install cloudflared dotenv axios archiver --save-dev', { stdio: 'ignore' });
|
|
556
|
-
console.log(' [7/7] Finalizing setup...');
|
|
557
|
-
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
558
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
559
|
-
packageJson.name = projectName;
|
|
560
|
-
packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node scripts/cli.js", "dev": "node scripts/cli.js" };
|
|
561
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
562
|
-
console.log('\n \x1b[32m [Fleetbo] Project successfully created!\x1b[0m');
|
|
563
|
-
console.log(`\n Run: cd ${projectName} && npm run fleetbo`);
|
|
564
|
-
console.log('\n \x1b[32m [Fleetbo] To start architecting with Alex, run: npm run fleetbo alex!\x1b[0m');
|
|
565
|
-
console.log('');
|
|
566
|
-
} catch (error) {
|
|
567
|
-
console.error('\n Setup failed:', error.message);
|
|
568
|
-
if (fs.existsSync(projectDir)) try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
569
|
-
process.exit(1);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
const repoOwner = 'FleetFleetbo';
|
|
9
|
+
const repoName = 'dev.fleetbo.io';
|
|
10
|
+
const branchName = 'master';
|
|
11
|
+
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/${branchName}.tar.gz`;
|
|
12
|
+
const bootstrapUrl = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const projectNameArg = args.find(arg => !arg.startsWith('--'));
|
|
16
|
+
const tokenArg = args.find(arg => arg.startsWith('--token='));
|
|
17
|
+
const emailArg = args.find(arg => arg.startsWith('--email='));
|
|
18
|
+
const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
|
|
19
|
+
const userEmailArg = emailArg ? emailArg.split('=')[1] : null;
|
|
20
|
+
|
|
21
|
+
if (!projectNameArg || !bootstrapTokenArg || !userEmailArg) {
|
|
22
|
+
console.error('\n Usage: npx create-fleetbo-project <name> --token=<token> --email=<email>');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const projectName = projectNameArg;
|
|
27
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
28
|
+
|
|
29
|
+
function fetchProjectKeys(token) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const postData = JSON.stringify({ token });
|
|
32
|
+
const uri = new URL(bootstrapUrl);
|
|
33
|
+
const options = {
|
|
34
|
+
hostname: uri.hostname,
|
|
35
|
+
path: uri.pathname,
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const req = https.request(options, (res) => {
|
|
43
|
+
let data = '';
|
|
44
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
45
|
+
res.on('end', () => {
|
|
46
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
47
|
+
try { resolve(JSON.parse(data)); }
|
|
48
|
+
catch (e) { reject(new Error('Invalid response')); }
|
|
49
|
+
} else {
|
|
50
|
+
reject(new Error(`Server error ${res.statusCode}`));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
req.on('error', (e) => reject(e));
|
|
55
|
+
req.write(postData);
|
|
56
|
+
req.end();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function downloadEngine(url, dest) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const uri = new URL(url);
|
|
63
|
+
const options = {
|
|
64
|
+
hostname: uri.hostname,
|
|
65
|
+
path: uri.pathname + uri.search,
|
|
66
|
+
method: 'GET',
|
|
67
|
+
headers: {
|
|
68
|
+
'User-Agent': 'Fleetbo-CLI-Installer',
|
|
69
|
+
'Accept': '*/*'
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const request = https.get(options, (response) => {
|
|
73
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
74
|
+
if (!response.headers.location) {
|
|
75
|
+
reject(new Error("Redirect error"));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (response.statusCode !== 200) {
|
|
82
|
+
reject(new Error(`Status: ${response.statusCode}`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const file = fs.createWriteStream(dest);
|
|
86
|
+
response.pipe(file);
|
|
87
|
+
file.on('finish', () => file.close(resolve));
|
|
88
|
+
file.on('error', (err) => {
|
|
89
|
+
fs.unlink(dest, () => {});
|
|
90
|
+
reject(err);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
request.on('error', (err) => reject(err));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function setupProject() {
|
|
98
|
+
console.log(`\n ⚡ Initializing Fleetbo Framework for "${projectName}"...`);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
if (fs.existsSync(projectDir)) {
|
|
102
|
+
throw new Error(`Directory "${projectName}" already exists.`);
|
|
103
|
+
}
|
|
104
|
+
fs.mkdirSync(projectDir);
|
|
105
|
+
|
|
106
|
+
console.log(' [1/8] Downloading Fleetbo Core Engine...');
|
|
107
|
+
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
108
|
+
await downloadEngine(archiveUrl, archivePath);
|
|
109
|
+
|
|
110
|
+
console.log(' [2/8] Scaffolding project structure...');
|
|
111
|
+
try {
|
|
112
|
+
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
113
|
+
fs.unlinkSync(archivePath);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
throw new Error("Extract failed.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
process.chdir(projectDir);
|
|
119
|
+
|
|
120
|
+
console.log(' [3/8] Standardizing Architecture (src/app)...');
|
|
121
|
+
const oldPath = path.join(projectDir, 'src/pages');
|
|
122
|
+
const newPath = path.join(projectDir, 'src/app');
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
125
|
+
try {
|
|
126
|
+
fs.renameSync(oldPath, newPath);
|
|
127
|
+
const appJsPath = path.join(projectDir, 'src/App.js');
|
|
128
|
+
if (fs.existsSync(appJsPath)) {
|
|
129
|
+
let appContent = fs.readFileSync(appJsPath, 'utf8');
|
|
130
|
+
appContent = appContent.replace(/from "\.\/pages\//g, 'from "./app/');
|
|
131
|
+
appContent = appContent.replace(/from '\.\/pages\//g, "from './app/");
|
|
132
|
+
fs.writeFileSync(appJsPath, appContent);
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.warn(' Architecture migration warning:', err.message);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log(' [4/8] Authenticating with Fleetbo Cloud...');
|
|
140
|
+
const keys = await fetchProjectKeys(bootstrapTokenArg);
|
|
141
|
+
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
142
|
+
|
|
143
|
+
console.log(' [5/8] Configuring environment...');
|
|
144
|
+
const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}
|
|
145
|
+
REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}
|
|
146
|
+
REACT_KEY_APP=${projectName}
|
|
147
|
+
REACT_APP_TESTER_EMAIL=${userEmailArg}
|
|
148
|
+
DANGEROUSLY_DISABLE_HOST_CHECK=true
|
|
149
|
+
WDS_SOCKET_PORT=0`;
|
|
150
|
+
|
|
151
|
+
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
152
|
+
|
|
153
|
+
console.log(' [6/8] Securing environment (adding .gitignore)...');
|
|
154
|
+
const gitignoreContent = `# Fleetbo Security
|
|
155
|
+
.env
|
|
156
|
+
.env.local
|
|
157
|
+
node_modules/
|
|
158
|
+
dist/
|
|
159
|
+
build/
|
|
160
|
+
.DS_Store
|
|
161
|
+
npm-debug.log*
|
|
162
|
+
yarn-debug.log*
|
|
163
|
+
yarn-error.log*
|
|
164
|
+
`;
|
|
165
|
+
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignoreContent, 'utf8');
|
|
166
|
+
|
|
167
|
+
console.log(' [7/8] Installing dependencies...');
|
|
168
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
169
|
+
|
|
170
|
+
// Installer cloudflared comme devDependency (nécessaire pour le tunnel)
|
|
171
|
+
execSync('npm install cloudflared --save-dev', { stdio: 'ignore' });
|
|
172
|
+
|
|
173
|
+
console.log(' [8/8] Finalizing setup...');
|
|
174
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
175
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
176
|
+
packageJson.name = projectName;
|
|
177
|
+
|
|
178
|
+
// Configuration pour utiliser le package NPM centralisé
|
|
179
|
+
packageJson.scripts = {
|
|
180
|
+
...packageJson.scripts,
|
|
181
|
+
"fleetbo": "npx fleetbo-cockpit-cli@latest",
|
|
182
|
+
"dev": "npx fleetbo-cockpit-cli@latest"
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
186
|
+
|
|
187
|
+
// Supprimer le dossier scripts local (car on utilise le CLI npm)
|
|
188
|
+
const scriptsDir = path.join(projectDir, 'scripts');
|
|
189
|
+
if (fs.existsSync(scriptsDir)) {
|
|
190
|
+
try {
|
|
191
|
+
fs.rmSync(scriptsDir, { recursive: true, force: true });
|
|
192
|
+
} catch (e) {
|
|
193
|
+
// Ignore si ça échoue
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.log('\n \x1b[32m✓ [Fleetbo] Project successfully created!\x1b[0m');
|
|
198
|
+
console.log(`\n Run: cd ${projectName} && npm run fleetbo`);
|
|
199
|
+
console.log('\n \x1b[32m✓ [Fleetbo] To start architecting with Alex, run: npm run fleetbo alex\x1b[0m');
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('\n Setup failed:', error.message);
|
|
204
|
+
if (fs.existsSync(projectDir)) {
|
|
205
|
+
try {
|
|
206
|
+
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
207
|
+
} catch(e) {}
|
|
208
|
+
}
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
572
213
|
setupProject();
|