init project
This commit is contained in:
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
import Docker from 'dockerode';
|
||||
import { config } from '../../config.js';
|
||||
import { resolveWorkspacePath } from '../../lib/sandbox.js';
|
||||
import { logger } from '../../lib/logger.js';
|
||||
class DockerManager {
|
||||
docker;
|
||||
containers = new Map();
|
||||
daemonReachable = null;
|
||||
constructor() {
|
||||
this.docker = new Docker();
|
||||
}
|
||||
async ensureReachable() {
|
||||
if (this.daemonReachable === true)
|
||||
return;
|
||||
try {
|
||||
await this.docker.ping();
|
||||
this.daemonReachable = true;
|
||||
}
|
||||
catch (error) {
|
||||
this.daemonReachable = false;
|
||||
throw new Error('Docker daemon is not reachable. Make sure Docker is running and the current user can access it. ' +
|
||||
`Original error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
getDocker() {
|
||||
return this.docker;
|
||||
}
|
||||
track(containerId, image, name, ports) {
|
||||
if (this.containers.size >= config.maxConcurrentContainers) {
|
||||
throw new Error(`Refusing to start another container: already at the max of ${config.maxConcurrentContainers} concurrent containers.`);
|
||||
}
|
||||
const shortId = containerId.substring(0, 12);
|
||||
const managed = {
|
||||
id: shortId,
|
||||
image,
|
||||
name,
|
||||
startedAt: new Date().toISOString(),
|
||||
ports,
|
||||
};
|
||||
this.containers.set(shortId, managed);
|
||||
return managed;
|
||||
}
|
||||
getTracked(id) {
|
||||
return this.containers.get(id) ?? this.containers.get(id.substring(0, 12));
|
||||
}
|
||||
listTracked() {
|
||||
return Array.from(this.containers.values());
|
||||
}
|
||||
untrack(id) {
|
||||
this.containers.delete(id.substring(0, 12));
|
||||
}
|
||||
async cleanupAll() {
|
||||
for (const [id, managed] of this.containers.entries()) {
|
||||
try {
|
||||
const container = this.docker.getContainer(id);
|
||||
await container.stop({ t: 5 }).catch(() => null);
|
||||
await container.remove({ force: true }).catch(() => null);
|
||||
}
|
||||
catch (error) {
|
||||
logger.error(`failed to cleanup container ${managed.name} (${id})`, {
|
||||
error: error instanceof Error ? error.message : error,
|
||||
});
|
||||
}
|
||||
finally {
|
||||
this.containers.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export const dockerManager = new DockerManager();
|
||||
/**
|
||||
* Validates that a volume mount specification keeps the source path inside the
|
||||
* workspace root. Rejects Docker socket mounts, host-relative escapes, etc.
|
||||
*/
|
||||
export function validateVolumeMount(spec) {
|
||||
const parts = spec.split(':');
|
||||
if (parts.length < 2) {
|
||||
throw new Error(`Invalid volume mount "${spec}": expected format "source:target[:mode]".`);
|
||||
}
|
||||
const source = parts[0] ?? '';
|
||||
const target = parts[1] ?? '';
|
||||
const mode = parts[2] ?? 'rw';
|
||||
if (!source || !target) {
|
||||
throw new Error(`Invalid volume mount "${spec}": source and target are required.`);
|
||||
}
|
||||
const absoluteSource = resolveWorkspacePath(config.workspaceRoot, source);
|
||||
if (absoluteSource.toLowerCase().startsWith('\\\\.\\pipe\\docker_engine') || source.includes('/var/run/docker.sock')) {
|
||||
throw new Error(`Refusing to mount the Docker socket: "${source}".`);
|
||||
}
|
||||
return { source: absoluteSource, target, mode };
|
||||
}
|
||||
export function isAllowListedImage(image) {
|
||||
const base = (image.split(':')[0] ?? '').split('/').pop()?.toLowerCase() ?? '';
|
||||
return config.dockerAllowedBaseImages.some((allowed) => base === allowed || base.startsWith(allowed));
|
||||
}
|
||||
export function isLocallyBuiltImage(image) {
|
||||
// Heuristic: an image built by this server via docker_build_image won't have
|
||||
// a registry prefix. We treat "my-app" or "web-dev-mcp" as local; anything
|
||||
// with a registry host (contains '.' or '/') is not.
|
||||
return !/[/.]/.test(image);
|
||||
}
|
||||
//# sourceMappingURL=dockerManager.js.map
|
||||
Reference in New Issue
Block a user