Files
mcp_web_dev_server/dist/tools/docker/buildImage.js
T
2026-07-31 13:12:54 -04:00

92 lines
3.7 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { z } from 'zod';
import { config } from '../../config.js';
import { resolveWorkspacePath } from '../../lib/sandbox.js';
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';
import { dockerManager } from './dockerManager.js';
export function registerDockerBuildImageTool(server) {
server.registerTool('docker_build_image', {
title: 'Docker Build Image',
description: 'Build a Docker image from a Dockerfile in the workspace. Streams the build output back and returns the resulting image tag.',
inputSchema: {
tag: z.string().min(1).describe('Image tag, e.g. "my-app:latest".'),
dockerfile: z
.string()
.optional()
.default('Dockerfile')
.describe('Path to the Dockerfile, relative to the workspace root.'),
buildContext: z
.string()
.optional()
.default('.')
.describe('Build context directory, relative to the workspace root.'),
},
}, async ({ tag, dockerfile, buildContext }) => {
try {
await dockerManager.ensureReachable();
const dockerfilePath = resolveWorkspacePath(config.workspaceRoot, dockerfile);
const contextPath = resolveWorkspacePath(config.workspaceRoot, buildContext);
if (!fs.existsSync(dockerfilePath)) {
return errorResult(`Dockerfile not found: ${dockerfilePath}`);
}
const stream = await dockerManager.getDocker().buildImage({
context: contextPath,
src: [path.basename(dockerfilePath), ...listContextFiles(contextPath)],
}, { t: tag, dockerfile: path.basename(dockerfilePath) });
const output = [];
await new Promise((resolve, reject) => {
dockerManager
.getDocker()
.modem.followProgress(stream, (err, res) => {
if (err) {
reject(err);
return;
}
const last = res?.[res.length - 1];
if (last && 'error' in last && last.error) {
reject(new Error(String(last.error)));
return;
}
resolve();
}, (event) => {
const line = typeof event === 'string' ? event : JSON.stringify(event);
output.push(line);
if (output.length > config.maxBufferedLogLines) {
output.splice(0, output.length - config.maxBufferedLogLines);
}
});
});
return jsonResult({
tag,
dockerfile: dockerfilePath,
context: contextPath,
output: output.join('\n').slice(-config.maxToolOutputChars),
});
}
catch (error) {
return errorResult(toErrorMessage(error));
}
});
}
function listContextFiles(root) {
const files = [];
const walk = (dir) => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
const rel = path.relative(root, full);
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist')
continue;
if (entry.isDirectory()) {
walk(full);
}
else {
files.push(rel);
}
}
};
walk(root);
return files;
}
//# sourceMappingURL=buildImage.js.map