genbox 1.0.201 → 1.0.203

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.
@@ -1157,7 +1157,7 @@ exports.createCommand = new commander_1.Command('create')
1157
1157
  // Wait for genbox to be ready with progress indicator
1158
1158
  const genboxId = genbox._id || genbox.id;
1159
1159
  const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genboxId, name, {
1160
- timeout: 180,
1160
+ timeout: 600,
1161
1161
  showProgress: true,
1162
1162
  });
1163
1163
  if (!waitResult.success) {
@@ -334,7 +334,7 @@ exports.newCommand = new commander_1.Command('new')
334
334
  // Wait for genbox to be ready with progress indicator
335
335
  const genboxId = genbox._id || genbox.id;
336
336
  const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genboxId, projectName, {
337
- timeout: 180,
337
+ timeout: 600,
338
338
  showProgress: true,
339
339
  });
340
340
  if (!waitResult.success) {
@@ -368,7 +368,7 @@ async function startGenbox(genbox) {
368
368
  });
369
369
  // Wait for genbox to be ready with progress indicator
370
370
  const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genbox.id, genbox.name, {
371
- timeout: 120,
371
+ timeout: 600,
372
372
  showProgress: true,
373
373
  });
374
374
  if (!waitResult.success) {
@@ -495,7 +495,16 @@ async function startSessionOnGenbox(provider, genbox) {
495
495
  const socketDir = getRemoteDtachSocketDir();
496
496
  const socketPath = `${socketDir}/${sessionName}.sock`;
497
497
  // Use projectPath from genbox if available, otherwise detect
498
- const knownProjectPath = targetGenbox.projectPath || '';
498
+ // For local Docker/VM, the workspace is mounted at /home/dev/workspace
499
+ let knownProjectPath = '';
500
+ if (genbox._isLocal) {
501
+ // Local genboxes mount workspace at /home/dev/workspace
502
+ knownProjectPath = '/home/dev/workspace';
503
+ }
504
+ else if (targetGenbox.projectPath) {
505
+ // Cloud genboxes use the path from API
506
+ knownProjectPath = targetGenbox.projectPath;
507
+ }
499
508
  const startScript = `#!/bin/bash
500
509
  # Set terminal environment for color support
501
510
  export TERM=xterm-256color
@@ -51,7 +51,7 @@ function estimateProgress(elapsedSecs, hasIp) {
51
51
  * Wait for a genbox to be ready with real-time progress indicator
52
52
  */
53
53
  async function waitForGenboxReady(genboxId, name, options = {}) {
54
- const { timeout = 180, interval = 2000, showProgress = true, onStatusChange, } = options;
54
+ const { timeout = 600, interval = 2000, showProgress = true, onStatusChange, } = options;
55
55
  const maxAttempts = Math.ceil((timeout * 1000) / interval);
56
56
  const startTime = Date.now();
57
57
  let lastStatus = '';
@@ -66,6 +66,8 @@ const unified_session_1 = require("./unified-session");
66
66
  const gemini_auth_1 = require("../utils/gemini-auth");
67
67
  const claude_auth_1 = require("../utils/claude-auth");
68
68
  const config_loader_1 = require("../config-loader");
69
+ const profile_resolver_1 = require("../profile-resolver");
70
+ const utils_1 = require("../utils");
69
71
  const random_name_1 = require("../random-name");
70
72
  const VM_SIZE_REQUIREMENTS = {
71
73
  small: {
@@ -721,7 +723,7 @@ async function createCloudGenbox(name, options = {}) {
721
723
  }
722
724
  // Wait for genbox to be ready with progress indicator
723
725
  const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genbox.id, name, {
724
- timeout: 180,
726
+ timeout: 600,
725
727
  showProgress: true,
726
728
  });
727
729
  if (!waitResult.success) {
@@ -892,7 +894,8 @@ echo 'dev ALL=(ALL) NOPASSWD:ALL' | tee /etc/sudoers.d/dev
892
894
  chmod 440 /etc/sudoers.d/dev
893
895
  mkdir -p /home/dev/.ssh
894
896
  echo '${publicKey}' > /home/dev/.ssh/authorized_keys
895
- chown -R dev:dev /home/dev/.ssh
897
+ chown -R dev:dev /home/dev/.ssh || true
898
+ chown dev:dev /home/dev || true
896
899
  chmod 700 /home/dev/.ssh
897
900
  chmod 600 /home/dev/.ssh/authorized_keys
898
901
  mkdir -p /run/sshd
@@ -916,13 +919,15 @@ mkdir -p /run/sshd
916
919
  const containerId = (0, child_process_1.execFileSync)('docker', dockerArgs, { encoding: 'utf8' }).trim();
917
920
  showProgress('Waiting for SSH', 0, 60);
918
921
  // Wait for SSH to be ready on the mapped port (localhost:sshPort)
922
+ // Use actual SSH test instead of just port check, as SSHD needs time to fully initialize
919
923
  let sshReady = false;
920
924
  const maxWaitTime = 120000; // 120 seconds (apt-get can be slow)
921
925
  const startWait = Date.now();
926
+ const sshKeyPath = (0, ssh_keys_1.getPrivateSshKeyPath)();
922
927
  while (Date.now() - startWait < maxWaitTime) {
923
928
  try {
924
- // Check if SSH is ready on the mapped port
925
- (0, child_process_1.execSync)(`nc -z -w 1 localhost ${sshPort}`, { stdio: 'pipe' });
929
+ // Actually test SSH connection, not just port availability
930
+ (0, child_process_1.execSync)(`ssh -p ${sshPort} -i "${sshKeyPath}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=3 dev@localhost "exit 0"`, { stdio: 'pipe', timeout: 10000 });
926
931
  sshReady = true;
927
932
  break;
928
933
  }
@@ -1004,6 +1009,66 @@ mkdir -p /run/sshd
1004
1009
  console.log(chalk_1.default.yellow(`\n Warning: Failed to install CLI automatically.`));
1005
1010
  console.log(chalk_1.default.dim(` You can install it manually after connecting.`));
1006
1011
  }
