genbox 1.0.79 → 1.0.81

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.
@@ -93,22 +93,43 @@ exports.forwardCommand = new commander_1.Command('forward')
93
93
  }
94
94
  // 3. Build port mappings from config or defaults
95
95
  const portMappings = [];
96
- // Try to load config for service ports
96
+ // Try to load config for service ports from genbox.yaml
97
97
  try {
98
98
  const config = (0, config_1.loadConfig)();
99
- if (config.services) {
100
- for (const [serviceName, serviceConfig] of Object.entries(config.services)) {
101
- portMappings.push({
102
- name: serviceName,
103
- localPort: serviceConfig.port,
104
- remotePort: serviceConfig.port,
105
- });
99
+ // Read ports from genbox.yaml apps config
100
+ if (config.apps) {
101
+ for (const [appName, appConfig] of Object.entries(config.apps)) {
102
+ const port = appConfig.port;
103
+ if (port && typeof port === 'number') {
104
+ portMappings.push({
105
+ name: appName,
106
+ localPort: port,
107
+ remotePort: port,
108
+ });
109
+ }
110
+ }
111
+ }
112
+ // Also add infrastructure ports from provides
113
+ if (config.provides) {
114
+ for (const [infraName, infraConfig] of Object.entries(config.provides)) {
115
+ if (infraConfig.port) {
116
+ portMappings.push({
117
+ name: infraName,
118
+ localPort: infraConfig.port,
119
+ remotePort: infraConfig.port,
120
+ });
121
+ }
106
122
  }
107
123
  }
108
124
  }
109
125
  catch {
110
- // No config, use default GoodPass ports
111
- portMappings.push({ name: 'Web App', localPort: 3000, remotePort: 3000 }, { name: 'Gateway', localPort: 3050, remotePort: 3050 }, { name: 'Auth', localPort: 3051, remotePort: 3051 }, { name: 'Notifications', localPort: 3052, remotePort: 3052 }, { name: 'Products', localPort: 3053, remotePort: 3053 }, { name: 'Partner API', localPort: 3054, remotePort: 3054 }, { name: 'Admin UI', localPort: 5173, remotePort: 5173 });
126
+ // No config found - require genbox.yaml
127
+ console.error(chalk_1.default.red('No genbox.yaml found. Run "gb init" first to configure your project.'));
128
+ return;
129
+ }
130
+ if (portMappings.length === 0) {
131
+ console.error(chalk_1.default.yellow('No apps with ports found in genbox.yaml. Nothing to forward.'));
132
+ return;
112
133
  }
113
134
  // Add custom ports if specified
114
135
  if (options.ports) {
@@ -762,8 +762,10 @@ async function setupEnvironmentsAndServiceUrls(detected, existingEnvValues) {
762
762
  }
763
763
  // Service URL detection and mapping
764
764
  const serviceUrlMappings = await setupServiceUrls(detected, environments, existingEnvValues);
765
- // Always add LOCAL_API_URL
766
- envVars['LOCAL_API_URL'] = 'http://localhost:3050';
765
+ // Always add LOCAL_API_URL - get port from detected api app
766
+ const apiApp = detected.apps?.['api'];
767
+ const apiPort = apiApp?.port || 3050;
768
+ envVars['LOCAL_API_URL'] = `http://localhost:${apiPort}`;
767
769
  return {
768
770
  environments: Object.keys(environments).length > 0 ? environments : undefined,
769
771
  serviceUrlMappings,
@@ -817,9 +819,11 @@ async function setupServiceUrls(detected, environments, existingEnvValues) {
817
819
  console.log(chalk_1.default.dim(`Mapping selected URLs to ${primaryEnv} environment:`));
818
820
  for (const svc of selectedServices) {
819
821
  const serviceInfo = getServiceInfoFromUrl(svc.base_url);
822
+ // Get API port from detected config for URL matching
823
+ const detectedApiPort = detected.apps?.['api']?.port;
820
824
  // Auto-map API/gateway URLs
821
825
  const isApiService = serviceInfo.name === 'gateway' ||
822
- svc.base_url.includes(':3050') ||
826
+ (detectedApiPort && svc.base_url.includes(`:${detectedApiPort}`)) ||
823
827
  svc.used_by.some(v => v.toLowerCase().includes('api'));
824
828
  let remoteUrl = '';
825
829
  if (isApiService && apiUrl) {
@@ -1799,13 +1803,17 @@ exports.initCommand = new commander_1.Command('init')
1799
1803
  const yamlContent = yaml.dump(config, { lineWidth: 120, noRefs: true, quotingType: '"' });
1800
1804
  fs_1.default.writeFileSync(configPath, yamlContent);
1801
1805
  console.log(chalk_1.default.green(`\n✔ Configuration saved to ${CONFIG_FILENAME}`));
1802
- const envContent = generateEnvFile(settings.projectName, detected, { ...gitEnvVars, LOCAL_API_URL: 'http://localhost:3050' }, []);
1806
+ // Get API port from detected config
1807
+ const fastPathApiApp = detected.apps?.['api'];
1808
+ const fastPathApiPort = fastPathApiApp?.port || 3050;
1809
+ const fastPathLocalApiUrl = `http://localhost:${fastPathApiPort}`;
1810
+ const envContent = generateEnvFile(settings.projectName, detected, { ...gitEnvVars, LOCAL_API_URL: fastPathLocalApiUrl }, []);
1803
1811
  fs_1.default.writeFileSync(path_1.default.join(rootDir, ENV_FILENAME), envContent);
1804
1812
  console.log(chalk_1.default.green(`✔ Created ${ENV_FILENAME}`));
1805
1813
  // Sync project to API if logged in
1806
1814
  if (config_store_1.ConfigStore.getToken()) {
1807
1815
  try {
1808
- const payload = configToSyncPayload(config, { ...gitEnvVars, LOCAL_API_URL: 'http://localhost:3050' });
1816
+ const payload = configToSyncPayload(config, { ...gitEnvVars, LOCAL_API_URL: fastPathLocalApiUrl });
1809
1817
  const result = await (0, api_1.syncProject)(payload);
1810
1818
  saveProjectCache(rootDir, {
1811
1819
  _id: result._id,
@@ -261,21 +261,42 @@ async function runSoftRebuild(options) {
261
261
  }
262
262
  }
263
263
  }
264
- // Step 6: Start Docker Compose services
264
+ // Step 6: Start Docker Compose services (only for apps with runner: docker + infrastructure)
265
265
  onStep?.('Starting Docker services...');
266
266
  for (const repo of resolved.repos) {
267
267
  const hasDockerCompose = await sshExec(ip, keyPath, `test -f ${repo.path}/docker-compose.yml -o -f ${repo.path}/docker-compose.yaml -o -f ${repo.path}/compose.yaml && echo yes || echo no`, 10);
268
268
  if (hasDockerCompose.output === 'yes') {
269
- log(`Starting Docker Compose in ${repo.name}...`, 'info');
270
- const composeResult = await sshExecStream(ip, keyPath, `cd ${repo.path} && docker compose up -d`, {
271
- onStdout: (line) => log(line, 'dim'),
272
- timeoutSecs: 180,
273
- });
274
- if (!composeResult.success) {
275
- log(`Warning: Docker Compose failed in ${repo.name}`, 'error');
269
+ // Get list of services to start: apps with runner: docker + infrastructure (provides)
270
+ const dockerServices = [];
271
+ // Add apps with runner: docker
272
+ for (const app of resolved.apps) {
273
+ const appConfig = config.apps[app.name];
274
+ if (appConfig?.runner === 'docker') {
275
+ const serviceName = appConfig.docker?.service || app.name;
276
+ dockerServices.push(serviceName);
277
+ }
278
+ }
279
+ // Add infrastructure services (provides: mongo, redis, etc.)
280
+ if (config.provides) {
281
+ for (const [name] of Object.entries(config.provides)) {
282
+ dockerServices.push(name);
283
+ }
284
+ }
285
+ if (dockerServices.length === 0) {
286
+ log(`No Docker services to start in ${repo.name}`, 'dim');
276
287
  }
277
288
  else {
278
- log(`✓ Docker services started in ${repo.name}`, 'success');
289
+ log(`Starting Docker Compose in ${repo.name} (${dockerServices.join(', ')})...`, 'info');
290
+ const composeResult = await sshExecStream(ip, keyPath, `cd ${repo.path} && docker compose up -d ${dockerServices.join(' ')}`, {
291
+ onStdout: (line) => log(line, 'dim'),
292
+ timeoutSecs: 600, // 10 minutes for Docker builds with npm/pnpm install
293
+ });
294
+ if (!composeResult.success) {
295
+ log(`Warning: Docker Compose failed in ${repo.name}`, 'error');
296
+ }
297
+ else {
298
+ log(`✓ Docker services started in ${repo.name}`, 'success');
299
+ }
279
300
  }
280
301
  }
281
302
  }
@@ -348,6 +369,24 @@ async function runSoftRebuild(options) {
348
369
  log(`Warning: Database restore failed: ${error.message}`, 'error');
349
370
  }
350
371
  }
372
+ // Step 7.5: Update localhost URLs in .env files to genbox URLs
373
+ {
374
+ const genboxName = genbox.name;
375
+ const workspace = genbox.workspace || resolved.project.name;
376
+ const baseDomain = 'genbox.dev';
377
+ log('Updating localhost URLs in .env files...', 'info');
378
+ // Replace localhost:{port} with genbox URL for each app based on its configured port
379
+ for (const app of resolved.apps) {
380
+ const appConfig = config.apps[app.name];
381
+ const port = appConfig?.port;
382
+ if (port) {
383
+ const genboxAppUrl = `https://${genboxName}.${app.name}.${workspace}.${baseDomain}`;
384
+ await sshExec(ip, keyPath, `find /home/dev -name ".env" -type f -exec sed -i 's|http://localhost:${port}|${genboxAppUrl}|g' {} \\;`, 30);
385
+ log(` localhost:${port} → ${genboxAppUrl}`, 'dim');
386
+ }
387
+ }
388
+ log(`✓ localhost URLs updated`, 'success');
389
+ }
351
390
  // Step 8: Start PM2 services (only for apps with runner: pm2 or unspecified)
352
391
  onStep?.('Starting application services...');
353
392
  for (const app of resolved.apps) {
@@ -378,12 +417,11 @@ async function runSoftRebuild(options) {
378
417
  }
379
418
  }
380
419
  else {
381
- // Use configured dev command, or fall back to dev script, then start script
382
- const devCommand = appConfig.commands?.dev;
383
- if (devCommand) {
384
- // Use the configured dev command
385
- log(`Starting ${app.name} with PM2 (${devCommand})...`, 'info');
386
- const pm2Result = await sshExecStream(ip, keyPath, `cd ${repoPath} && source ~/.nvm/nvm.sh && pm2 start "pnpm run ${devCommand}" --name ${app.name}`, {
420
+ // Check for dev script in package.json first (preferred), then start script
421
+ const hasDevScript = await sshExec(ip, keyPath, `grep -q '"dev"' ${repoPath}/package.json 2>/dev/null && echo yes || echo no`, 10);
422
+ if (hasDevScript.output === 'yes') {
423
+ log(`Starting ${app.name} with PM2 (pnpm dev)...`, 'info');
424
+ const pm2Result = await sshExecStream(ip, keyPath, `cd ${repoPath} && source ~/.nvm/nvm.sh && pm2 start "pnpm run dev" --name ${app.name}`, {
387
425
  onStdout: (line) => log(line, 'dim'),
388
426
  timeoutSecs: 60,
389
427
  });
@@ -392,11 +430,11 @@ async function runSoftRebuild(options) {
392
430
  }
393
431
  }
394
432
  else {
395
- // Check for dev script in package.json first, then start
396
- const hasDevScript = await sshExec(ip, keyPath, `grep -q '"dev"' ${repoPath}/package.json 2>/dev/null && echo yes || echo no`, 10);
397
- if (hasDevScript.output === 'yes') {
398
- log(`Starting ${app.name} with PM2 (pnpm dev)...`, 'info');
399
- const pm2Result = await sshExecStream(ip, keyPath, `cd ${repoPath} && source ~/.nvm/nvm.sh && pm2 start "pnpm run dev" --name ${app.name}`, {
433
+ // Fall back to start script
434
+ const hasStartScript = await sshExec(ip, keyPath, `grep -q '"start"' ${repoPath}/package.json 2>/dev/null && echo yes || echo no`, 10);
435
+ if (hasStartScript.output === 'yes') {
436
+ log(`Starting ${app.name} with PM2 (pnpm start)...`, 'info');
437
+ const pm2Result = await sshExecStream(ip, keyPath, `cd ${repoPath} && source ~/.nvm/nvm.sh && pm2 start "pnpm run start" --name ${app.name}`, {
400
438
  onStdout: (line) => log(line, 'dim'),
401
439
  timeoutSecs: 60,
402
440
  });
@@ -404,20 +442,6 @@ async function runSoftRebuild(options) {
404
442
  log(`✓ Started ${app.name}`, 'success');
405
443
  }
406
444
  }
407
- else {
408
- // Fall back to start script
409
- const hasStartScript = await sshExec(ip, keyPath, `grep -q '"start"' ${repoPath}/package.json 2>/dev/null && echo yes || echo no`, 10);
410
- if (hasStartScript.output === 'yes') {
411
- log(`Starting ${app.name} with PM2 (pnpm start)...`, 'info');
412
- const pm2Result = await sshExecStream(ip, keyPath, `cd ${repoPath} && source ~/.nvm/nvm.sh && pm2 start "pnpm run start" --name ${app.name}`, {
413
- onStdout: (line) => log(line, 'dim'),
414
- timeoutSecs: 60,
415
- });
416
- if (pm2Result.success) {
417
- log(`✓ Started ${app.name}`, 'success');
418
- }
419
- }
420
- }
421
445
  }
422
446
  }
423
447
  }
package/dist/index.js CHANGED
File without changes
@@ -539,9 +539,13 @@ class ProfileResolver {
539
539
  const apiDep = app.dependencies['api'];
540
540
  if (apiDep) {
541
541
  if (apiDep.mode === 'local') {
542
- env['API_URL'] = 'http://localhost:3050';
543
- env['VITE_API_URL'] = 'http://localhost:3050';
544
- env['NEXT_PUBLIC_API_URL'] = 'http://localhost:3050';
542
+ // Get API port from config instead of hardcoding
543
+ const apiAppConfig = config.apps?.['api'];
544
+ const apiPort = (apiAppConfig?.port && typeof apiAppConfig.port === 'number') ? apiAppConfig.port : 3050;
545
+ const localApiUrl = `http://localhost:${apiPort}`;
546
+ env['API_URL'] = localApiUrl;
547
+ env['VITE_API_URL'] = localApiUrl;
548
+ env['NEXT_PUBLIC_API_URL'] = localApiUrl;
545
549
  }
546
550
  else if (apiDep.url) {
547
551
  env['API_URL'] = apiDep.url;
@@ -555,7 +559,10 @@ class ProfileResolver {
555
559
  if (hasBackendApps) {
556
560
  // Add database URL
557
561
  if (database.mode === 'local') {
558
- env['MONGODB_URI'] = `mongodb://localhost:27017/${config.project.name}`;
562
+ // Get MongoDB port from provides config instead of hardcoding
563
+ const mongoConfig = config.provides?.['mongodb'];
564
+ const mongoPort = mongoConfig?.port || 27017;
565
+ env['MONGODB_URI'] = `mongodb://localhost:${mongoPort}/${config.project.name}`;
559
566
  }
560
567
  else if (database.url) {
561
568
  env['MONGODB_URI'] = database.url;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.79",
3
+ "version": "1.0.81",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {