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.
package/dist/commands/create.js
CHANGED
|
@@ -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:
|
|
1160
|
+
timeout: 600,
|
|
1161
1161
|
showProgress: true,
|
|
1162
1162
|
});
|
|
1163
1163
|
if (!waitResult.success) {
|
package/dist/commands/new.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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
|
-
//
|
|
925
|
-
(0, child_process_1.execSync)(`
|
|
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
|
-
|
|
1079
|
-
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
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
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1791
|
-
|
|
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 =
|
|
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
|
|
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
|
-
//
|
|
1823
|
-
|
|
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
|
-
|
|
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') {
|