genbox 1.0.196 → 1.0.198
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 +70 -47
- package/dist/commands/destroy.js +45 -3
- package/dist/commands/list.js +74 -7
- package/dist/commands/logs.js +35 -1
- package/dist/commands/new.js +170 -24
- package/dist/commands/provider-command.js +443 -220
- package/dist/commands/session/attach.js +190 -0
- package/dist/commands/session/index.js +18 -7
- package/dist/commands/session/list.js +14 -188
- package/dist/commands/session/start.js +66 -92
- package/dist/commands/status.js +54 -1
- package/dist/genbox-selector.js +77 -5
- package/dist/lib/genbox-progress.js +152 -0
- package/dist/lib/genbox-wizard.js +601 -0
- package/dist/lib/unified-session/index.js +8 -1
- package/dist/lib/unified-session/list-sessions.js +387 -0
- package/package.json +1 -1
package/dist/commands/create.js
CHANGED
|
@@ -53,6 +53,8 @@ const ssh_config_1 = require("../ssh-config");
|
|
|
53
53
|
const schema_v4_1 = require("../schema-v4");
|
|
54
54
|
const random_name_1 = require("../random-name");
|
|
55
55
|
const db_utils_1 = require("../db-utils");
|
|
56
|
+
const genbox_progress_1 = require("../lib/genbox-progress");
|
|
57
|
+
const genbox_wizard_1 = require("../lib/genbox-wizard");
|
|
56
58
|
const utils_1 = require("../utils");
|
|
57
59
|
const config_warnings_1 = require("../config-warnings");
|
|
58
60
|
// Credits consumed per hour for each size (matches API billing.config.ts)
|
|
@@ -84,28 +86,6 @@ function spawnSshConfigSetup(genboxId, name) {
|
|
|
84
86
|
// Allow parent to exit independently
|
|
85
87
|
child.unref();
|
|
86
88
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Wait for genbox to reach a specific status
|
|
89
|
-
*/
|
|
90
|
-
async function waitForGenboxStatus(genboxId, targetStatus, maxAttempts = 120, // 10 minutes with 5s interval
|
|
91
|
-
intervalMs = 5000) {
|
|
92
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
93
|
-
try {
|
|
94
|
-
const genbox = await (0, api_1.fetchApi)(`/genboxes/${genboxId}`);
|
|
95
|
-
if (targetStatus.includes(genbox.status)) {
|
|
96
|
-
return { success: true, genbox };
|
|
97
|
-
}
|
|
98
|
-
if (genbox.status === 'failed' || genbox.status === 'error') {
|
|
99
|
-
return { success: false, error: `Genbox provisioning failed: ${genbox.statusMessage || 'unknown error'}` };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
// Ignore fetch errors and continue polling
|
|
104
|
-
}
|
|
105
|
-
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
106
|
-
}
|
|
107
|
-
return { success: false, error: 'Timeout waiting for genbox to be ready' };
|
|
108
|
-
}
|
|
109
89
|
/**
|
|
110
90
|
* Run restore script on genbox via SSH
|
|
111
91
|
*/
|
|
@@ -593,12 +573,39 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
593
573
|
.option('-n, --new-branch <name>', 'Create a new branch with this name (defaults to env name)')
|
|
594
574
|
.option('-f, --from-branch <branch>', 'Source branch to create new branch from (defaults to config default or main)')
|
|
595
575
|
.option('-y, --yes', 'Skip interactive prompts')
|
|
576
|
+
.option('-d, --detach', 'Run in background (like docker compose up -d)')
|
|
596
577
|
.option('--dry-run', 'Show what would be created without actually creating')
|
|
597
578
|
.option('-r, --restore', 'Restore from backup (uses genbox name to find backup)')
|
|
598
579
|
.option('--inject-claude-auth', 'Inject local Claude Code credentials for remote execution')
|
|
599
580
|
.option('--inject-gemini-auth', 'Inject local Gemini CLI credentials for remote execution')
|
|
600
581
|
.action(async (nameArg, options) => {
|
|
601
582
|
try {
|
|
583
|
+
// Interactive mode: ask cloud vs local if not specified (use shared wizard)
|
|
584
|
+
if (!options.local && !options.yes && !options.restore && !options.dryRun) {
|
|
585
|
+
const location = await (0, genbox_wizard_1.promptForLocation)();
|
|
586
|
+
if (!location) {
|
|
587
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (location === 'direct') {
|
|
591
|
+
// Show warning for direct mode
|
|
592
|
+
const proceed = await (0, genbox_wizard_1.showDirectModeWarning)();
|
|
593
|
+
if (!proceed) {
|
|
594
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
options.local = true;
|
|
598
|
+
options.native = true;
|
|
599
|
+
}
|
|
600
|
+
else if (location === 'local-docker') {
|
|
601
|
+
options.local = true;
|
|
602
|
+
}
|
|
603
|
+
else if (location === 'local-vm') {
|
|
604
|
+
options.local = true;
|
|
605
|
+
options.vm = true;
|
|
606
|
+
}
|
|
607
|
+
// 'cloud' continues with the default flow
|
|
608
|
+
}
|
|
602
609
|
// Handle local genbox creation
|
|
603
610
|
if (options.local) {
|
|
604
611
|
await createLocalGenbox(nameArg, options);
|
|
@@ -1132,36 +1139,50 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
1132
1139
|
return;
|
|
1133
1140
|
}
|
|
1134
1141
|
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created!`));
|
|
1135
|
-
//
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1142
|
+
// Handle detach mode - return immediately without waiting
|
|
1143
|
+
if (options.detach) {
|
|
1144
|
+
// Spawn background process to poll for IP and add SSH config
|
|
1145
|
+
if (genbox._id) {
|
|
1146
|
+
spawnSshConfigSetup(genbox._id, name);
|
|
1140
1147
|
}
|
|
1148
|
+
console.log('');
|
|
1149
|
+
console.log(chalk_1.default.dim('Running in background (detached mode).'));
|
|
1150
|
+
console.log('');
|
|
1151
|
+
console.log(chalk_1.default.bold('Commands:'));
|
|
1152
|
+
console.log(` Status: ${chalk_1.default.cyan(`gb status ${name}`)}`);
|
|
1153
|
+
console.log(` Connect: ${chalk_1.default.cyan(`gb connect ${name}`)}`);
|
|
1154
|
+
console.log(` Logs: ${chalk_1.default.cyan(`gb status ${name} -w`)}`);
|
|
1155
|
+
return;
|
|
1141
1156
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1157
|
+
// Wait for genbox to be ready with progress indicator
|
|
1158
|
+
const genboxId = genbox._id || genbox.id;
|
|
1159
|
+
const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genboxId, name, {
|
|
1160
|
+
timeout: 180,
|
|
1161
|
+
showProgress: true,
|
|
1162
|
+
});
|
|
1163
|
+
if (!waitResult.success) {
|
|
1164
|
+
console.log('');
|
|
1165
|
+
console.log(chalk_1.default.bold('Commands:'));
|
|
1166
|
+
console.log(` Status: ${chalk_1.default.cyan(`gb status ${name}`)}`);
|
|
1167
|
+
console.log(` Logs: ${chalk_1.default.cyan(`gb status ${name} -w`)}`);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
const readyGenbox = waitResult.genbox;
|
|
1171
|
+
// Add SSH config now that we have IP
|
|
1172
|
+
if (readyGenbox.ipAddress) {
|
|
1173
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({ name, ipAddress: readyGenbox.ipAddress });
|
|
1174
|
+
if (sshAdded) {
|
|
1175
|
+
console.log(chalk_1.default.dim(`SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(name)}`));
|
|
1176
|
+
}
|
|
1146
1177
|
}
|
|
1147
1178
|
// Display results
|
|
1148
|
-
|
|
1179
|
+
console.log(chalk_1.default.green(`\nGenbox ${name} is ready!`));
|
|
1180
|
+
displayGenboxInfo(readyGenbox, resolved);
|
|
1149
1181
|
// Handle restore if backup was specified
|
|
1150
|
-
if (restoreBackup && restoreDownloadUrl
|
|
1182
|
+
if (restoreBackup && restoreDownloadUrl) {
|
|
1151
1183
|
console.log('');
|
|
1152
1184
|
console.log(chalk_1.default.blue('=== Restoring from Backup ==='));
|
|
1153
|
-
|
|
1154
|
-
const waitSpinner = (0, ora_1.default)('Waiting for genbox to be ready...').start();
|
|
1155
|
-
const waitResult = await waitForGenboxStatus(genbox._id, ['running']);
|
|
1156
|
-
if (!waitResult.success) {
|
|
1157
|
-
waitSpinner.fail(chalk_1.default.red(waitResult.error || 'Failed to wait for genbox'));
|
|
1158
|
-
console.log(chalk_1.default.dim(' You can manually restore later: gb connect, then run the restore script'));
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
waitSpinner.succeed(chalk_1.default.green('Genbox is ready'));
|
|
1162
|
-
// Get IP address from the ready genbox
|
|
1163
|
-
const readyGenbox = waitResult.genbox;
|
|
1164
|
-
const ipAddress = readyGenbox?.ipAddress || genbox.ipAddress;
|
|
1185
|
+
const ipAddress = readyGenbox.ipAddress;
|
|
1165
1186
|
if (!ipAddress) {
|
|
1166
1187
|
console.log(chalk_1.default.red('Could not get genbox IP address'));
|
|
1167
1188
|
console.log(chalk_1.default.dim(' You can manually restore by connecting and downloading the backup'));
|
|
@@ -1187,9 +1208,11 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
1187
1208
|
}
|
|
1188
1209
|
}
|
|
1189
1210
|
else {
|
|
1190
|
-
//
|
|
1211
|
+
// Show next steps
|
|
1191
1212
|
console.log('');
|
|
1192
|
-
console.log(chalk_1.default.
|
|
1213
|
+
console.log(chalk_1.default.bold('Next steps:'));
|
|
1214
|
+
console.log(` Connect: ${chalk_1.default.cyan(`gb connect ${name}`)}`);
|
|
1215
|
+
console.log(` Status: ${chalk_1.default.cyan(`gb status ${name}`)}`);
|
|
1193
1216
|
}
|
|
1194
1217
|
}
|
|
1195
1218
|
catch (error) {
|
package/dist/commands/destroy.js
CHANGED
|
@@ -82,12 +82,54 @@ async function handleBulkDelete(options) {
|
|
|
82
82
|
const projectCloudGenboxes = projectName
|
|
83
83
|
? allCloudGenboxes.filter(g => g.project === projectName || g.workspace === projectName)
|
|
84
84
|
: [];
|
|
85
|
-
// Fetch local genboxes
|
|
85
|
+
// Fetch local genboxes from both LocalGenboxProvisioner and UnifiedSessionManager
|
|
86
86
|
const provisioner = (0, local_genbox_provisioner_1.getLocalGenboxProvisioner)();
|
|
87
|
-
const allLocalSessions =
|
|
87
|
+
const allLocalSessions = [];
|
|
88
|
+
const seenNames = new Set();
|
|
89
|
+
// Get from LocalGenboxProvisioner (legacy)
|
|
90
|
+
for (const s of provisioner.listSessions()) {
|
|
91
|
+
if (!seenNames.has(s.name)) {
|
|
92
|
+
allLocalSessions.push(s);
|
|
93
|
+
seenNames.add(s.name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Get from UnifiedSessionManager (wizard-created VMs)
|
|
97
|
+
try {
|
|
98
|
+
const sessionManager = (0, unified_session_1.getUnifiedSessionManager)();
|
|
99
|
+
const unifiedSessions = sessionManager.listSessions({
|
|
100
|
+
type: ['multipass', 'docker'],
|
|
101
|
+
});
|
|
102
|
+
for (const s of unifiedSessions) {
|
|
103
|
+
if (!seenNames.has(s.name)) {
|
|
104
|
+
allLocalSessions.push({
|
|
105
|
+
id: s.id,
|
|
106
|
+
name: s.name,
|
|
107
|
+
projectName: s.projectPath?.split('/').pop() || '',
|
|
108
|
+
workdir: s.projectPath || '',
|
|
109
|
+
sessionDir: '',
|
|
110
|
+
createdAt: s.createdAt,
|
|
111
|
+
size: 'small',
|
|
112
|
+
isolation: s.type,
|
|
113
|
+
apps: [{
|
|
114
|
+
name: s.provider || 'unknown',
|
|
115
|
+
path: '',
|
|
116
|
+
status: s.status === 'running' || s.status === 'active' ? 'running' : 'stopped',
|
|
117
|
+
}],
|
|
118
|
+
infrastructure: [],
|
|
119
|
+
database: { mode: 'none', seeded: false },
|
|
120
|
+
vmName: s.infrastructure?.vmName,
|
|
121
|
+
vmIpAddress: s.infrastructure?.vmIpAddress,
|
|
122
|
+
});
|
|
123
|
+
seenNames.add(s.name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Ignore errors
|
|
129
|
+
}
|
|
88
130
|
// Filter local sessions by project if applicable
|
|
89
131
|
const projectLocalSessions = projectName
|
|
90
|
-
? allLocalSessions.filter(s => s.projectName === projectName)
|
|
132
|
+
? allLocalSessions.filter(s => s.projectName === projectName || !s.projectName)
|
|
91
133
|
: allLocalSessions;
|
|
92
134
|
const totalCloud = allCloudGenboxes.length;
|
|
93
135
|
const totalLocal = allLocalSessions.length;
|
package/dist/commands/list.js
CHANGED
|
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.listCommand = void 0;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
9
10
|
const api_1 = require("../api");
|
|
10
11
|
const genbox_selector_1 = require("../genbox-selector");
|
|
11
12
|
const local_genbox_provisioner_1 = require("../lib/local-genbox-provisioner");
|
|
13
|
+
const unified_session_1 = require("../lib/unified-session");
|
|
12
14
|
exports.listCommand = new commander_1.Command('list')
|
|
13
15
|
.alias('ls')
|
|
14
16
|
.description('List genboxes (scoped to current project by default)')
|
|
@@ -26,20 +28,85 @@ exports.listCommand = new commander_1.Command('list')
|
|
|
26
28
|
all: options.all,
|
|
27
29
|
includeTerminated: options.terminated,
|
|
28
30
|
}),
|
|
29
|
-
// Local genboxes
|
|
31
|
+
// Local genboxes from both LocalGenboxProvisioner and UnifiedSessionManager
|
|
30
32
|
Promise.resolve().then(() => {
|
|
33
|
+
const sessions = [];
|
|
34
|
+
const seenNames = new Set();
|
|
35
|
+
// Get sessions from LocalGenboxProvisioner (for legacy local genboxes)
|
|
31
36
|
try {
|
|
32
37
|
const provisioner = (0, local_genbox_provisioner_1.getLocalGenboxProvisioner)();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
for (const s of provisioner.listSessions()) {
|
|
39
|
+
if (!seenNames.has(s.name)) {
|
|
40
|
+
sessions.push(s);
|
|
41
|
+
seenNames.add(s.name);
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
|
-
return sessions;
|
|
39
44
|
}
|
|
40
45
|
catch {
|
|
41
|
-
|
|
46
|
+
// Ignore errors
|
|
42
47
|
}
|
|
48
|
+
// Get local sessions from UnifiedSessionManager (multipass/docker VMs created via gb gemini)
|
|
49
|
+
try {
|
|
50
|
+
const sessionManager = (0, unified_session_1.getUnifiedSessionManager)();
|
|
51
|
+
const unifiedSessions = sessionManager.listSessions({
|
|
52
|
+
type: ['multipass', 'docker'],
|
|
53
|
+
});
|
|
54
|
+
for (const s of unifiedSessions) {
|
|
55
|
+
if (!seenNames.has(s.name)) {
|
|
56
|
+
// Check actual VM status for multipass sessions
|
|
57
|
+
let actualStatus = s.status === 'running' || s.status === 'active' ? 'running' : 'stopped';
|
|
58
|
+
let vmIp = s.infrastructure?.vmIpAddress;
|
|
59
|
+
if (s.type === 'multipass') {
|
|
60
|
+
// Use vmName or fall back to session name for older sessions
|
|
61
|
+
const vmName = s.infrastructure?.vmName || s.name;
|
|
62
|
+
try {
|
|
63
|
+
const info = (0, child_process_1.execSync)(`multipass info ${vmName} --format json 2>/dev/null`, { encoding: 'utf8' });
|
|
64
|
+
const vmInfo = JSON.parse(info);
|
|
65
|
+
const vm = vmInfo.info?.[vmName];
|
|
66
|
+
if (vm) {
|
|
67
|
+
actualStatus = vm.state === 'Running' ? 'running' : 'stopped';
|
|
68
|
+
vmIp = vm.ipv4?.[0] || vmIp;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// VM might not exist anymore
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Convert to LocalGenboxSession format for rendering
|
|
76
|
+
sessions.push({
|
|
77
|
+
id: s.id,
|
|
78
|
+
name: s.name,
|
|
79
|
+
projectName: s.projectPath?.split('/').pop() || '',
|
|
80
|
+
workdir: s.projectPath || '',
|
|
81
|
+
sessionDir: '',
|
|
82
|
+
createdAt: s.createdAt,
|
|
83
|
+
size: 'small',
|
|
84
|
+
isolation: s.type,
|
|
85
|
+
apps: [{
|
|
86
|
+
name: s.provider || 'unknown',
|
|
87
|
+
path: '',
|
|
88
|
+
status: actualStatus,
|
|
89
|
+
}],
|
|
90
|
+
infrastructure: [],
|
|
91
|
+
database: {
|
|
92
|
+
mode: 'none',
|
|
93
|
+
seeded: false,
|
|
94
|
+
},
|
|
95
|
+
vmName: s.infrastructure?.vmName || s.name,
|
|
96
|
+
vmIpAddress: vmIp,
|
|
97
|
+
});
|
|
98
|
+
seenNames.add(s.name);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Ignore errors
|
|
104
|
+
}
|
|
105
|
+
// Filter by project if applicable
|
|
106
|
+
if (projectName && !options.all) {
|
|
107
|
+
return sessions.filter(s => s.projectName === projectName || !s.projectName);
|
|
108
|
+
}
|
|
109
|
+
return sessions;
|
|
43
110
|
}),
|
|
44
111
|
]);
|
|
45
112
|
// Extract results
|
package/dist/commands/logs.js
CHANGED
|
@@ -51,6 +51,7 @@ const path = __importStar(require("path"));
|
|
|
51
51
|
const fs = __importStar(require("fs"));
|
|
52
52
|
const genbox_selector_1 = require("../genbox-selector");
|
|
53
53
|
const local_genbox_provisioner_1 = require("../lib/local-genbox-provisioner");
|
|
54
|
+
const unified_session_1 = require("../lib/unified-session");
|
|
54
55
|
function getPrivateSshKey() {
|
|
55
56
|
const home = os.homedir();
|
|
56
57
|
const potentialKeys = [
|
|
@@ -116,8 +117,41 @@ Examples:
|
|
|
116
117
|
* Show logs for a local genbox
|
|
117
118
|
*/
|
|
118
119
|
async function showLocalGenboxLogs(sessionId, options) {
|
|
120
|
+
// Try LocalGenboxProvisioner first (for legacy sessions)
|
|
119
121
|
const provisioner = (0, local_genbox_provisioner_1.getLocalGenboxProvisioner)();
|
|
120
|
-
|
|
122
|
+
let session = provisioner.getSession(sessionId);
|
|
123
|
+
// If not found, try UnifiedSessionManager (for wizard-created VMs)
|
|
124
|
+
if (!session) {
|
|
125
|
+
try {
|
|
126
|
+
const sessionManager = (0, unified_session_1.getUnifiedSessionManager)();
|
|
127
|
+
const unifiedSession = sessionManager.getSession(sessionId);
|
|
128
|
+
if (unifiedSession && (unifiedSession.type === 'multipass' || unifiedSession.type === 'docker')) {
|
|
129
|
+
// Convert to LocalGenboxSession format
|
|
130
|
+
session = {
|
|
131
|
+
id: unifiedSession.id,
|
|
132
|
+
name: unifiedSession.name,
|
|
133
|
+
projectName: unifiedSession.projectPath?.split('/').pop() || '',
|
|
134
|
+
workdir: unifiedSession.projectPath || '',
|
|
135
|
+
sessionDir: '',
|
|
136
|
+
createdAt: unifiedSession.createdAt,
|
|
137
|
+
size: 'small',
|
|
138
|
+
isolation: unifiedSession.type,
|
|
139
|
+
apps: [{
|
|
140
|
+
name: unifiedSession.provider || 'unknown',
|
|
141
|
+
path: '',
|
|
142
|
+
status: unifiedSession.status === 'running' || unifiedSession.status === 'active' ? 'running' : 'stopped',
|
|
143
|
+
}],
|
|
144
|
+
infrastructure: [],
|
|
145
|
+
database: { mode: 'none', seeded: false },
|
|
146
|
+
vmName: unifiedSession.infrastructure?.vmName,
|
|
147
|
+
vmIpAddress: unifiedSession.infrastructure?.vmIpAddress,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Ignore errors
|
|
153
|
+
}
|
|
154
|
+
}
|
|
121
155
|
if (!session) {
|
|
122
156
|
console.log(chalk_1.default.red('Session not found.'));
|
|
123
157
|
return;
|
package/dist/commands/new.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -16,6 +49,8 @@ const utils_1 = require("../utils");
|
|
|
16
49
|
const random_name_1 = require("../random-name");
|
|
17
50
|
const ssh_config_1 = require("../ssh-config");
|
|
18
51
|
const child_process_1 = require("child_process");
|
|
52
|
+
const genbox_wizard_1 = require("../lib/genbox-wizard");
|
|
53
|
+
const genbox_progress_1 = require("../lib/genbox-progress");
|
|
19
54
|
// Credits per hour by size
|
|
20
55
|
const CREDITS_PER_HOUR = {
|
|
21
56
|
small: 1,
|
|
@@ -40,6 +75,8 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
40
75
|
.argument('[name]', 'Project name')
|
|
41
76
|
.option('--list', 'List available templates')
|
|
42
77
|
.option('-s, --size <size>', 'Genbox size: small, medium, large, xl')
|
|
78
|
+
.option('-l, --local', 'Create local genbox (Docker)')
|
|
79
|
+
.option('--cloud', 'Create cloud genbox (default)')
|
|
43
80
|
.option('-y, --yes', 'Skip interactive prompts, use defaults')
|
|
44
81
|
.action(async (templateArg, nameArg, options) => {
|
|
45
82
|
try {
|
|
@@ -126,6 +163,27 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
126
163
|
projectName = `${template.name}-${(0, random_name_1.generateRandomName)()}`;
|
|
127
164
|
console.log(chalk_1.default.dim(`Generated name: ${projectName}`));
|
|
128
165
|
}
|
|
166
|
+
// Local vs Cloud selection (use shared wizard)
|
|
167
|
+
let isLocal = options.local || false;
|
|
168
|
+
if (!options.local && !options.cloud && !options.yes) {
|
|
169
|
+
const location = await (0, genbox_wizard_1.promptForLocation)();
|
|
170
|
+
if (!location) {
|
|
171
|
+
console.log(chalk_1.default.dim('\nCancelled.'));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Direct mode not supported for templates - need a genbox
|
|
175
|
+
if (location === 'direct') {
|
|
176
|
+
console.log(chalk_1.default.yellow('\nDirect mode is not available for templates.'));
|
|
177
|
+
console.log(chalk_1.default.dim('Templates require a genbox environment. Choose Cloud or Local Docker/VM.'));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
isLocal = location === 'local-docker' || location === 'local-vm';
|
|
181
|
+
}
|
|
182
|
+
// Handle local creation
|
|
183
|
+
if (isLocal) {
|
|
184
|
+
await createLocalFromTemplate(template, projectName);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
129
187
|
// Size selection
|
|
130
188
|
let size = options.size || preferences.defaultGenboxSize || template.recommendedSize;
|
|
131
189
|
if (!options.yes && !options.size) {
|
|
@@ -273,41 +331,42 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
273
331
|
return;
|
|
274
332
|
}
|
|
275
333
|
createSpinner.succeed(chalk_1.default.green(`Genbox '${projectName}' created!`));
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
334
|
+
// Wait for genbox to be ready with progress indicator
|
|
335
|
+
const genboxId = genbox._id || genbox.id;
|
|
336
|
+
const waitResult = await (0, genbox_progress_1.waitForGenboxReady)(genboxId, projectName, {
|
|
337
|
+
timeout: 180,
|
|
338
|
+
showProgress: true,
|
|
339
|
+
});
|
|
340
|
+
if (!waitResult.success) {
|
|
341
|
+
console.log('');
|
|
342
|
+
console.log(chalk_1.default.bold('Commands:'));
|
|
343
|
+
console.log(` Status: ${chalk_1.default.cyan(`gb status ${projectName}`)}`);
|
|
344
|
+
console.log(` Logs: ${chalk_1.default.cyan(`gb status ${projectName} -w`)}`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const readyGenbox = waitResult.genbox;
|
|
348
|
+
// Add SSH config now that we have IP
|
|
349
|
+
if (readyGenbox.ipAddress) {
|
|
350
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({ name: projectName, ipAddress: readyGenbox.ipAddress });
|
|
279
351
|
if (sshAdded) {
|
|
280
|
-
console.log(chalk_1.default.dim(`
|
|
352
|
+
console.log(chalk_1.default.dim(`SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(projectName)}`));
|
|
281
353
|
}
|
|
282
354
|
}
|
|
283
|
-
else if (genbox._id) {
|
|
284
|
-
spawnSshConfigSetup(genbox._id, projectName);
|
|
285
|
-
console.log(chalk_1.default.dim(' SSH config will be added once IP is assigned.'));
|
|
286
|
-
}
|
|
287
355
|
// Display results
|
|
356
|
+
console.log(chalk_1.default.green(`\nGenbox ${projectName} is ready!`));
|
|
288
357
|
console.log('');
|
|
289
358
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
290
|
-
console.log(` ${chalk_1.default.bold('Name:')} ${
|
|
291
|
-
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.
|
|
292
|
-
if (
|
|
293
|
-
console.log(` ${chalk_1.default.bold('IP:')} ${
|
|
359
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${readyGenbox.name || projectName}`);
|
|
360
|
+
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.green('running')}`);
|
|
361
|
+
if (readyGenbox.ipAddress) {
|
|
362
|
+
console.log(` ${chalk_1.default.bold('IP:')} ${readyGenbox.ipAddress}`);
|
|
294
363
|
}
|
|
295
364
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
296
|
-
// Display URLs
|
|
297
|
-
if (genbox.urls && Object.keys(genbox.urls).length > 0) {
|
|
298
|
-
console.log('');
|
|
299
|
-
console.log(chalk_1.default.bold('Access URLs:'));
|
|
300
|
-
for (const [service, url] of Object.entries(genbox.urls)) {
|
|
301
|
-
console.log(` ${chalk_1.default.cyan(String(service).padEnd(12))} ${url}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
365
|
console.log('');
|
|
305
366
|
console.log(chalk_1.default.bold('Next steps:'));
|
|
306
|
-
console.log(`
|
|
307
|
-
console.log(`
|
|
308
|
-
console.log(` 3. Start Claude: ${chalk_1.default.cyan(`gb claude ${projectName}`)}`);
|
|
367
|
+
console.log(` Connect: ${chalk_1.default.cyan(`gb connect ${projectName}`)}`);
|
|
368
|
+
console.log(` Start Claude: ${chalk_1.default.cyan(`gb claude --on ${projectName}`)}`);
|
|
309
369
|
console.log('');
|
|
310
|
-
console.log(chalk_1.default.dim('Server is provisioning. This takes about 2-3 minutes.'));
|
|
311
370
|
}
|
|
312
371
|
catch (error) {
|
|
313
372
|
createSpinner.fail(chalk_1.default.red(`Failed to create genbox: ${error.message}`));
|
|
@@ -328,6 +387,93 @@ exports.newCommand = new commander_1.Command('new')
|
|
|
328
387
|
console.error(chalk_1.default.red('Error:'), error.message);
|
|
329
388
|
}
|
|
330
389
|
});
|
|
390
|
+
/**
|
|
391
|
+
* Create a local genbox from a starter template using Docker
|
|
392
|
+
*/
|
|
393
|
+
async function createLocalFromTemplate(template, projectName) {
|
|
394
|
+
console.log('');
|
|
395
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
396
|
+
console.log(` ${chalk_1.default.bold('Template:')} ${template.icon} ${template.displayName}`);
|
|
397
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${projectName}`);
|
|
398
|
+
console.log(` ${chalk_1.default.bold('Location:')} ${chalk_1.default.green('Local (Docker)')}`);
|
|
399
|
+
console.log(` ${chalk_1.default.bold('Features:')} ${template.features.join(', ')}`);
|
|
400
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
401
|
+
const confirmed = await (0, confirm_1.default)({
|
|
402
|
+
message: `Create local genbox '${projectName}'?`,
|
|
403
|
+
default: true,
|
|
404
|
+
});
|
|
405
|
+
if (!confirmed) {
|
|
406
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const spinner = (0, ora_1.default)('Creating local genbox...').start();
|
|
410
|
+
try {
|
|
411
|
+
// Build the create command for the template
|
|
412
|
+
let createCmd;
|
|
413
|
+
if (template.createCommand) {
|
|
414
|
+
createCmd = template.createCommand.replace(/\{\{name\}\}/g, projectName);
|
|
415
|
+
}
|
|
416
|
+
else if (template.repoUrl) {
|
|
417
|
+
createCmd = `git clone https://${template.repoUrl} ${projectName}`;
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
spinner.fail('Template does not support local creation');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Run the create command locally
|
|
424
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
425
|
+
spinner.text = 'Setting up project...';
|
|
426
|
+
execSync(createCmd, {
|
|
427
|
+
stdio: 'pipe',
|
|
428
|
+
cwd: process.cwd(),
|
|
429
|
+
});
|
|
430
|
+
// Run setup commands if any
|
|
431
|
+
if (template.setupCommands && template.setupCommands.length > 0) {
|
|
432
|
+
const projectPath = `${process.cwd()}/${projectName}`;
|
|
433
|
+
for (const cmd of template.setupCommands) {
|
|
434
|
+
const setupCmd = cmd.replace(/\{\{name\}\}/g, projectName);
|
|
435
|
+
spinner.text = `Running: ${setupCmd}`;
|
|
436
|
+
try {
|
|
437
|
+
execSync(setupCmd, {
|
|
438
|
+
stdio: 'pipe',
|
|
439
|
+
cwd: projectPath,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// Some setup commands may fail, continue
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
spinner.succeed(chalk_1.default.green(`Local project '${projectName}' created!`));
|
|
448
|
+
console.log('');
|
|
449
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
450
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${projectName}`);
|
|
451
|
+
console.log(` ${chalk_1.default.bold('Path:')} ${process.cwd()}/${projectName}`);
|
|
452
|
+
console.log(` ${chalk_1.default.bold('Type:')} Local`);
|
|
453
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
454
|
+
console.log('');
|
|
455
|
+
console.log(chalk_1.default.bold('Next steps:'));
|
|
456
|
+
console.log(` 1. ${chalk_1.default.cyan(`cd ${projectName}`)}`);
|
|
457
|
+
console.log(` 2. ${chalk_1.default.cyan('pnpm install')} ${chalk_1.default.dim('(or npm install)')}`);
|
|
458
|
+
console.log(` 3. ${chalk_1.default.cyan('pnpm dev')} ${chalk_1.default.dim('(or npm run dev)')}`);
|
|
459
|
+
console.log('');
|
|
460
|
+
console.log(chalk_1.default.dim('To start a Claude session in this project:'));
|
|
461
|
+
console.log(` ${chalk_1.default.cyan(`cd ${projectName} && claude`)}`);
|
|
462
|
+
console.log('');
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
spinner.fail(chalk_1.default.red(`Failed to create local project: ${error.message}`));
|
|
466
|
+
if (error.message.includes('command not found')) {
|
|
467
|
+
console.log(chalk_1.default.yellow('\nMake sure the required tools are installed:'));
|
|
468
|
+
if (template.createCommand?.includes('npx')) {
|
|
469
|
+
console.log(chalk_1.default.dim(' - Node.js and npx'));
|
|
470
|
+
}
|
|
471
|
+
if (template.createCommand?.includes('bunx')) {
|
|
472
|
+
console.log(chalk_1.default.dim(' - Bun (https://bun.sh)'));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
331
477
|
async function listTemplates() {
|
|
332
478
|
const spinner = (0, ora_1.default)('Fetching templates...').start();
|
|
333
479
|
try {
|