UNPKG

@nestbox-ai/cli

Version:

The cli tools that helps developers to build agents

196 lines (166 loc) 8.31 kB
import { Command } from "commander"; import chalk from "chalk"; import ora from "ora"; import { resolveProject } from "../../utils/project"; import { createComputeApis } from "./apiUtils"; import inquirer from "inquirer"; import axios from "axios"; import { userData } from "../../utils/user"; export function registerCreateCommand(computeCommand: Command): void { computeCommand .command('create') .description('Create a new compute instance') .requiredOption('--image <imageId>', 'Image ID to use for the compute instance') .option('--project <projectId>', 'Project ID or name (defaults to the current project)') .action(async (options) => { try { const apis = createComputeApis(); if (!apis) { return; } const { miscellaneousApi, projectsApi, authToken } = apis; // Resolve project using the shared utility const project = await resolveProject(projectsApi, options); const spinner = ora(`Fetching available images...`).start(); try { // Get available images data const response = await miscellaneousApi.miscellaneousControllerGetData(); const availableImages: any = response.data || []; // Find the selected image const selectedImage = availableImages.find((image: any) => image.id === options.image); if (!selectedImage) { spinner.fail(`Image ID '${options.image}' does not exist.`); console.log(chalk.yellow('Available image IDs:')); availableImages.forEach((img: any) => { console.log(` ${chalk.cyan(img.id)} - ${img.name} (${img.type})`); }); return; } spinner.succeed(`Found image: ${selectedImage.name}`); // Ask for instance name first const instanceNameAnswer = await inquirer.prompt([ { type: 'input', name: 'instanceName', message: 'Enter a name for this compute instance:', validate: (input: string) => { if (!input || input.trim() === '') { return 'Instance name cannot be empty'; } return true; } } ]); const instanceName = instanceNameAnswer.instanceName; // Directly create the provisioningParams object with the structure needed for API let provisioningParams: Record<string, any> = {}; if (selectedImage.provisioningParameters && selectedImage.provisioningParameters.properties) { const requiredParams = selectedImage.provisioningParameters.required || []; const paramProperties = selectedImage.provisioningParameters.properties; // Build questions for inquirer const questions = []; for (const [paramName, paramConfig] of Object.entries(paramProperties) as any) { const isRequired = requiredParams.includes(paramName); const paramUI = selectedImage.provisioningParametersUI?.[paramName] || {}; let question: any = { name: paramName, message: paramUI['ui:title'] || `Enter ${paramName}:`, when: isRequired // Only ask required params by default }; // Handle different parameter types if (paramConfig.type === 'string') { if (paramConfig.enum) { question.type = 'list'; question.choices = paramConfig.enum; } else if (paramUI['ui:widget'] === 'password') { question.type = 'password'; } else { question.type = 'input'; } } else if (paramConfig.type === 'array') { if (paramConfig.items && paramConfig.items.enum) { question.type = 'checkbox'; question.choices = paramConfig.items.enum; // Ensure at least one option is selected for required array parameters question.validate = (input: any[]) => { if (isRequired && (!input || input.length === 0)) { return 'Please select at least one option'; } return true; }; } } if (paramUI['ui:help']) { question.message += ` (${paramUI['ui:help']})`; } questions.push(question); } if (questions.length > 0) { console.log(chalk.blue(`\nPlease provide the required parameters for ${selectedImage.name}:`)); const answers = await inquirer.prompt(questions); // Assign the answers directly to provisioningParams for (const [key, value] of Object.entries(answers)) { provisioningParams[key] = value; } } } // Now we have all the required params in provisioningParams console.log(chalk.green(`\nCreating compute instance '${instanceName}' with image '${selectedImage.name}'`)); console.log(chalk.dim('Using the following parameters:')); for (const [key, value] of Object.entries(provisioningParams)) { console.log(chalk.dim(` ${key}: ${Array.isArray(value) ? value.join(', ') : value}`)); } // Start a spinner for the creation process const creationSpinner = ora('Creating compute instance...').start(); const getUserData = await userData(); try { const createParams = { userId: getUserData.id, machineId: selectedImage.id, machineTitle: selectedImage.name, instanceName: instanceName, ...provisioningParams }; const createResponse = await axios.post( `${authToken.serverUrl}/projects/${project.id}/instances`, createParams, { headers: { Authorization: authToken.token, }, } ); creationSpinner.succeed('Compute instance created successfully!'); console.log(chalk.green(`\nInstance '${instanceName}' is now being provisioned`)); console.log(chalk.gray('You can check the status with: nestbox compute list')); console.log(chalk.green("Instance created successfully!")); } catch (createError: any) { creationSpinner.fail('Failed to create compute instance'); if (createError.response && createError.response.status === 401) { console.error(chalk.red('Authentication token has expired. Please login again using "nestbox login <domain>".')); } else if (createError.response) { console.error(chalk.red('API Error:'), createError.response.data?.message || 'Unknown error'); } else { console.error(chalk.red('Error:'), createError.message || 'Unknown error'); } } } catch (error: any) { spinner.fail('Failed to fetch available images'); if (error.response && error.response.status === 401) { console.error(chalk.red('Authentication token has expired. Please login again using "nestbox login <domain>".')); } else if (error.response) { console.error(chalk.red('API Error:'), error.response.data?.message || 'Unknown error'); } else { console.error(chalk.red('Error:'), error.message || 'Unknown error'); } } } catch (error: any) { if (error.response && error.response.status === 401) { console.error(chalk.red('Authentication token has expired. Please login again using "nestbox login <domain>".')); } else { console.error(chalk.red('Error:'), error.message || 'Unknown error'); } } }); }