create-fleetbo-project 1.2.40 → 1.2.42

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.
@@ -31,12 +31,13 @@ const PORT = 3000;
31
31
  const args = process.argv.slice(2);
32
32
  const command = args[0];
33
33
 
34
+ process.env.DOTENV_SILENT = 'true';
34
35
  const envPath = path.join(process.cwd(), '.env');
35
36
  if (!fs.existsSync(envPath)) {
36
37
  console.error('\\\\x1b[31m%s\\\\x1b[0m', '❌ Error: Configuration file (.env) not found.');
37
38
  process.exit(1);
38
39
  }
39
- dotenv.config({ path: envPath });
40
+ dotenv.config({ path: envPath, quiet: true });
40
41
 
41
42
  const projectId = process.env.REACT_APP_ENTERPRISE_ID;
42
43
  const keyApp = process.env.REACT_KEY_APP;
@@ -63,24 +64,29 @@ if (!projectId) {
63
64
  process.exit(1);
64
65
  }
65
66
 
66
- const injectRouteIntoAppJs = (pageName) => {
67
+ const injectRouteIntoAppJs = (pageName, subPath = '') => {
67
68
  if (!fs.existsSync(APP_JS_PATH)) return false;
68
69
  let content = fs.readFileSync(APP_JS_PATH, 'utf8');
69
- if (!content.includes('// FLEETBO_IMPORTS') || !content.includes('{/* FLEETBO_ROUTES */}')) return false;
70
+
71
+ const importAnchor = '// FLEETBO_MORE_IMPORTS';
72
+ const alexRouteAnchor = '{/* FLEETBO_DYNAMIC ROUTES */}';
73
+
74
+ if (!content.includes(alexRouteAnchor)) return false;
70
75
 
71
- const importLine = \`import \\\${pageName} from './pages/\\\${pageName}';\`;
72
- const routeLine = \`<Route path="/\\\${pageName.toLowerCase()}" element=<\\\${pageName} /> />\`;
76
+ const pathPrefix = subPath ? \\\`\\\${subPath}/\\\` : '';
77
+ const importLine = \\\`import \\\${pageName} from './pages/\\\${pathPrefix}\\\${pageName}';\\\`;
78
+ const routeLine = \\\`<Route path="/\\\${pathPrefix.toLowerCase()}\\\${pageName.toLowerCase()}" element=<\\\${pageName} /> />\\\`;
73
79
 
74
80
  let injected = false;
75
- if (!content.includes(importLine) && !content.includes(\`import \\\${pageName} from\`)) {
76
- content = content.replace('// FLEETBO_IMPORTS', \`\\\${importLine}\\\\n// FLEETBO_IMPORTS\`);
81
+ if (!content.includes(importLine)) {
82
+ content = content.replace(importAnchor, \\\`\\\${importLine}\\\\n\\\${importAnchor}\\\`);
77
83
  injected = true;
78
84
  }
79
- if (!content.includes(routeLine) && !content.includes(\`path="/\\\${pageName.toLowerCase()}"\`)) {
80
- const anchor = content.includes('{/* FLEETBO_ROUTES */}') ? '{/* FLEETBO_ROUTES */}' : '// FLEETBO_ROUTES';
81
- content = content.replace(anchor, \`\\\${routeLine}\\\\n \\\${anchor}\`);
85
+ if (!content.includes(routeLine)) {
86
+ content = content.replace(alexRouteAnchor, \\\`\\\${routeLine}\\\\n \\\${alexRouteAnchor}\\\`);
82
87
  injected = true;
83
88
  }
89
+
84
90
  if (injected) fs.writeFileSync(APP_JS_PATH, content);
85
91
  return injected;
86
92
  };
@@ -89,7 +95,7 @@ const showEnergyTransfer = async () => {
89
95
  const width = 30;
90
96
  for (let i = 0; i <= width; i++) {
91
97
  const dots = "█".repeat(i); const empty = "░".repeat(width - i);
92
- process.stdout.write(\`\\\\r \\\\x1b[32m⚡ Alex Energy Sync:\\\\x1b[0m [\\\${dots}\\\${empty}] \\\${Math.round((i / width) * 100)}%\`);
98
+ process.stdout.write(\\\`\\\\r \\\\x1b[32m⚡ Alex Energy Sync:\\\\x1b[0m [\\\${dots}\\\${empty}] \\\${Math.round((i / width) * 100)}%\\\`);
93
99
  await new Promise(r => setTimeout(r, 45));
94
100
  }
95
101
  process.stdout.write('\\\\n');
@@ -100,68 +106,88 @@ if (command === 'alex') {
100
106
  const initialPrompt = args.slice(1).join(' ');
101
107
 
102
108
  const processAlexRequest = async (prompt) => {
103
- process.stdout.write('\\\\n\\\\x1b[33m🧠 Alex is architecting...\\\\x1b[0m\\\\r');
109
+ process.stdout.write('\\\\x1b[33m🧠 Alex is thinking...\\\\x1b[0m');
104
110
  try {
105
111
  const result = await axios.post(ALEX_ENGINE_URL, { prompt, projectType: 'android' }, {
106
112
  headers: { 'x-project-id': projectId }
107
113
  });
108
- const aiData = result.data.data || result.data;
109
- process.stdout.write(' '.repeat(30) + '\\\\r');
114
+
115
+ const aiData = result.data;
116
+ process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
117
+
118
+ if (aiData.status === 'quota_exceeded') {
119
+ console.log(\\\`\\\\n\\\\x1b[31m⛔ ENGINE OUT OF FUEL:\\\\x1b[0m \\\${aiData.message}\\\`);
120
+ return;
121
+ }
122
+
123
+ if (aiData.status === 'success' || aiData.status === 'message') {
124
+ console.log('');
125
+ console.log(\\\`\\\\x1b[32mAlex ❯\\\\x1b[0m \\\${aiData.message || "I'm ready."}\\\`);
126
+
127
+ if (aiData.remainingTokens !== undefined) {
128
+ const remaining = aiData.remainingTokens;
129
+ const limit = aiData.limit || 2500;
130
+ const percent = Math.round((remaining / limit) * 100);
131
+ const energyColor = percent > 30 ? '\\\\x1b[32m' : '\\\\x1b[31m';
132
+ console.log(\\\`\\\\x1b[36m📊 Energy:\\\\x1b[0m \\\${energyColor}\\\${percent}%\\\\x1b[0m (\\\${remaining}/\\\${limit} tokens left)\\\`);
133
+ }
134
+ }
110
135
 
111
136
  if (aiData.status === 'success' && aiData.moduleData) {
112
- const { fileName, code, mockFileName, mockCode } = aiData.moduleData;
137
+ const { fileName, code, mockFileName, mockCode, moduleName } = aiData.moduleData;
138
+ console.log(\\\` \\\\x1b[90m⚙️ Architecting: \\\${moduleName}\\\\x1b[0m\\\`);
139
+
113
140
  const writeFile = (dir, name, content) => {
114
141
  const fullPath = path.join(process.cwd(), dir);
115
142
  const filePath = path.join(fullPath, name);
116
143
  if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
117
-
118
- if (fs.existsSync(filePath)) {
119
- const backupPath = filePath.replace(/(\\\\.jsx|\\\\.kt|\\\\.swift)$/, '.original\\\$1');
120
- fs.copyFileSync(filePath, backupPath);
121
- fs.writeFileSync(filePath, content);
122
- console.log(\` ✅ \\\\x1b[33m[Updated]\\\\x1b[0m \\\${dir}\\\${name} \\\\x1b[90m(Backup created)\\\\x1b[0m\`);
123
- } else {
124
- fs.writeFileSync(filePath, content);
125
- console.log(\` ✅ \\\\x1b[32m[Created]\\\\x1b[0m \\\${dir}\\\${name}\`);
126
- }
144
+ fs.writeFileSync(filePath, content);
145
+ console.log(\\\` ✅ \\\\x1b[32m[Written]\\\\x1b[0m \\\${dir}\\\${name}\\\`);
127
146
  };
128
- console.log('');
129
- if (code && fileName && (fileName.endsWith('.kt') || fileName.endsWith('.swift'))) {
130
- writeFile(fileName.endsWith('.kt') ? 'public/native/android/' : 'public/native/ios/', fileName, code);
147
+
148
+ if (code && fileName) {
149
+ const folder = fileName.endsWith('.kt') ? 'public/native/android/' : 'src/pages/';
150
+ writeFile(folder, fileName, code);
151
+ if (fileName.endsWith('.jsx')) injectRouteIntoAppJs(fileName.replace('.jsx', ''));
131
152
  }
132
153
  if (mockCode && mockFileName) {
133
154
  writeFile('src/pages/mocks/', mockFileName, mockCode);
134
- writeFile('src/pages/mocks/', 'Quick.js', mockCode);
135
- }
136
- if (code && fileName && fileName.endsWith('.jsx') && !mockCode) {
137
- const pageName = fileName.replace('.jsx', '');
138
- writeFile('src/pages/', fileName, code);
139
- injectRouteIntoAppJs(pageName);
155
+ writeFile('src/pages/mocks/', 'Quick.jsx', mockCode);
140
156
  }
141
- console.log('\\\\n\\\\x1b[32mAlex ❯\\\\x1b[0m ' + aiData.message);
142
157
  }
143
- } catch (error) { console.error('\\\\n\\\\x1b[31m❌ Alex Error:\\\\x1b[0m ' + error.message); }
158
+ } catch (error) {
159
+ process.stdout.write('\\\\r' + ' '.repeat(50) + '\\\\r');
160
+ console.error('\\\\n\\\\x1b[31m❌ Alex Error:\\\\x1b[0m ' + (error.response?.data?.message || error.message));
161
+ }
144
162
  };
145
163
 
146
164
  const startAlexSession = async () => {
147
- process.stdout.write('\\\\n\\\\x1b[33m🛡️ Alex is checking runtime state...\\\\x1b[0m\\\\r');
148
- try {
149
- const validation = await axios.post(ALEX_ENGINE_URL, { validateProject: true, checkNetwork: true, projectKey: keyApp, testerEmail: testerEmail }, { headers: { 'x-project-id': projectId } });
150
- if (!validation.data.exists || !validation.data.isRunning) {
151
- console.error('\\\\n\\\\x1b[31m⚠️ ENGINE OFFLINE:\\\\x1b[0m Alex needs a live Uplink.');
152
- process.exit(1);
153
- }
154
- process.stdout.write(' '.repeat(60) + '\\\\r');
155
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: '\\\\x1b[32mAlex ❯ \\\\x1b[0m' });
156
- console.log('\\\\n\\\\x1b[32m🤖 Alex is now online.\\\\x1b[0m Pilot safely.');
165
+ process.stdout.write('\\\\x1b[33m🛡️ Alex is checking runtime state...\\\\x1b[0m\\\\r');
166
+ let attempts = 0; const maxAttempts = 5; let isReady = false;
167
+ while (attempts < maxAttempts && !isReady) {
168
+ try {
169
+ const validation = await axios.post(ALEX_ENGINE_URL, { prompt: "ping", validateProject: true, checkNetwork: true }, { headers: { 'x-project-id': projectId }, timeout: 5000 });
170
+ if (validation.data?.isRunning) { isReady = true; break; }
171
+ attempts++; if (attempts < maxAttempts) await new Promise(r => setTimeout(r, 2000));
172
+ } catch (error) { attempts++; await new Promise(r => setTimeout(r, 2000)); }
173
+ }
174
+ if (!isReady) {
175
+ console.error('\\\\n\\\\x1b[31m⚠️ ENGINE OFFLINE:\\\\x1b[0m Start Fleetbo runtime first: "npm run fleetbo" ');
176
+ process.exit(1);
177
+ }
178
+ process.stdout.write(' '.repeat(60) + '\\\\r');
179
+ console.log('\\\\n\\\\x1b[32m🤖 Alex is now online.\\\\x1b[0m');
180
+ console.log('\\\\x1b[32mAlex ❯\\\\x1b[0m Welcome! I am Alex, your architect. What are we building today?\\\\n');
181
+ const userName = testerEmail ? testerEmail.split('@')[0] : 'Dev';
182
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: \\\`\\\\x1b[34m\\\${userName} ❯ \\\\x1b[0m\\\` });
183
+ rl.prompt();
184
+ rl.on('line', async (line) => {
185
+ if (['exit', 'quit'].includes(line.trim().toLowerCase())) { console.log('\\\\n\\\\x1b[90m👋 Alex session closed.\\\\x1b[0m'); rl.close(); return; }
186
+ if (line.trim()) { await processAlexRequest(line.trim()); console.log(''); }
157
187
  rl.prompt();
158
- rl.on('line', async (line) => {
159
- if (['exit', 'quit', 'bye'].includes(line.trim().toLowerCase())) { rl.close(); return; }
160
- if (line.trim()) await processAlexRequest(line.trim());
161
- rl.prompt();
162
- }).on('close', () => { console.log('\\\\n👋 Alex going offline.'); process.exit(0); });
163
- } catch (error) { process.exit(1); }
188
+ }).on('close', () => { process.exit(0); });
164
189
  };
190
+
165
191
  if (!initialPrompt || initialPrompt === '?') startAlexSession();
166
192
  else processAlexRequest(initialPrompt);
167
193
  return;
@@ -192,21 +218,19 @@ if (command === 'deploy') {
192
218
 
193
219
  const GENERATOR_COMMANDS = ['page', 'g', 'generate', 'android', 'ios'];
194
220
  if (GENERATOR_COMMANDS.includes(command)) {
195
- try { require('./page.js'); } catch (error) { process.exit(1); }
221
+ try { require('./page.js'); } catch (e) { console.error(e.message); process.exit(1); }
196
222
  return;
197
223
  }
198
224
 
199
225
  const NULL_DEV = process.platform === 'win32' ? '>nul 2>&1' : '2>/dev/null';
200
-
201
226
  function killProcessOnPort(port) {
202
227
  try {
203
228
  if (process.platform !== 'win32') {
204
- const pid = execSync(\`lsof -ti:\\\${port} \\\${NULL_DEV}\`).toString().trim();
205
- if (pid) { execSync(\`kill -9 \\\${pid.split('\\\\n').join(' ')} \\\${NULL_DEV}\`); }
229
+ const pid = execSync(\\\`lsof -ti:\\\${port} \\\${NULL_DEV}\\\`).toString().trim();
230
+ if (pid) execSync(\\\`kill -9 \\\${pid.split('\\\\n').join(' ')} \\\${NULL_DEV}\\\`);
206
231
  }
207
232
  } catch (e) {}
208
233
  }
209
-
210
234
  function killNetworkService() {
211
235
  try {
212
236
  const cmd = process.platform === 'win32' ? 'taskkill /IM cloudflared.exe /F' : 'pkill cloudflared';
@@ -216,14 +240,10 @@ function killNetworkService() {
216
240
 
217
241
  let isExiting = false;
218
242
  async function cleanupAndExit(code = 0) {
219
- if (isExiting) return;
220
- isExiting = true;
221
- try {
222
- await axios.post(UPDATE_NETWORK_URL, { keyApp: keyApp, networkUrl: '', tester: testerEmail });
223
- } catch (e) {}
224
- killNetworkService();
225
- killProcessOnPort(PORT);
226
- process.exit(code);
243
+ if (isExiting) return; isExiting = true;
244
+ console.log('\\\\n[Fleetbo] 🛑 Stopping environment...');
245
+ try { await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl: '', tester: testerEmail }); } catch (e) {}
246
+ killNetworkService(); killProcessOnPort(PORT); process.exit(code);
227
247
  }
228
248
  process.on('SIGINT', () => cleanupAndExit(0));
229
249
  process.on('SIGTERM', () => cleanupAndExit(0));
@@ -231,30 +251,37 @@ process.on('SIGTERM', () => cleanupAndExit(0));
231
251
  async function syncFirebase(keyApp, networkUrl, testerEmail) {
232
252
  try {
233
253
  await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl, tester: testerEmail });
234
- console.log(\`\\\\n[Fleetbo] ✅ Uplink Status: Online for \\\${testerEmail}\`);
235
- console.log(\`[Fleetbo] 🚀 Fleetbo Studio: https://fleetbo.io/studio/\\\${keyApp}\`);
236
- } catch (err) { console.error(\`[Fleetbo] ⚠️ Sync Error: \\\${err.message}\`); }
254
+ console.log(\\\`\\\\n\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\\\`);
255
+ console.log('\\\\x1b[32m[Fleetbo] GO GO GO ! FLEETBO STUDIO IS READY \\\\x1b[0m');
256
+ console.log(\\\`\\\\x1b[32m[Fleetbo] Link: https://fleetbo.io/studio/\\\${keyApp}\\\\x1b[0m\\\`);
257
+ console.log('\\\\x1b[32m[Fleetbo] You can now start coding and previewing in Studio. 🚀\\\\x1b[0m\\\`);
258
+ console.log(\\\`\\\\x1b[32m[Fleetbo]\\\\x1b[0m -------------------------------------------------------------\\\`);
259
+ } catch (err) { console.error(\\\`[Fleetbo] ❌ Sync Error: \\\${err.message}\\\`); }
237
260
  }
238
261
 
239
262
  async function runDevEnvironment() {
240
- killNetworkService();
241
- killProcessOnPort(PORT);
263
+ console.log(\\\`[Fleetbo] 🛡️ Initializing Dev Environment...\\\`);
264
+ killNetworkService(); killProcessOnPort(PORT);
242
265
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
243
266
  const devServer = spawn(npmCmd, ['start'], { stdio: ['ignore', 'pipe', 'pipe'], shell: true, env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() } });
267
+ devServer.stdout.pipe(process.stdout); devServer.stderr.pipe(process.stderr);
268
+ let connectionStarted = false;
244
269
  devServer.stdout.on('data', (data) => {
245
270
  const output = data.toString();
246
- if (output.includes('Local:') || output.includes('Compiled successfully')) {
271
+ if (!connectionStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
272
+ connectionStarted = true;
273
+ console.log('\\\\n[Fleetbo] ---------------------------------------------------');
274
+ console.log(\\\`[Fleetbo] 🔗 Establishing Secure Uplink...\\\\n[Fleetbo] 🛑 DO NOT open the Fleetbo Studio yet.\\\\n[Fleetbo] ⏳ Please wait green message...\\\\n[Fleetbo] ---------------------------------------------------\\\`);
247
275
  const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
248
- const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \`http://localhost:\\\${PORT}\`], { shell: true });
276
+ const uplink = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \\\`http://localhost:\\\${PORT}\\\`], { shell: true });
249
277
  uplink.stderr.on('data', (chunk) => {
250
- const match = log.match(/https:[//][//][a-zA-Z0-9-]+[.]trycloudflare[.]com/);
278
+ const match = chunk.toString().match(/https:\\\\/\\\\/[a-zA-Z0-9-]+\\\\.trycloudflare\\\\.com/);
251
279
  if (match) syncFirebase(keyApp, match[0], testerEmail);
252
280
  });
253
281
  }
254
282
  });
255
283
  }
256
- runDevEnvironment();
257
- `;
284
+ runDevEnvironment();`;
258
285
 
259
286
 
260
287
  const args = process.argv.slice(2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fleetbo-project",
3
- "version": "1.2.40",
3
+ "version": "1.2.42",
4
4
  "description": "Creates a new Fleetbo project.",
5
5
  "main": "install-react-template.js",
6
6
  "bin": {