@jjdenhertog/ai-driven-development
Version:
AI-driven development workflow with learning capabilities for Claude
191 lines • 10.9 kB
JavaScript
;
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