genbox 1.0.17 → 1.0.19

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.
@@ -925,13 +925,42 @@ exports.initCommand = new commander_1.Command('init')
925
925
  console.log(chalk_1.default.dim(' • default_connection: production → uses PRODUCTION_API_URL'));
926
926
  console.log(chalk_1.default.dim(' • local/no default_connection → uses LOCAL_API_URL'));
927
927
  }
928
+ // CORS Configuration Warning
929
+ const hasBackendApps = scan.apps.some(a => a.type === 'backend' || a.type === 'api');
930
+ const hasFrontendApps = scan.apps.some(a => a.type === 'frontend');
931
+ if (hasBackendApps && hasFrontendApps) {
932
+ console.log('');
933
+ console.log(chalk_1.default.yellow('=== CORS Configuration Required ==='));
934
+ console.log(chalk_1.default.white('To use genbox environments, add .genbox.dev to your backend CORS config:'));
935
+ console.log('');
936
+ console.log(chalk_1.default.dim(' NestJS (main.ts):'));
937
+ console.log(chalk_1.default.cyan(` app.enableCors({`));
938
+ console.log(chalk_1.default.cyan(` origin: [/\\.genbox\\.dev$/, ...otherOrigins],`));
939
+ console.log(chalk_1.default.cyan(` credentials: true,`));
940
+ console.log(chalk_1.default.cyan(` });`));
941
+ console.log('');
942
+ console.log(chalk_1.default.dim(' Express:'));
943
+ console.log(chalk_1.default.cyan(` app.use(cors({ origin: /\\.genbox\\.dev$/ }));`));
944
+ console.log('');
945
+ console.log(chalk_1.default.dim(' Or use env var: CORS_ORIGINS=*.genbox.dev'));
946
+ console.log('');
947
+ console.log(chalk_1.default.red(' Without this, you will see CORS errors when accessing genbox environments.'));
948
+ }
928
949
  // Next steps
929
950
  console.log('');
930
951
  console.log(chalk_1.default.bold('Next steps:'));
931
952
  console.log(chalk_1.default.dim(` 1. Review and edit ${CONFIG_FILENAME}`));
932
- console.log(chalk_1.default.dim(` 2. Update ${ENV_FILENAME} to use API URL variables where needed`));
933
- console.log(chalk_1.default.dim(` 3. Run 'genbox profiles' to see available profiles`));
934
- console.log(chalk_1.default.dim(` 4. Run 'genbox create <name> --profile <profile>' to create an environment`));
953
+ if (hasBackendApps && hasFrontendApps) {
954
+ console.log(chalk_1.default.yellow(` 2. Add .genbox.dev to your backend CORS configuration`));
955
+ console.log(chalk_1.default.dim(` 3. Update ${ENV_FILENAME} to use API URL variables where needed`));
956
+ console.log(chalk_1.default.dim(` 4. Run 'genbox profiles' to see available profiles`));
957
+ console.log(chalk_1.default.dim(` 5. Run 'genbox create <name> --profile <profile>' to create an environment`));
958
+ }
959
+ else {
960
+ console.log(chalk_1.default.dim(` 2. Update ${ENV_FILENAME} to use API URL variables where needed`));
961
+ console.log(chalk_1.default.dim(` 3. Run 'genbox profiles' to see available profiles`));
962
+ console.log(chalk_1.default.dim(` 4. Run 'genbox create <name> --profile <profile>' to create an environment`));
963
+ }
935
964
  }