1012
+ // Inject credentials for the provider
1013
+ showProgress('Setting up credentials', 0, 90);
1014
+ try {
1015
+ if (provider === 'gemini') {
1016
+ const geminiCreds = (0, gemini_auth_1.getGeminiCredentials)();
1017
+ if (geminiCreds.found) {
1018
+ const isJsonCredentials = geminiCreds.credentials.trim().startsWith('{');
1019
+ if (isJsonCredentials) {
1020
+ // OAuth credentials - base64 encode to avoid shell escaping issues
1021
+ const credsBase64 = Buffer.from(geminiCreds.credentials).toString('base64');
1022
+ const settingsJson = JSON.stringify({
1023
+ autoAccept: true,
1024
+ security: { auth: { selectedType: "oauth-personal" }, disableYoloMode: false },
1025
+ tools: { sandbox: false, autoAccept: true }
1026
+ });
1027
+ const settingsBase64 = Buffer.from(settingsJson).toString('base64');
1028
+ (0, child_process_1.execFileSync)('ssh', [
1029
+ '-p', String(sshPort),
1030
+ '-i', keyPath,
1031
+ '-o', 'StrictHostKeyChecking=no',
1032
+ '-o', 'UserKnownHostsFile=/dev/null',
1033
+ '-o', 'LogLevel=ERROR',
1034
+ 'dev@localhost',
1035
+ `mkdir -p ~/.gemini && echo "${credsBase64}" | base64 -d > ~/.gemini/oauth_creds.json && chmod 600 ~/.gemini/oauth_creds.json && echo "${settingsBase64}" | base64 -d > ~/.gemini/settings.json`,
1036
+ ], { timeout: 30000, stdio: 'pipe' });
1037
+ console.log(chalk_1.default.green(` ✓ Gemini credentials configured`));
1038
+ }
1039
+ }
1040
+ }
1041
+ else if (provider === 'claude') {
1042
+ const claudeCreds = (0, claude_auth_1.getClaudeCredentials)();
1043
+ if (claudeCreds.found) {
1044
+ const isJsonCredentials = claudeCreds.credentials.trim().startsWith('{');
1045
+ if (isJsonCredentials) {
1046
+ // OAuth credentials - base64 encode
1047
+ const credsBase64 = Buffer.from(claudeCreds.credentials).toString('base64');
1048
+ const settingsJson = JSON.stringify({
1049
+ hasCompletedOnboarding: true,
1050
+ theme: "dark",
1051
+ preferredNotifChannel: "terminal"
1052
+ });
1053
+ const settingsBase64 = Buffer.from(settingsJson).toString('base64');
1054
+ (0, child_process_1.execFileSync)('ssh', [
1055
+ '-p', String(sshPort),
1056
+ '-i', keyPath,
1057
+ '-o', 'StrictHostKeyChecking=no',
1058
+ '-o', 'UserKnownHostsFile=/dev/null',
1059
+ '-o', 'LogLevel=ERROR',
1060
+ 'dev@localhost',
1061
+ `mkdir -p ~/.claude && echo "${credsBase64}" | base64 -d > ~/.claude/.credentials.json && chmod 600 ~/.claude/.credentials.json && echo "${settingsBase64}" | base64 -d > ~/.claude/settings.json`,
1062
+ ], { timeout: 30000, stdio: 'pipe' });
1063
+ console.log(chalk_1.default.green(` ✓ Claude credentials configured`));
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ catch (err) {
1069
+ // Non-fatal - credentials can be set up manually
1070
+ console.log(chalk_1.default.yellow(`\n Warning: Failed to configure credentials automatically.`));
1071
+ }
1007
1072
  showProgress('Creating session', 0, 95);
1008
1073
  // Create session record
1009
1074
  const session = await manager.createSession({
@@ -1075,79 +1140,49 @@ async function createLocalVmGenbox(name, provider = 'claude') {
1075
1140
  return { success: false, error: 'Resource check failed or user declined' };
1076
1141
  }
1077
1142
  const sizeReqs = VM_SIZE_REQUIREMENTS[selectedSize];
1078
- const startTime = Date.now();
1079
- // Progress stages with estimated percentages
1080
- const stages = [
1081
- { name: 'Launching VM', percent: 5 },
1082
- { name: 'Downloading image', percent: 40 },
1083
- { name: 'Configuring VM', percent: 70 },
1084
- { name: 'Finalizing', percent: 85 },
1085
- { name: 'Getting VM info', percent: 95 },
1086
- ];
1087
- // Progress update helper
1088
- const updateProgress = (stageIndex, extraPercent = 0) => {
1089
- const stage = stages[Math.min(stageIndex, stages.length - 1)];
1090
- const elapsed = Math.floor((Date.now() - startTime) / 1000);
1091
- const percent = Math.min(stage.percent + extraPercent, 99);
1092
- showProgress(stage.name, elapsed, percent);
1093
- };
1094
- try {
1095
- const manager = (0, unified_session_1.getUnifiedSessionManager)();
1096
- updateProgress(0);
1097
- // Get user's SSH public key
1098
- const publicKey = getPublicSshKey();
1099
- if (!publicKey) {
1100
- console.log(chalk_1.default.red('\nNo SSH public key found.'));
1101
- console.log(chalk_1.default.dim('Generate one with: ssh-keygen -t ed25519'));
1102
- return { success: false, error: 'No SSH key found' };
1103
- }
1104
- // Determine CLI package and helper based on provider
1105
- const cliPackage = provider === 'gemini' ? '@google/gemini-cli' : '@anthropic-ai/claude-code';
1106
- const cliCommand = provider === 'gemini' ? 'gemini' : 'claude';
1107
- const helperName = provider === 'gemini' ? 'gem' : 'cld';
1108
- const helperFlags = provider === 'gemini' ? '--sandbox=false' : '--dangerously-skip-permissions';
1109
- // Detect credentials based on provider
1110
- let credentialsScript = '';
1111
- if (provider === 'gemini') {
1112
- const geminiCreds = (0, gemini_auth_1.getGeminiCredentials)();
1113
- if (geminiCreds.found) {
1114
- console.log(chalk_1.default.green(`✓ Gemini credentials detected (${geminiCreds.source})`));
1115
- const isJsonCredentials = geminiCreds.credentials.trim().startsWith('{');
1116
- if (isJsonCredentials) {
1117
- // Escape single quotes in the credentials for use in shell heredoc
1118
- const escapedCreds = geminiCreds.credentials.replace(/'/g, "'\\''");
1119
- credentialsScript = `
1120
- # Setup Gemini credentials
1143
+ // Determine CLI package and helper based on provider
1144
+ const cliPackage = provider === 'gemini' ? '@google/gemini-cli' : '@anthropic-ai/claude-code';
1145
+ const cliCommand = provider === 'gemini' ? 'gemini' : 'claude';
1146
+ const helperName = provider === 'gemini' ? 'gem' : 'cld';
1147
+ const helperFlags = provider === 'gemini' ? '--sandbox=false' : '--dangerously-skip-permissions';
1148
+ // Get user's SSH public key (before progress starts)
1149
+ const publicKey = getPublicSshKey();
1150
+ if (!publicKey) {
1151
+ console.log(chalk_1.default.red('\nNo SSH public key found.'));
1152
+ console.log(chalk_1.default.dim('Generate one with: ssh-keygen -t ed25519'));
1153
+ return { success: false, error: 'No SSH key found' };
1154
+ }
1155
+ // Detect credentials based on provider (before progress starts to avoid visual artifacts)
1156
+ let credentialsScript = '';
1157
+ if (provider === 'gemini') {
1158
+ const geminiCreds = (0, gemini_auth_1.getGeminiCredentials)();
1159
+ if (geminiCreds.found) {
1160
+ console.log(chalk_1.default.green(`✓ Gemini credentials detected (${geminiCreds.source})`));
1161
+ const isJsonCredentials = geminiCreds.credentials.trim().startsWith('{');
1162
+ if (isJsonCredentials) {
1163
+ // Base64 encode the credentials to avoid YAML parsing issues
1164
+ const credsBase64 = Buffer.from(geminiCreds.credentials).toString('base64');
1165
+ const settingsJson = JSON.stringify({
1166
+ autoAccept: true,
1167
+ security: { auth: { selectedType: "oauth-personal" }, disableYoloMode: false },
1168
+ tools: { sandbox: false, autoAccept: true }
1169
+ });
1170
+ const settingsBase64 = Buffer.from(settingsJson).toString('base64');
1171
+ credentialsScript = `
1172
+ # Setup Gemini credentials (base64 encoded to avoid YAML issues)
1121
1173
  echo "=== Setting up Gemini CLI Authentication ==="
1122
1174
  mkdir -p /home/dev/.gemini
1123
- cat > /home/dev/.gemini/oauth_creds.json << 'GEMINI_CREDS_EOF'
1124
- ${geminiCreds.credentials}
1125
- GEMINI_CREDS_EOF
1175
+ echo "${credsBase64}" | base64 -d > /home/dev/.gemini/oauth_creds.json
1126
1176
  chmod 600 /home/dev/.gemini/oauth_creds.json
1127
- # Create settings.json for Gemini CLI (YOLO mode)
1128
- cat > /home/dev/.gemini/settings.json << 'GEMINI_SETTINGS_EOF'
1129
- {
1130
- "autoAccept": true,
1131
- "security": {
1132
- "auth": {
1133
- "selectedType": "oauth-personal"
1134
- },
1135
- "disableYoloMode": false
1136
- },
1137
- "tools": {
1138
- "sandbox": false,
1139
- "autoAccept": true
1140
- }
1141
- }
1142
- GEMINI_SETTINGS_EOF
1177
+ echo "${settingsBase64}" | base64 -d > /home/dev/.gemini/settings.json
1143
1178
  chmod 644 /home/dev/.gemini/settings.json
1144
1179
  chown -R dev:dev /home/dev/.gemini
1145
1180
  echo "Gemini CLI authentication configured."
1146
1181
  `;
1147
- }
1148
- else {
1149
- // API key
1150
- credentialsScript = `
1182
+ }
1183
+ else {
1184
+ // API key
1185
+ credentialsScript = `
1151
1186
  # Setup Gemini API key
1152
1187
  echo "=== Setting up Gemini CLI Authentication ==="
1153
1188
  mkdir -p /home/dev/.gemini
@@ -1155,53 +1190,50 @@ GEMINI_SETTINGS_EOF
1155
1190
  chown -R dev:dev /home/dev/.gemini
1156
1191
  echo "Gemini API key configured."
1157
1192
  `;
1158
- }
1159
- }
1160
- else {
1161
- console.log(chalk_1.default.yellow('\n⚠ Gemini CLI credentials not found locally'));
1162
- console.log(chalk_1.default.dim(` ${geminiCreds.error}`));
1163
- console.log(chalk_1.default.dim((0, gemini_auth_1.getGeminiAuthInstructions)()));
1164
- console.log(chalk_1.default.dim('\nGemini CLI will be installed but you\'ll need to authenticate on the genbox.'));
1165
1193
  }
1166
1194
  }
1167
- else if (provider === 'claude') {
1168
- const claudeCreds = (0, claude_auth_1.getClaudeCredentials)();
1169
- if (claudeCreds.found) {
1170
- console.log(chalk_1.default.green(`✓ Claude credentials detected (${claudeCreds.source})`));
1171
- const isJsonCredentials = claudeCreds.credentials.trim().startsWith('{');
1172
- if (isJsonCredentials) {
1173
- credentialsScript = `
1174
- # Setup Claude credentials
1195
+ else {
1196
+ console.log(chalk_1.default.yellow('\n⚠ Gemini CLI credentials not found locally'));
1197
+ console.log(chalk_1.default.dim(` ${geminiCreds.error}`));
1198
+ console.log(chalk_1.default.dim((0, gemini_auth_1.getGeminiAuthInstructions)()));
1199
+ console.log(chalk_1.default.dim('\nGemini CLI will be installed but you\'ll need to authenticate on the genbox.'));
1200
+ }
1201
+ }
1202
+ else if (provider === 'claude') {
1203
+ const claudeCreds = (0, claude_auth_1.getClaudeCredentials)();
1204
+ if (claudeCreds.found) {
1205
+ console.log(chalk_1.default.green(`✓ Claude credentials detected (${claudeCreds.source})`));
1206
+ const isJsonCredentials = claudeCreds.credentials.trim().startsWith('{');
1207
+ if (isJsonCredentials) {
1208
+ // Base64 encode the credentials to avoid YAML parsing issues
1209
+ const credsBase64 = Buffer.from(claudeCreds.credentials).toString('base64');
1210
+ const settingsJson = JSON.stringify({
1211
+ hasCompletedOnboarding: true,
1212
+ theme: "dark",
1213
+ preferredNotifChannel: "terminal"
1214
+ });
1215
+ const settingsBase64 = Buffer.from(settingsJson).toString('base64');
1216
+ const claudeJsonBase64 = Buffer.from(JSON.stringify({
1217
+ hasCompletedOnboarding: true,
1218
+ theme: "dark"
1219
+ })).toString('base64');
1220
+ credentialsScript = `
1221
+ # Setup Claude credentials (base64 encoded to avoid YAML issues)
1175
1222
  echo "=== Setting up Claude Code Authentication ==="
1176
1223
  mkdir -p /home/dev/.claude
1177
- cat > /home/dev/.claude/.credentials.json << 'CLAUDE_CREDS_EOF'
1178
- ${claudeCreds.credentials}
1179
- CLAUDE_CREDS_EOF
1224
+ echo "${credsBase64}" | base64 -d > /home/dev/.claude/.credentials.json
1180
1225
  chmod 600 /home/dev/.claude/.credentials.json
1181
- # Create settings to skip onboarding
1182
- cat > /home/dev/.claude/settings.json << 'CLAUDE_SETTINGS_EOF'
1183
- {
1184
- "hasCompletedOnboarding": true,
1185
- "theme": "dark",
1186
- "preferredNotifChannel": "terminal"
1187
- }
1188
- CLAUDE_SETTINGS_EOF
1226
+ echo "${settingsBase64}" | base64 -d > /home/dev/.claude/settings.json
1189
1227
  chmod 644 /home/dev/.claude/settings.json
1190
- # Also create ~/.claude.json for Claude Code v2.x
1191
- cat > /home/dev/.claude.json << 'CLAUDE_JSON_EOF'
1192
- {
1193
- "hasCompletedOnboarding": true,
1194
- "theme": "dark"
1195
- }
1196
- CLAUDE_JSON_EOF
1228
+ echo "${claudeJsonBase64}" | base64 -d > /home/dev/.claude.json
1197
1229
  chmod 644 /home/dev/.claude.json
1198
1230
  chown -R dev:dev /home/dev/.claude /home/dev/.claude.json
1199
1231
  echo "Claude Code authentication configured."
1200
1232
  `;
1201
- }
1202
- else {
1203
- // Raw token
1204
- credentialsScript = `
1233
+ }
1234
+ else {
1235
+ // Raw token
1236
+ credentialsScript = `
1205
1237
  # Setup Claude OAuth token
1206
1238
  echo "=== Setting up Claude Code Authentication ==="
1207
1239
  mkdir -p /home/dev/.claude
@@ -1209,15 +1241,42 @@ CLAUDE_JSON_EOF
1209
1241
  chown -R dev:dev /home/dev/.claude
1210
1242
  echo "Claude Code OAuth token configured."
1211
1243
  `;
1212
- }
1213
- }
1214
- else {
1215
- console.log(chalk_1.default.yellow('\n⚠ Claude Code credentials not found locally'));
1216
- console.log(chalk_1.default.dim(` ${claudeCreds.error}`));
1217
- console.log(chalk_1.default.dim((0, claude_auth_1.getClaudeAuthInstructions)()));
1218
- console.log(chalk_1.default.dim('\nClaude Code will be installed but you\'ll need to authenticate on the genbox.'));
1219
1244
  }
1220
1245
  }
1246
+ else {
1247
+ console.log(chalk_1.default.yellow('\n⚠ Claude Code credentials not found locally'));
1248
+ console.log(chalk_1.default.dim(` ${claudeCreds.error}`));
1249
+ console.log(chalk_1.default.dim((0, claude_auth_1.getClaudeAuthInstructions)()));
1250
+ console.log(chalk_1.default.dim('\nClaude Code will be installed but you\'ll need to authenticate on the genbox.'));
1251
+ }
1252
+ }
1253
+ // Now start progress tracking (after all console.log output is done)
1254
+ const startTime = Date.now();
1255
+ // Progress stages with estimated percentages
1256
+ const stages = [
1257
+ { name: 'Launching VM', percent: 5 },
1258
+ { name: 'Downloading image', percent: 40 },
1259
+ { name: 'Configuring VM', percent: 70 },
1260
+ { name: 'Finalizing', percent: 85 },
1261
+ { name: 'Getting VM info', percent: 95 },
1262
+ ];
1263
+ // Progress update helper
1264
+ const updateProgress = (stageIndex, extraPercent = 0) => {
1265
+ const stage = stages[Math.min(stageIndex, stages.length - 1)];
1266
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
1267
+ const percent = Math.min(stage.percent + extraPercent, 99);
1268
+ showProgress(stage.name, elapsed, percent);
1269
+ };
1270
+ try {
1271
+ const manager = (0, unified_session_1.getUnifiedSessionManager)();
1272
+ updateProgress(0);
1273
+ // Create helper script and encode as base64 to avoid YAML indentation issues
1274
+ const helperScript = `#!/bin/bash
1275
+ export NVM_DIR="/home/dev/.nvm"
1276
+ [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
1277
+ ${cliCommand} ${helperFlags} "$@"
1278
+ `;
1279
+ const helperScriptBase64 = Buffer.from(helperScript).toString('base64');
1221
1280
  // Create cloud-init config for VM setup
1222
1281
  // Mirrors cloud genbox setup: DNS fallback, nvm, node, CLI tools, credentials
1223
1282
  const cloudInitConfig = `#cloud-config
@@ -1263,13 +1322,8 @@ runcmd:
1263
1322
 
1264
1323
  echo "=== Adding ${helperName} helper command ==="
1265
1324
  '
1266
- # Create helper command (needs sudo, run as root)
1267
- cat > /usr/local/bin/${helperName} << 'HELPER_EOF'
1268
- #!/bin/bash
1269
- export NVM_DIR="/home/dev/.nvm"
1270
- [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
1271
- ${cliCommand} ${helperFlags} "$@"
1272
- HELPER_EOF
1325
+ # Create helper command (needs sudo, run as root) - use base64 to avoid YAML issues
1326
+ echo "${helperScriptBase64}" | base64 -d > /usr/local/bin/${helperName}
1273
1327
  chmod +x /usr/local/bin/${helperName}
1274
1328
  echo "${cliCommand} CLI installed. Use '${cliCommand}' or '${helperName}' command."
1275
1329
  ${credentialsScript}`;
@@ -1308,6 +1362,11 @@ ${credentialsScript}`;
1308
1362
  const stageProgress = (elapsed % 30) / 30 * 10;
1309
1363
  updateProgress(currentStage, Math.floor(stageProgress));
1310
1364
  }, 500);
1365
+ // Capture stderr for error messages
1366
+ let vmStderr = '';
1367
+ vmProcess.stderr.on('data', (data) => {
1368
+ vmStderr += data.toString();
1369
+ });
1311
1370
  // Wait for VM creation
1312
1371
  await new Promise((resolve, reject) => {
1313
1372
  vmProcess.on('close', (code) => {
@@ -1316,7 +1375,8 @@ ${credentialsScript}`;
1316
1375
  resolve();
1317
1376
  }
1318
1377
  else {
1319
- reject(new Error(`VM creation failed with code ${code}`));
1378
+ const errorMsg = vmStderr.trim() || `VM creation failed with code ${code}`;
1379
+ reject(new Error(errorMsg));
1320
1380
  }
1321
1381
  });
1322
1382
  vmProcess.on('error', (err) => {
@@ -1778,7 +1838,7 @@ async function runCreateWizard(options = {}) {
1778
1838
  }
1779
1839
  /**
1780
1840
  * Create a cloud genbox from a project with genbox.yaml
1781
- * Uses profile selection similar to `gb create`
1841
+ * Uses profile selection and ProfileResolver similar to `gb create`
1782
1842
  */
1783
1843
  async function createCloudGenboxFromProject(config, configLoader, options) {
1784
1844
  console.log('');
@@ -1787,10 +1847,11 @@ async function createCloudGenboxFromProject(config, configLoader, options) {
1787
1847
  console.log('');
1788
1848
  // Profile selection if available
1789
1849
  let selectedProfile;
1790
- if (config.profiles && Object.keys(config.profiles).length > 0 && !options.skipPrompts) {
1791
- const profileNames = Object.keys(config.profiles);
1850
+ const profiles = config.profiles || {};
1851
+ if (Object.keys(profiles).length > 0 && !options.skipPrompts) {
1852
+ const profileNames = Object.keys(profiles);
1792
1853
  const choices = profileNames.map(name => {
1793
- const profile = config.profiles[name];
1854
+ const profile = profiles[name];
1794
1855
  const apps = profile.apps ? profile.apps.join(', ') : 'default apps';
1795
1856
  const description = profile.description || `Apps: ${apps}`;
1796
1857
  return {
@@ -1811,19 +1872,85 @@ async function createCloudGenboxFromProject(config, configLoader, options) {
1811
1872
  }
1812
1873
  }
1813
1874
  // Get name
1814
- const defaultName = options.provider
1815
- ? `${options.provider}-${Date.now().toString(36)}`
1816
- : `genbox-${Date.now().toString(36)}`;
1817
- const name = options.name || await promptForName(defaultName);
1875
+ const name = options.name || await promptForName();
1818
1876
  if (!name) {
1819
1877
  console.log(chalk_1.default.dim('\nCancelled.'));
1820
1878
  return { success: false, error: 'Cancelled' };
1821
1879
  }
1822
- // Build payload with project config
1823
- // Note: This is a simplified version - full implementation would use ProfileResolver
1880
+ // Use ProfileResolver to resolve full configuration (including repos)
1881
+ const profileResolver = new profile_resolver_1.ProfileResolver(configLoader);
1882
+ const createOptions = {
1883
+ name,
1884
+ profile: selectedProfile,
1885
+ yes: options.skipPrompts,
1886
+ };
1887
+ const resolved = await profileResolver.resolve(config, createOptions);
1888
+ // Display resolved configuration
1889
+ console.log('');
1890
+ console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
1891
+ console.log(` ${chalk_1.default.bold('Name:')} ${name}`);
1892
+ console.log(` ${chalk_1.default.bold('Project:')} ${resolved.project.name}`);
1893
+ console.log(` ${chalk_1.default.bold('Size:')} ${resolved.size}`);
1894
+ if (selectedProfile) {
1895
+ console.log(` ${chalk_1.default.bold('Profile:')} ${selectedProfile}`);
1896
+ }
1897
+ if (resolved.apps.length > 0) {
1898
+ console.log(` ${chalk_1.default.bold('Apps:')} ${resolved.apps.map(a => a.name).join(', ')}`);
1899
+ }
1900
+ if (resolved.repos.length > 0) {
1901
+ console.log(` ${chalk_1.default.bold('Repos:')} ${resolved.repos.map(r => r.name).join(', ')}`);
1902
+ }
1903
+ console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
1904
+ console.log('');
1905
+ // Build repos payload
1906
+ const repos = {};
1907
+ for (const repo of resolved.repos) {
1908
+ repos[repo.name] = {
1909
+ url: repo.url,
1910
+ path: repo.path,
1911
+ branch: repo.branch || config.defaults?.branch || 'main',
1912
+ };
1913
+ }
1914
+ // Get git config for commits
1915
+ const gitConfig = (0, utils_1.getGitConfig)();
1916
+ // Load env vars for GIT_TOKEN
1917
+ const envVars = configLoader.loadEnvVars(process.cwd());
1918
+ // Build services map for routing (same as create.ts)
1919
+ const services = {};
1920
+ for (const app of resolved.apps) {
1921
+ if (app.port) {
1922
+ services[app.name] = { port: app.port, healthcheck: app.healthcheck };
1923
+ }
1924
+ }
1925
+ // Build full payload
1824
1926
  const payload = {
1825
1927
  profile: selectedProfile,
1826
- fromProject: true,
1928
+ workspace: resolved.project.name,
1929
+ size: resolved.size,
1930
+ repos,
1931
+ services, // Include services map for routing
1932
+ apps: resolved.apps.map(a => a.name),
1933
+ appConfigs: resolved.apps.map(a => ({
1934
+ name: a.name,
1935
+ path: a.path.startsWith('/') ? a.path : `${resolved.repos[0]?.path || '/home/dev'}/${a.path}`,
1936
+ type: a.type,
1937
+ port: a.port,
1938
+ framework: a.framework,
1939
+ runner: a.runner,
1940
+ docker: a.docker,
1941
+ healthcheck: a.healthcheck,
1942
+ dependsOn: a.dependsOn,
1943
+ commands: a.commands,
1944
+ })),
1945
+ infrastructure: resolved.infrastructure.map(i => ({
1946
+ name: i.name,
1947
+ type: i.type,
1948
+ mode: i.mode,
1949
+ })),
1950
+ database: resolved.database,
1951
+ gitToken: envVars.GIT_TOKEN,
1952
+ gitUserName: gitConfig.userName,
1953
+ gitUserEmail: gitConfig.userEmail,
1827
1954
  };
1828
1955
  // Add provider-specific CLI installation
1829
1956
  if (options.provider === 'claude') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.201",
3
+ "version": "1.0.203",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {