genbox 1.0.198 → 1.0.199
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/destroy.js +43 -1
- package/dist/lib/genbox-wizard.js +316 -14
- package/package.json +1 -1
package/dist/commands/destroy.js
CHANGED
|
@@ -43,6 +43,7 @@ const confirm_1 = __importDefault(require("@inquirer/confirm"));
|
|
|
43
43
|
const select_1 = __importDefault(require("@inquirer/select"));
|
|
44
44
|
const prompts = __importStar(require("@inquirer/prompts"));
|
|
45
45
|
const ora_1 = __importDefault(require("ora"));
|
|
46
|
+
const child_process_1 = require("child_process");
|
|
46
47
|
const api_1 = require("../api");
|
|
47
48
|
const genbox_selector_1 = require("../genbox-selector");
|
|
48
49
|
const ssh_config_1 = require("../ssh-config");
|
|
@@ -260,10 +261,51 @@ async function handleBulkDelete(options) {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
// Delete local genboxes
|
|
264
|
+
const sessionManager = (0, unified_session_1.getUnifiedSessionManager)();
|
|
263
265
|
for (const session of selectedLocalSessions) {
|
|
264
266
|
const spinner = (0, ora_1.default)(`Destroying ${session.name} (local)...`).start();
|
|
265
267
|
try {
|
|
266
|
-
|
|
268
|
+
// Check if this is a UnifiedSessionManager session (wizard-created VM)
|
|
269
|
+
const unifiedSession = sessionManager.getSession(session.id);
|
|
270
|
+
if (unifiedSession) {
|
|
271
|
+
// Delete the actual VM/container based on type
|
|
272
|
+
if (unifiedSession.type === 'multipass') {
|
|
273
|
+
const vmName = unifiedSession.infrastructure?.vmName || unifiedSession.name;
|
|
274
|
+
try {
|
|
275
|
+
(0, child_process_1.execSync)(`multipass delete ${vmName} --purge`, { stdio: 'pipe' });
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// VM might already be deleted
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (unifiedSession.type === 'docker') {
|
|
282
|
+
// Try containerId first, then fall back to container name
|
|
283
|
+
const containerId = unifiedSession.infrastructure?.containerId;
|
|
284
|
+
const containerName = unifiedSession.infrastructure?.containerName || unifiedSession.name;
|
|
285
|
+
if (containerId) {
|
|
286
|
+
try {
|
|
287
|
+
(0, child_process_1.execSync)(`docker rm -f ${containerId}`, { stdio: 'pipe' });
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Container might already be deleted
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else if (containerName) {
|
|
294
|
+
try {
|
|
295
|
+
(0, child_process_1.execSync)(`docker rm -f ${containerName}`, { stdio: 'pipe' });
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
// Container might not exist or already be deleted
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Delete from UnifiedSessionManager
|
|
303
|
+
await sessionManager.deleteSession(session.id);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// Fallback to LocalGenboxProvisioner for legacy sessions
|
|
307
|
+
await provisioner.destroy(session.id);
|
|
308
|
+
}
|
|
267
309
|
spinner.succeed(chalk_1.default.green(`Destroyed '${session.name}' (local)`));
|
|
268
310
|
successCount++;
|
|
269
311
|
}
|
|
@@ -42,6 +42,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
42
42
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.promptForSize = promptForSize;
|
|
45
46
|
exports.showDirectModeWarning = showDirectModeWarning;
|
|
46
47
|
exports.promptForLocation = promptForLocation;
|
|
47
48
|
exports.promptForName = promptForName;
|
|
@@ -61,6 +62,285 @@ const genbox_progress_1 = require("./genbox-progress");
|
|
|
61
62
|
const ssh_keys_1 = require("../utils/ssh-keys");
|
|
62
63
|
const config_store_1 = require("../config-store");
|
|
63
64
|
const unified_session_1 = require("./unified-session");
|
|
65
|
+
const VM_SIZE_REQUIREMENTS = {
|
|
66
|
+
small: {
|
|
67
|
+
cpus: 2,
|
|
68
|
+
memoryGB: 4,
|
|
69
|
+
diskGB: 20,
|
|
70
|
+
label: 'Small',
|
|
71
|
+
description: 'Basic development tasks',
|
|
72
|
+
},
|
|
73
|
+
medium: {
|
|
74
|
+
cpus: 4,
|
|
75
|
+
memoryGB: 8,
|
|
76
|
+
diskGB: 40,
|
|
77
|
+
label: 'Medium',
|
|
78
|
+
description: 'Standard development',
|
|
79
|
+
},
|
|
80
|
+
large: {
|
|
81
|
+
cpus: 6,
|
|
82
|
+
memoryGB: 12,
|
|
83
|
+
diskGB: 60,
|
|
84
|
+
label: 'Large',
|
|
85
|
+
description: 'Heavy workloads, multiple services',
|
|
86
|
+
},
|
|
87
|
+
xl: {
|
|
88
|
+
cpus: 8,
|
|
89
|
+
memoryGB: 16,
|
|
90
|
+
diskGB: 80,
|
|
91
|
+
label: 'XL',
|
|
92
|
+
description: 'Intensive tasks, large projects',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const DOCKER_SIZE_REQUIREMENTS = {
|
|
96
|
+
small: {
|
|
97
|
+
cpus: 1,
|
|
98
|
+
memoryGB: 2,
|
|
99
|
+
diskGB: 5,
|
|
100
|
+
label: 'Small',
|
|
101
|
+
description: 'Basic development tasks',
|
|
102
|
+
},
|
|
103
|
+
medium: {
|
|
104
|
+
cpus: 2,
|
|
105
|
+
memoryGB: 4,
|
|
106
|
+
diskGB: 10,
|
|
107
|
+
label: 'Medium',
|
|
108
|
+
description: 'Standard development',
|
|
109
|
+
},
|
|
110
|
+
large: {
|
|
111
|
+
cpus: 4,
|
|
112
|
+
memoryGB: 8,
|
|
113
|
+
diskGB: 20,
|
|
114
|
+
label: 'Large',
|
|
115
|
+
description: 'Heavy workloads',
|
|
116
|
+
},
|
|
117
|
+
xl: {
|
|
118
|
+
cpus: 6,
|
|
119
|
+
memoryGB: 12,
|
|
120
|
+
diskGB: 30,
|
|
121
|
+
label: 'XL',
|
|
122
|
+
description: 'Intensive tasks',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Get current system resources
|
|
127
|
+
*/
|
|
128
|
+
function getSystemResources() {
|
|
129
|
+
const cpuCores = os.cpus().length;
|
|
130
|
+
const totalMemoryGB = Math.round(os.totalmem() / (1024 * 1024 * 1024) * 10) / 10;
|
|
131
|
+
// Get available memory (platform-specific)
|
|
132
|
+
let availableMemoryGB;
|
|
133
|
+
if (process.platform === 'darwin') {
|
|
134
|
+
try {
|
|
135
|
+
// On macOS, use vm_stat for more accurate available memory
|
|
136
|
+
const vmStat = (0, child_process_1.execSync)('vm_stat', { encoding: 'utf8' });
|
|
137
|
+
const pageSize = 16384; // Default page size on Apple Silicon
|
|
138
|
+
const freeMatch = vmStat.match(/Pages free:\s+(\d+)/);
|
|
139
|
+
const inactiveMatch = vmStat.match(/Pages inactive:\s+(\d+)/);
|
|
140
|
+
const speculativeMatch = vmStat.match(/Pages speculative:\s+(\d+)/);
|
|
141
|
+
const freePages = parseInt(freeMatch?.[1] || '0', 10);
|
|
142
|
+
const inactivePages = parseInt(inactiveMatch?.[1] || '0', 10);
|
|
143
|
+
const speculativePages = parseInt(speculativeMatch?.[1] || '0', 10);
|
|
144
|
+
const reclaimableBytes = (freePages + inactivePages + speculativePages) * pageSize;
|
|
145
|
+
availableMemoryGB = Math.round(reclaimableBytes / (1024 * 1024 * 1024) * 10) / 10;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
availableMemoryGB = Math.round(os.freemem() / (1024 * 1024 * 1024) * 10) / 10;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
availableMemoryGB = Math.round(os.freemem() / (1024 * 1024 * 1024) * 10) / 10;
|
|
153
|
+
}
|
|
154
|
+
// Get available disk space
|
|
155
|
+
let diskSpaceGB = 0;
|
|
156
|
+
try {
|
|
157
|
+
if (process.platform === 'darwin' || process.platform === 'linux') {
|
|
158
|
+
const df = (0, child_process_1.execSync)('df -k / | tail -1', { encoding: 'utf8' });
|
|
159
|
+
const parts = df.trim().split(/\s+/);
|
|
160
|
+
const availableKb = parseInt(parts[3], 10);
|
|
161
|
+
diskSpaceGB = Math.round(availableKb / (1024 * 1024) * 10) / 10;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
diskSpaceGB = 50; // Assume 50GB if we can't detect
|
|
166
|
+
}
|
|
167
|
+
// Check Docker status
|
|
168
|
+
let dockerRunning = false;
|
|
169
|
+
try {
|
|
170
|
+
(0, child_process_1.execSync)('docker info', { stdio: 'pipe' });
|
|
171
|
+
dockerRunning = true;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
dockerRunning = false;
|
|
175
|
+
}
|
|
176
|
+
// Check Multipass status
|
|
177
|
+
let multipassRunning = false;
|
|
178
|
+
try {
|
|
179
|
+
(0, child_process_1.execSync)('multipass version', { stdio: 'pipe' });
|
|
180
|
+
multipassRunning = true;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
multipassRunning = false;
|
|
184
|
+
}
|
|
185
|
+
// Count existing VMs
|
|
186
|
+
let existingVms = 0;
|
|
187
|
+
try {
|
|
188
|
+
const vmList = (0, child_process_1.execSync)('multipass list --format json 2>/dev/null', { encoding: 'utf8' });
|
|
189
|
+
const vms = JSON.parse(vmList);
|
|
190
|
+
existingVms = vms.list?.length || 0;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
existingVms = 0;
|
|
194
|
+
}
|
|
195
|
+
// Count existing Docker containers (genbox-related)
|
|
196
|
+
let existingContainers = 0;
|
|
197
|
+
try {
|
|
198
|
+
const containerList = (0, child_process_1.execSync)('docker ps -a --filter "name=genbox" --format "{{.Names}}" 2>/dev/null', { encoding: 'utf8' });
|
|
199
|
+
existingContainers = containerList.trim().split('\n').filter(Boolean).length;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
existingContainers = 0;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
cpuCores,
|
|
206
|
+
totalMemoryGB,
|
|
207
|
+
availableMemoryGB,
|
|
208
|
+
diskSpaceGB,
|
|
209
|
+
dockerRunning,
|
|
210
|
+
multipassRunning,
|
|
211
|
+
existingVms,
|
|
212
|
+
existingContainers,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Format bytes to human-readable string
|
|
217
|
+
*/
|
|
218
|
+
function formatGB(gb) {
|
|
219
|
+
return `${gb.toFixed(1)} GB`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Prompt user to select a genbox size
|
|
223
|
+
*/
|
|
224
|
+
async function promptForSize(type, resources) {
|
|
225
|
+
const sizeRequirements = type === 'vm' ? VM_SIZE_REQUIREMENTS : DOCKER_SIZE_REQUIREMENTS;
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log(chalk_1.default.cyan(' Select a size for your genbox:'));
|
|
228
|
+
console.log('');
|
|
229
|
+
try {
|
|
230
|
+
const choices = Object.entries(sizeRequirements).map(([size, req]) => {
|
|
231
|
+
// Check if system has enough resources
|
|
232
|
+
const hasEnoughMemory = resources.totalMemoryGB >= req.memoryGB * 1.5;
|
|
233
|
+
const hasEnoughCpus = resources.cpuCores >= req.cpus + 1;
|
|
234
|
+
const hasEnoughDisk = resources.diskSpaceGB >= req.diskGB;
|
|
235
|
+
const canFit = hasEnoughMemory && hasEnoughCpus && hasEnoughDisk;
|
|
236
|
+
const warning = !canFit ? chalk_1.default.yellow(' ⚠') : '';
|
|
237
|
+
const disabled = resources.totalMemoryGB < req.memoryGB || resources.cpuCores < req.cpus;
|
|
238
|
+
return {
|
|
239
|
+
name: `${req.label}${warning} ${chalk_1.default.dim(`(${req.cpus} CPU, ${req.memoryGB}GB RAM, ${req.diskGB}GB disk)`)} ${chalk_1.default.dim('- ' + req.description)}`,
|
|
240
|
+
value: size,
|
|
241
|
+
disabled: disabled ? chalk_1.default.red('(insufficient resources)') : false,
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
const selected = await (0, select_1.default)({
|
|
245
|
+
message: 'Size:',
|
|
246
|
+
choices,
|
|
247
|
+
});
|
|
248
|
+
return selected;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Show resource summary and get user confirmation before creating local genbox
|
|
256
|
+
*/
|
|
257
|
+
async function confirmResourceUsage(type, resources, size = 'small') {
|
|
258
|
+
const sizeRequirements = type === 'vm' ? VM_SIZE_REQUIREMENTS : DOCKER_SIZE_REQUIREMENTS;
|
|
259
|
+
const requirements = sizeRequirements[size];
|
|
260
|
+
const warnings = [];
|
|
261
|
+
const isVm = type === 'vm';
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(chalk_1.default.blue('═══════════════════════════════════════════════════════════'));
|
|
264
|
+
console.log(chalk_1.default.blue.bold(` ${isVm ? 'Multipass VM' : 'Docker Container'} Resource Requirements`));
|
|
265
|
+
console.log(chalk_1.default.blue('═══════════════════════════════════════════════════════════'));
|
|
266
|
+
console.log('');
|
|
267
|
+
// Show what will be allocated
|
|
268
|
+
console.log(chalk_1.default.cyan(' This genbox will use:'));
|
|
269
|
+
console.log(` CPUs: ${chalk_1.default.yellow(requirements.cpus)} cores`);
|
|
270
|
+
console.log(` Memory: ${chalk_1.default.yellow(formatGB(requirements.memoryGB))}`);
|
|
271
|
+
console.log(` Disk: ${chalk_1.default.yellow(formatGB(requirements.diskGB))}`);
|
|
272
|
+
console.log('');
|
|
273
|
+
// Show current system state
|
|
274
|
+
console.log(chalk_1.default.cyan(' Your system:'));
|
|
275
|
+
console.log(` CPUs: ${chalk_1.default.green(resources.cpuCores)} cores`);
|
|
276
|
+
console.log(` Total Memory: ${chalk_1.default.green(formatGB(resources.totalMemoryGB))}`);
|
|
277
|
+
console.log(` Free Memory: ${resources.availableMemoryGB < requirements.memoryGB ? chalk_1.default.red(formatGB(resources.availableMemoryGB)) : chalk_1.default.green(formatGB(resources.availableMemoryGB))}`);
|
|
278
|
+
console.log(` Free Disk: ${resources.diskSpaceGB < requirements.diskGB ? chalk_1.default.red(formatGB(resources.diskSpaceGB)) : chalk_1.default.green(formatGB(resources.diskSpaceGB))}`);
|
|
279
|
+
if (isVm && resources.existingVms > 0) {
|
|
280
|
+
console.log(` Existing VMs: ${chalk_1.default.yellow(resources.existingVms)}`);
|
|
281
|
+
}
|
|
282
|
+
if (!isVm && resources.existingContainers > 0) {
|
|
283
|
+
console.log(` Existing Containers: ${chalk_1.default.yellow(resources.existingContainers)}`);
|
|
284
|
+
}
|
|
285
|
+
console.log('');
|
|
286
|
+
// Check for issues
|
|
287
|
+
if (resources.availableMemoryGB < requirements.memoryGB) {
|
|
288
|
+
warnings.push(`Low memory: ${formatGB(resources.availableMemoryGB)} available, ${formatGB(requirements.memoryGB)} needed`);
|
|
289
|
+
}
|
|
290
|
+
if (resources.diskSpaceGB < requirements.diskGB) {
|
|
291
|
+
warnings.push(`Low disk space: ${formatGB(resources.diskSpaceGB)} available, ${formatGB(requirements.diskGB)} needed`);
|
|
292
|
+
}
|
|
293
|
+
if (resources.cpuCores < requirements.cpus + 2) {
|
|
294
|
+
warnings.push(`Limited CPUs: ${resources.cpuCores} cores total, allocating ${requirements.cpus} may slow your system`);
|
|
295
|
+
}
|
|
296
|
+
if (isVm && !resources.multipassRunning) {
|
|
297
|
+
warnings.push('Multipass is not installed');
|
|
298
|
+
}
|
|
299
|
+
if (!isVm && !resources.dockerRunning) {
|
|
300
|
+
warnings.push('Docker is not running');
|
|
301
|
+
}
|
|
302
|
+
// Show warnings if any
|
|
303
|
+
if (warnings.length > 0) {
|
|
304
|
+
console.log(chalk_1.default.yellow(' ⚠️ Warnings:'));
|
|
305
|
+
for (const warning of warnings) {
|
|
306
|
+
console.log(chalk_1.default.yellow(` • ${warning}`));
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
}
|
|
310
|
+
// Block creation if critical resources missing
|
|
311
|
+
if ((isVm && !resources.multipassRunning) || (!isVm && !resources.dockerRunning)) {
|
|
312
|
+
console.log(chalk_1.default.red(` ✗ Cannot create ${isVm ? 'VM' : 'container'}: ${isVm ? 'Multipass' : 'Docker'} is not available.`));
|
|
313
|
+
if (isVm) {
|
|
314
|
+
console.log(chalk_1.default.dim(' Install from: https://canonical.com/multipass/install'));
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
console.log(chalk_1.default.dim(' Start Docker Desktop or run: sudo systemctl start docker'));
|
|
318
|
+
}
|
|
319
|
+
console.log('');
|
|
320
|
+
return { confirmed: false, cancelled: false };
|
|
321
|
+
}
|
|
322
|
+
// Block if truly insufficient resources
|
|
323
|
+
if (resources.diskSpaceGB < requirements.diskGB * 0.5) {
|
|
324
|
+
console.log(chalk_1.default.red(' ✗ Insufficient disk space. Please free up disk space before creating a genbox.'));
|
|
325
|
+
console.log('');
|
|
326
|
+
return { confirmed: false, cancelled: false };
|
|
327
|
+
}
|
|
328
|
+
console.log(chalk_1.default.blue('═══════════════════════════════════════════════════════════'));
|
|
329
|
+
console.log('');
|
|
330
|
+
// Ask for confirmation
|
|
331
|
+
try {
|
|
332
|
+
const proceed = await (0, prompts_1.confirm)({
|
|
333
|
+
message: warnings.length > 0
|
|
334
|
+
? 'Continue despite warnings?'
|
|
335
|
+
: 'Create this genbox?',
|
|
336
|
+
default: warnings.length === 0,
|
|
337
|
+
});
|
|
338
|
+
return { confirmed: proceed, cancelled: false };
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return { confirmed: false, cancelled: true };
|
|
342
|
+
}
|
|
343
|
+
}
|
|
64
344
|
/**
|
|
65
345
|
* Get SSH key paths
|
|
66
346
|
*/
|
|
@@ -298,12 +578,22 @@ function showProgress(status, elapsed, percent) {
|
|
|
298
578
|
* Create a local Docker genbox
|
|
299
579
|
*/
|
|
300
580
|
async function createLocalDockerGenbox(name, provider = 'claude') {
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
581
|
+
// Get system resources
|
|
582
|
+
const resources = getSystemResources();
|
|
583
|
+
// Prompt for size selection
|
|
584
|
+
const selectedSize = await promptForSize('docker', resources);
|
|
585
|
+
if (!selectedSize) {
|
|
586
|
+
return { success: false, error: 'Cancelled' };
|
|
587
|
+
}
|
|
588
|
+
// Show resource summary and get confirmation
|
|
589
|
+
const { confirmed, cancelled } = await confirmResourceUsage('docker', resources, selectedSize);
|
|
590
|
+
if (cancelled) {
|
|
591
|
+
return { success: false, error: 'Cancelled' };
|
|
592
|
+
}
|
|
593
|
+
if (!confirmed) {
|
|
594
|
+
return { success: false, error: 'Resource check failed or user declined' };
|
|
306
595
|
}
|
|
596
|
+
const sizeReqs = DOCKER_SIZE_REQUIREMENTS[selectedSize];
|
|
307
597
|
const startTime = Date.now();
|
|
308
598
|
showProgress('Creating Docker container', 0, 10);
|
|
309
599
|
try {
|
|
@@ -330,6 +620,7 @@ async function createLocalDockerGenbox(name, provider = 'claude') {
|
|
|
330
620
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
331
621
|
console.log(` ${chalk_1.default.bold('Name:')} ${session.name}`);
|
|
332
622
|
console.log(` ${chalk_1.default.bold('Type:')} Docker container`);
|
|
623
|
+
console.log(` ${chalk_1.default.bold('Size:')} ${sizeReqs.label} (${sizeReqs.cpus} CPU, ${sizeReqs.memoryGB}GB RAM)`);
|
|
333
624
|
console.log(` ${chalk_1.default.bold('Provider:')} ${provider}`);
|
|
334
625
|
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.green('running')}`);
|
|
335
626
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
@@ -355,12 +646,22 @@ async function createLocalDockerGenbox(name, provider = 'claude') {
|
|
|
355
646
|
* Create a local VM genbox (Multipass)
|
|
356
647
|
*/
|
|
357
648
|
async function createLocalVmGenbox(name, provider = 'claude') {
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
649
|
+
// Get system resources
|
|
650
|
+
const resources = getSystemResources();
|
|
651
|
+
// Prompt for size selection
|
|
652
|
+
const selectedSize = await promptForSize('vm', resources);
|
|
653
|
+
if (!selectedSize) {
|
|
654
|
+
return { success: false, error: 'Cancelled' };
|
|
655
|
+
}
|
|
656
|
+
// Show resource summary and get confirmation
|
|
657
|
+
const { confirmed, cancelled } = await confirmResourceUsage('vm', resources, selectedSize);
|
|
658
|
+
if (cancelled) {
|
|
659
|
+
return { success: false, error: 'Cancelled' };
|
|
660
|
+
}
|
|
661
|
+
if (!confirmed) {
|
|
662
|
+
return { success: false, error: 'Resource check failed or user declined' };
|
|
363
663
|
}
|
|
664
|
+
const sizeReqs = VM_SIZE_REQUIREMENTS[selectedSize];
|
|
364
665
|
const startTime = Date.now();
|
|
365
666
|
// Progress stages with estimated percentages
|
|
366
667
|
const stages = [
|
|
@@ -401,13 +702,13 @@ packages:
|
|
|
401
702
|
// Write cloud-init to temp file
|
|
402
703
|
const cloudInitPath = path.join(os.tmpdir(), `genbox-cloud-init-${name}.yaml`);
|
|
403
704
|
fs.writeFileSync(cloudInitPath, cloudInitConfig);
|
|
404
|
-
// Create Multipass VM with cloud-init
|
|
705
|
+
// Create Multipass VM with cloud-init (using selected size)
|
|
405
706
|
const vmProcess = (0, child_process_1.spawn)('multipass', [
|
|
406
707
|
'launch',
|
|
407
708
|
'--name', name,
|
|
408
|
-
'--cpus',
|
|
409
|
-
'--memory',
|
|
410
|
-
'--disk',
|
|
709
|
+
'--cpus', String(sizeReqs.cpus),
|
|
710
|
+
'--memory', `${sizeReqs.memoryGB}G`,
|
|
711
|
+
'--disk', `${sizeReqs.diskGB}G`,
|
|
411
712
|
'--cloud-init', cloudInitPath,
|
|
412
713
|
], {
|
|
413
714
|
stdio: 'pipe',
|
|
@@ -490,6 +791,7 @@ packages:
|
|
|
490
791
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
491
792
|
console.log(` ${chalk_1.default.bold('Name:')} ${session.name}`);
|
|
492
793
|
console.log(` ${chalk_1.default.bold('Type:')} Multipass VM`);
|
|
794
|
+
console.log(` ${chalk_1.default.bold('Size:')} ${sizeReqs.label} (${sizeReqs.cpus} CPU, ${sizeReqs.memoryGB}GB RAM, ${sizeReqs.diskGB}GB disk)`);
|
|
493
795
|
console.log(` ${chalk_1.default.bold('Provider:')} ${provider}`);
|
|
494
796
|
if (vmIp) {
|
|
495
797
|
console.log(` ${chalk_1.default.bold('IP:')} ${vmIp}`);
|