936
965
  catch (error) {
937
966
  if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
@@ -136,7 +136,11 @@ exports.scanCommand = new commander_1.Command('scan')
136
136
  .filter(([, app]) => app.type === 'frontend')
137
137
  .map(([name]) => name);
138
138
  if (frontendApps.length > 0) {
139
- const serviceUrls = scanEnvFilesForUrls(detected.apps, cwd);
139
+ let serviceUrls = scanEnvFilesForUrls(detected.apps, cwd);
140
+ // In interactive mode, let user select which URLs to configure
141
+ if (isInteractive && serviceUrls.length > 0) {
142
+ serviceUrls = await interactiveUrlSelection(serviceUrls);
143
+ }
140
144
  if (serviceUrls.length > 0) {
141
145
  detected.service_urls = serviceUrls;
142
146
  }
@@ -257,6 +261,36 @@ async function interactiveSelection(detected) {
257
261
  }
258
262
  return result;
259
263
  }
264
+ /**
265
+ * Interactive service URL selection
266
+ */
267
+ async function interactiveUrlSelection(serviceUrls) {
268
+ if (serviceUrls.length === 0) {
269
+ return [];
270
+ }
271
+ console.log('');
272
+ console.log(chalk_1.default.blue('=== Detected Service URLs ==='));
273
+ console.log(chalk_1.default.dim('These are local/development URLs found in frontend env files.'));
274
+ console.log(chalk_1.default.dim('Select which ones need staging URL equivalents.\n'));
275
+ // Show detected URLs
276
+ for (const svc of serviceUrls) {
277
+ console.log(` ${chalk_1.default.cyan(svc.base_url)}`);
278
+ console.log(chalk_1.default.dim(` Used by: ${svc.used_by.slice(0, 3).join(', ')}${svc.used_by.length > 3 ? ` +${svc.used_by.length - 3} more` : ''}`));
279
+ }
280
+ console.log();
281
+ // Let user select which URLs to configure
282
+ const urlChoices = serviceUrls.map(svc => ({
283
+ name: `${svc.base_url} (${svc.used_by.length} var${svc.used_by.length > 1 ? 's' : ''})`,
284
+ value: svc.base_url,
285
+ checked: true, // Default: include all
286
+ }));
287
+ const selectedUrls = await prompts.checkbox({
288
+ message: 'Select service URLs to configure for staging:',
289
+ choices: urlChoices,
290
+ });
291
+ // Filter to selected URLs
292
+ return serviceUrls.filter(svc => selectedUrls.includes(svc.base_url));
293
+ }
260
294
  /**
261
295
  * Scan env files in app directories for service URLs
262
296
  */
@@ -270,25 +304,37 @@ function scanEnvFilesForUrls(apps, rootDir) {
270
304
  const appDir = path.join(rootDir, app.path);
271
305
  // Find env file
272
306
  let envContent;
273
- let envSource;
274
307
  for (const pattern of envPatterns) {
275
308
  const envPath = path.join(appDir, pattern);
276
309
  if (fs.existsSync(envPath)) {
277
310
  envContent = fs.readFileSync(envPath, 'utf8');
278
- envSource = pattern;
279
311
  break;
280
312
  }
281
313
  }
282
314
  if (!envContent)
283
315
  continue;
284
- // Find all HTTP URLs
285
- const urlRegex = /^([A-Z_][A-Z0-9_]*)=["']?(https?:\/\/[a-zA-Z0-9_.-]+(?::\d+)?[^"'\s]*)["']?/gm;
286
- let match;
287
- while ((match = urlRegex.exec(envContent)) !== null) {
288
- const varName = match[1];
289
- const fullUrl = match[2];
316
+ // Process each line individually
317
+ for (const line of envContent.split('\n')) {
318
+ // Skip comments and empty lines
319
+ const trimmedLine = line.trim();
320
+ if (!trimmedLine || trimmedLine.startsWith('#'))
321
+ continue;
322
+ // Parse VAR=value format
323
+ const lineMatch = trimmedLine.match(/^([A-Z_][A-Z0-9_]*)=["']?(.+?)["']?$/);
324
+ if (!lineMatch)
325
+ continue;
326
+ const varName = lineMatch[1];
327
+ const value = lineMatch[2];
328
+ // Skip URLs with @ symbol (credentials, connection strings)
329
+ if (value.includes('@'))
330
+ continue;
331
+ // Check if it's a URL
332
+ const urlMatch = value.match(/^(https?:\/\/[a-zA-Z0-9_.-]+(?::\d+)?)/);
333
+ if (!urlMatch)
334
+ continue;
335
+ const baseUrl = urlMatch[1];
290
336
  // Extract hostname
291
- const hostMatch = fullUrl.match(/^https?:\/\/([a-zA-Z0-9_.-]+)/);
337
+ const hostMatch = baseUrl.match(/^https?:\/\/([a-zA-Z0-9_.-]+)/);
292
338
  if (!hostMatch)
293
339
  continue;
294
340
  const hostname = hostMatch[1];
@@ -298,11 +344,6 @@ function scanEnvFilesForUrls(apps, rootDir) {
298
344
  /^\d+\.\d+\.\d+\.\d+$/.test(hostname);
299
345
  if (!isLocalUrl)
300
346
  continue;
301
- // Extract base URL
302
- const baseMatch = fullUrl.match(/^(https?:\/\/[a-zA-Z0-9_.-]+(?::\d+)?)/);
303
- if (!baseMatch)
304
- continue;
305
- const baseUrl = baseMatch[1];
306
347
  // Add to map
307
348
  if (!serviceUrls.has(baseUrl)) {
308
349
  serviceUrls.set(baseUrl, { vars: new Set(), apps: new Set() });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {