genbox 1.0.106 → 1.0.108

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.
@@ -1164,35 +1164,30 @@ async function setupProfiles(detected, environments) {
1164
1164
  }
1165
1165
  // Start with default profiles
1166
1166
  let profiles = { ...defaultProfiles };
1167
- // If there are existing profiles (including user-created ones), offer to retain them
1168
- if (Object.keys(existingProfiles).length > 0) {
1169
- console.log(chalk_1.default.dim('Found existing profiles in genbox.yaml:'));
1167
+ // If there are user-created profiles, offer to retain them
1168
+ if (Object.keys(userCreatedProfiles).length > 0) {
1169
+ console.log(chalk_1.default.dim('Found user-created profiles in genbox.yaml:'));
1170
1170
  console.log('');
1171
- // Display existing profiles with indication if they're user-created
1172
- for (const [name, profile] of Object.entries(existingProfiles)) {
1173
- const isUserCreated = !defaultProfileNames.has(name);
1174
- const label = isUserCreated ? chalk_1.default.yellow(' (user-created)') : chalk_1.default.dim(' (auto-generated)');
1175
- console.log(` ${chalk_1.default.cyan(name)}${label}`);
1171
+ // Display user-created profiles
1172
+ for (const [name, profile] of Object.entries(userCreatedProfiles)) {
1173
+ console.log(` ${chalk_1.default.cyan(name)}`);
1176
1174
  console.log(chalk_1.default.dim(` ${profile.description || 'No description'}`));
1177
1175
  console.log(chalk_1.default.dim(` Apps: ${profile.apps?.join(', ') || 'all'}`));
1178
1176
  console.log('');
1179
1177
  }
1180
- // Let user select which existing profiles to retain
1181
- const retainChoices = Object.entries(existingProfiles).map(([name, profile]) => {
1182
- const isUserCreated = !defaultProfileNames.has(name);
1183
- return {
1184
- name: `${name}${isUserCreated ? ' (user-created)' : ''} - ${profile.description || 'No description'}`,
1185
- value: name,
1186
- checked: isUserCreated, // User-created profiles are checked by default
1187
- };
1188
- });
1178
+ // Let user select which user-created profiles to retain
1179
+ const retainChoices = Object.entries(userCreatedProfiles).map(([name, profile]) => ({
1180
+ name: `${name} - ${profile.description || 'No description'}`,
1181
+ value: name,
1182
+ checked: true, // All user-created profiles are checked by default
1183
+ }));
1189
1184
  const retainedNames = await prompts.checkbox({
1190
- message: 'Select existing profiles to retain:',
1185
+ message: 'Select user-created profiles to retain:',
1191
1186
  choices: retainChoices,
1192
1187
  });
1193
- // Add retained profiles (they override defaults with same name)
1188
+ // Add retained user-created profiles
1194
1189
  for (const name of retainedNames) {
1195
- profiles[name] = existingProfiles[name];
1190
+ profiles[name] = userCreatedProfiles[name];
1196
1191
  }
1197
1192
  }
1198
1193
  if (Object.keys(profiles).length === 0) {
@@ -1204,8 +1199,8 @@ async function setupProfiles(detected, environments) {
1204
1199
  console.log(chalk_1.default.dim('Final profiles:'));
1205
1200
  console.log('');
1206
1201
  for (const [name, profile] of Object.entries(profiles)) {
1207
- const isRetained = existingProfiles[name] && !defaultProfileNames.has(name);
1208
- const label = isRetained ? chalk_1.default.yellow(' (retained)') : '';
1202
+ const isUserCreated = userCreatedProfiles[name] !== undefined;
1203
+ const label = isUserCreated ? chalk_1.default.yellow(' (user-created)') : '';
1209
1204
  console.log(` ${chalk_1.default.cyan(name)}${label}`);
1210
1205
  console.log(chalk_1.default.dim(` ${profile.description || 'No description'}`));
1211
1206
  console.log(chalk_1.default.dim(` Apps: ${profile.apps?.join(', ') || 'all'}`));
@@ -49,6 +49,12 @@ exports.listCommand = new commander_1.Command('list')
49
49
  }
50
50
  return;
51
51
  }
52
+ // Sort by createdAt (newest first)
53
+ genboxes.sort((a, b) => {
54
+ const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
55
+ const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
56
+ return dateB - dateA;
57
+ });
52
58
  // Calculate column widths
53
59
  const nameWidth = Math.max(12, ...genboxes.map(g => g.name.length + (options.all && g.project ? g.project.length + 3 : 0)));
54
60
  const statusWidth = 12;
@@ -398,80 +398,75 @@ exports.statusCommand = new commander_1.Command('status')
398
398
  console.log(chalk_1.default.dim(` (updated ${statsAge < 60 ? 'just now' : Math.floor(statsAge / 60) + 'm ago'})`));
399
399
  console.log('');
400
400
  }
401
- // Show Docker containers status
402
- const dockerStatus = sshExec(target.ipAddress, keyPath, 'docker ps --format "{{.Names}}\\t{{.Status}}" 2>/dev/null', 10);
403
- const hasDockerServices = dockerStatus && dockerStatus.trim();
401
+ // Show Docker containers status from DB (updated by heartbeat)
402
+ const dockerServices = systemStats?.dockerServices || [];
403
+ const hasDockerServices = dockerServices.length > 0;
404
404
  if (hasDockerServices) {
405
405
  console.log(chalk_1.default.blue('[INFO] === Docker Services ==='));
406
- console.log('NAMES\tSTATUS');
407
- console.log(dockerStatus);
406
+ console.log('NAME\t\t\tSTATUS');
407
+ for (const svc of dockerServices) {
408
+ const healthBadge = svc.health
409
+ ? (svc.health === 'healthy' ? chalk_1.default.green(' ✓') : chalk_1.default.red(` (${svc.health})`))
410
+ : '';
411
+ console.log(`${svc.name}\t${svc.status}${healthBadge}`);
412
+ }
408
413
  console.log('');
409
414
  }
410
- // Show PM2 processes (only if there are actual apps)
411
- const pm2Status = sshExec(target.ipAddress, keyPath, 'source ~/.nvm/nvm.sh 2>/dev/null; pm2 list 2>/dev/null || echo ""', 10);
412
- // Check for actual PM2 apps - empty tables don't contain status keywords
413
- const hasPm2Apps = pm2Status && pm2Status.trim() &&
414
- !pm2Status.includes('No process') &&
415
- (pm2Status.includes('online') || pm2Status.includes('stopped') ||
416
- pm2Status.includes('errored') || pm2Status.includes('launching'));
415
+ // Show PM2 processes from DB (updated by heartbeat)
416
+ const pm2Services = systemStats?.pm2Services || [];
417
+ const hasPm2Apps = pm2Services.length > 0;
417
418
  if (hasPm2Apps) {
418
419
  console.log(chalk_1.default.blue('[INFO] === PM2 Services ==='));
419
- console.log(pm2Status);
420
+ console.log('NAME\t\tSTATUS\t\tCPU\tMEM');
421
+ for (const app of pm2Services) {
422
+ const statusColor = app.status === 'online' ? chalk_1.default.green :
423
+ app.status === 'stopped' ? chalk_1.default.yellow : chalk_1.default.red;
424
+ console.log(`${app.name}\t\t${statusColor(app.status)}\t\t${app.cpu || 0}%\t${app.memory || 0}MB`);
425
+ }
420
426
  console.log('');
421
427
  }
422
- // Warn if no services are running and try to diagnose why
428
+ // Warn if no services are running and show setup errors from DB (no SSH needed)
423
429
  if (!hasDockerServices && !hasPm2Apps) {
424
430
  console.log(chalk_1.default.yellow('[WARN] No Docker or PM2 services are running!'));
425
- // First, check for saved errors from setup (fast - just read a file)
426
- const savedErrors = sshExec(target.ipAddress, keyPath, 'cat ~/.genbox-errors 2>/dev/null || echo ""', 10);
427
- if (savedErrors && savedErrors.trim()) {
428
- // Found saved errors from setup
431
+ // Check for setup errors stored in DB (sent by setup callback)
432
+ const setupErrors = target.setupErrors;
433
+ if (setupErrors && setupErrors.trim()) {
434
+ // Found errors from setup - display them
429
435
  console.log(chalk_1.default.red('[ERROR] Build failed during setup:'));
430
- console.log(savedErrors);
436
+ console.log(setupErrors);
431
437
  console.log('');
432
438
  console.log(chalk_1.default.dim(' Fix the error and run: cd ~/goodpass/api && docker compose up -d'));
433
439
  }
434
440
  else {
435
- // No saved errors - try docker compose build to diagnose (uses cache, fails fast)
436
- console.log(chalk_1.default.dim(' Checking for build errors...'));
437
- console.log('');
438
- const buildResult = sshExec(target.ipAddress, keyPath, 'cd ~/goodpass/api 2>/dev/null && docker compose build 2>&1 | tail -60 || echo "No compose file found"', 120);
439
- if (buildResult && buildResult.trim()) {
440
- // Check for common error patterns
441
- if (buildResult.includes('ERROR') || buildResult.includes('TS2') ||
442
- buildResult.includes('failed') || buildResult.includes('ELIFECYCLE') ||
443
- buildResult.includes('exit code') || buildResult.includes('did not complete successfully')) {
441
+ // No errors in DB - try SSH fallback for older genboxes
442
+ // First check ~/.genbox-errors file (fast)
443
+ const savedErrors = sshExec(target.ipAddress, keyPath, 'cat ~/.genbox-errors 2>/dev/null || echo ""', 10);
444
+ if (savedErrors && savedErrors.trim()) {
445
+ console.log(chalk_1.default.red('[ERROR] Build failed during setup:'));
446
+ console.log(savedErrors);
447
+ console.log('');
448
+ console.log(chalk_1.default.dim(' Fix the error and run: cd ~/goodpass/api && docker compose up -d'));
449
+ }
450
+ else {
451
+ // No saved errors - check if there's a docker compose error
452
+ console.log(chalk_1.default.dim(' Checking for build errors...'));
453
+ const buildResult = sshExec(target.ipAddress, keyPath, 'cd ~/goodpass/api 2>/dev/null && docker compose build 2>&1 | tail -60 || echo ""', 120);
454
+ if (buildResult && (buildResult.includes('ERROR') || buildResult.includes('TS2') ||
455
+ buildResult.includes('failed') || buildResult.includes('ELIFECYCLE'))) {
444
456
  console.log(chalk_1.default.red('[ERROR] Docker build failed:'));
445
- // Extract just the error portion
446
457
  const lines = buildResult.split('\n');
447
458
  const errorLines = lines.filter(line => line.includes('ERROR') || line.includes('TS2') || line.includes('TS1') ||
448
459
  line.includes('failed') || line.includes('exit code') ||
449
460
  line.includes('>') || line.includes('^') ||
450
- line.match(/^\s*\d+\s*\|/) // Source code lines with line numbers
451
- ).slice(-20);
452
- if (errorLines.length > 0) {
453
- console.log(errorLines.join('\n'));
454
- }
455
- else {
456
- // Show last 25 lines if no specific error pattern found
457
- console.log(lines.slice(-25).join('\n'));
458
- }
461
+ line.match(/^\s*\d+\s*\|/)).slice(-20);
462
+ console.log(errorLines.length > 0 ? errorLines.join('\n') : lines.slice(-25).join('\n'));
459
463
  console.log('');
460
464
  console.log(chalk_1.default.dim(' Fix the error and run: cd ~/goodpass/api && docker compose up -d'));
461
465
  }
462
- else if (buildResult.includes('Built') || buildResult.includes('FINISHED')) {
463
- // Build succeeded, services just need to start
464
- console.log(chalk_1.default.dim(' Build OK. Start services with: cd ~/goodpass/api && docker compose up -d'));
465
- }
466
466
  else {
467
- console.log(chalk_1.default.dim(buildResult));
468
- console.log('');
469
467
  console.log(chalk_1.default.dim(' Try: cd ~/goodpass/api && docker compose up -d'));
470
468
  }
471
469
  }
472
- else {
473
- console.log(chalk_1.default.dim(' Try: cd ~/goodpass/api && docker compose up -d'));
474
- }
475
470
  }
476
471
  console.log('');
477
472
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.106",
3
+ "version": "1.0.108",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {