UNPKG

@jjdenhertog/ai-driven-development

Version:

AI-driven development workflow with learning capabilities for Claude

191 lines 10.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.containerStartCommand = containerStartCommand; /* eslint-disable max-depth */ /* eslint-disable unicorn/no-array-push-push */ const fs_extra_1 = require("fs-extra"); const node_child_process_1 = require("node:child_process"); const node_path_1 = require("node:path"); const node_util_1 = require("node:util"); const checkDockerAvailable_1 = require("../utils/docker/checkDockerAvailable"); const getContainerName_1 = require("../utils/docker/getContainerName"); const getContainerStatus_1 = require("../utils/docker/getContainerStatus"); const isContainerRunning_1 = require("../utils/docker/isContainerRunning"); const logger_1 = require("../utils/logger"); const isRunningInDocker_1 = require("../utils/docker/isRunningInDocker"); const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec); function containerStartCommand(options) { return __awaiter(this, void 0, void 0, function* () { const { name, type, path = process.cwd(), proxyPort = 8888 } = options; try { // Check Docker availability const docker = yield (0, checkDockerAvailable_1.checkDockerAvailable)(); if (!docker.available) { (0, logger_1.log)(docker.error || 'Docker is not available', 'error'); throw new Error('Docker is required to run containers'); } // Check if .aidev-containers exists const devcontainerPath = (0, node_path_1.join)(path, '.aidev-containers'); if (!(0, fs_extra_1.existsSync)(devcontainerPath)) { (0, logger_1.log)('No .aidev-containers directory found. Run "aidev init" first.', 'error'); (0, logger_1.log)(path, 'info'); throw new Error('.aidev-containers directory not found'); } // Determine which container config to use const configType = type || name; (0, logger_1.log)(`Starting container '${name}' with type '${configType}'...`, 'info'); // Check if specific container config exists const configPath = (0, node_path_1.join)(devcontainerPath, configType, 'devcontainer.json'); if (!(0, fs_extra_1.existsSync)(configPath)) { (0, logger_1.log)(`No container configuration found for type '${configType}' at ${configPath}`, 'error'); (0, logger_1.log)(`Available types: code, learn, plan, web`, 'info'); throw new Error(`Missing ${configType} container configuration`); } // Get the container name const containerName = (0, getContainerName_1.getContainerName)(name); if (yield (0, isContainerRunning_1.isContainerRunning)(containerName)) { (0, logger_1.log)(`Container ${containerName} is already running`, 'warn'); return; } // Check if container exists but is stopped const status = yield (0, getContainerStatus_1.getContainerStatus)(containerName); if (status && status.state !== 'running') { (0, logger_1.log)(`Starting existing container ${containerName}...`, 'info'); yield execAsync(`docker start ${containerName}`, { cwd: path }); (0, logger_1.log)(`Container ${containerName} started successfully`, 'success'); return; } // Build the Docker image (0, logger_1.log)(`Building Docker image for ${name} using ${configType} configuration...`, 'info'); const dockerfilePath = (0, node_path_1.join)(devcontainerPath, configType, 'Dockerfile'); try { yield execAsync(`docker build -f "${dockerfilePath}" -t ${containerName} "${devcontainerPath}"`, { maxBuffer: 1024 * 1024 * 10, cwd: path } // 10MB buffer for build output ); (0, logger_1.log)(`Docker image built successfully`, 'success'); } catch (error) { (0, logger_1.log)(`Failed to build Docker image: ${error instanceof Error ? error.message : String(error)}`, 'error'); throw error; } // Prepare run command const runArgs = [ 'run', '-dit', // -d for detached, -i for interactive, -t for tty '--name', containerName ]; if (process.env.AIDEV_HOST_WORKSPACE) { // Running in a standardized workstation environment const currentPath = path; const workspaceBase = '/workspace'; if (currentPath.startsWith(workspaceBase)) { // Extract relative path from /workspace const relativePath = currentPath.slice(workspaceBase.length); const hostPath = process.env.AIDEV_HOST_WORKSPACE + relativePath; runArgs.push('-v', `${hostPath}:/workspace`); runArgs.push('-v', `${process.env.AIDEV_HOST_WORKSPACE}/.dev-credentials:/credentials`); (0, logger_1.log)(`Using workstation path mapping: ${hostPath}`, 'info'); } else { (0, logger_1.log)('Current directory is not under /workspace', 'error'); throw new Error('When using AIDEV_HOST_WORKSPACE, you must be in /workspace directory'); } } else { // Standard host execution runArgs.push('-v', `${path}:/workspace`); } runArgs.push('--workdir', '/workspace', '--cap-add=NET_ADMIN', '--cap-add=NET_RAW'); // Add environment variables runArgs.push('-e', 'NODE_OPTIONS=--max-old-space-size=4096'); const webPort = process.env.AIDEV_WEB_PORT ? parseInt(process.env.AIDEV_WEB_PORT) : 3001; if (configType === 'web') { const containerId = yield getContainerId(); // Use AIDEV_WEB_PORT if available, otherwise use the port parameter runArgs.push('-p', `${webPort}:${webPort}`); runArgs.push('-e', `AIDEV_WEB_PORT=${webPort}`); runArgs.push('-e', `CONTAINER_PREFIX=${(0, getContainerName_1.getContainerName)('')}`); (0, logger_1.log)(`Web container will run on port ${webPort}`, 'info'); const runningInDocker = (0, isRunningInDocker_1.isRunningInDocker)(); if (runningInDocker) { if (containerId) { const network = yield getCurrentContainerNetwork(containerId); const containerName = yield getCurrentContainerName(containerId); if (network && containerName) { runArgs.push('--network', network); runArgs.push('-e', `AIDEV_HOST_PROXY=http://${containerName}:${proxyPort}`); (0, logger_1.log)(`Container will join network of ${containerName}: ${network}`, 'info'); } } } else { runArgs.push('-e', `AIDEV_HOST_PROXY=http://host.docker.internal:${proxyPort}`); } (0, logger_1.log)(`Container can use a proxy for container management`, 'info'); (0, logger_1.log)(`Make sure to start the proxy with: aidev proxy start`, 'info'); } // Add the container image name runArgs.push(containerName); // Run the container (0, logger_1.log)(`Starting container ${containerName}...`, 'info'); try { yield execAsync(`docker ${runArgs.join(' ')}`, { cwd: path }); (0, logger_1.log)(`Container ${containerName} started successfully`, 'success'); if (configType === 'web') { (0, logger_1.log)(`Web interface will be available at http://localhost:${webPort} once startup is complete`, 'info'); (0, logger_1.log)(`Use "aidev container logs ${name} -f" to monitor startup progress`, 'info'); } else { (0, logger_1.log)(`Use "aidev container exec ${name} <command>" to run commands in the container`, 'info'); } } catch (error) { (0, logger_1.log)(`Failed to start container: ${error instanceof Error ? error.message : String(error)}`, 'error'); throw error; } } catch (error) { (0, logger_1.log)(`Failed to start ${name} container: ${error instanceof Error ? error.message : String(error)}`, 'error'); throw error; } }); } function getContainerId() { return __awaiter(this, void 0, void 0, function* () { const { stdout: containerId } = yield execAsync(`cat /proc/self/cgroup 2>/dev/null | grep -o -E '[0-9a-f]{64}' | head -n 1 || echo ""`); if (containerId.trim()) return containerId.trim(); const { stdout: fallbackId } = yield execAsync(`cat /proc/1/cgroup 2>/dev/null | grep 'docker' | sed 's/^.*\\///' | tail -n 1 || echo ""`); if (fallbackId.trim()) return fallbackId.trim(); }); } function getCurrentContainerName(containerId) { return __awaiter(this, void 0, void 0, function* () { const result = yield execAsync(`docker inspect ${containerId} --format='{{.Name}}'`); return result.stdout.trim().replace(/^\//, ''); }); } function getCurrentContainerNetwork(containerId) { return __awaiter(this, void 0, void 0, function* () { try { const result = yield execAsync(`docker inspect ${containerId} --format='{{range $net, $config := .NetworkSettings.Networks}}{{$net}}{{end}}'`); return result.stdout .trim() .split('\n') .filter(n => n && n !== 'bridge')[0]; } catch (_error) { (0, logger_1.log)('Could not determine current container network', 'warn'); return null; } }); } //# sourceMappingURL=containerStartCommand.js.map