import Docker from "dockerode"; const DEFAULT_DOCKER_PROBE_TIMEOUT_MS = 4000; const WINDOWS_DOCKER_SOCKET_CANDIDATES = [ "//./pipe/docker_engine", "//./pipe/dockerDesktopLinuxEngine", ] as const; const POSIX_DOCKER_SOCKET_CANDIDATES = [ "/var/run/docker.sock", ] as const; export interface DockerClientResolution { docker: Docker; socketPath: string; } export interface DockerAvailabilityResult { available: boolean; docker?: Docker; socketPath?: string; reason?: string; } function getConfiguredSocketPath(): string | null { const configuredSocketPath = process.env.DOCKER_SOCKET_PATH?.trim(); return configuredSocketPath ? configuredSocketPath : null; } export function getDockerSocketCandidates(): string[] { const configuredSocketPath = getConfiguredSocketPath(); const platformCandidates = process.platform === "win32" ? [...WINDOWS_DOCKER_SOCKET_CANDIDATES] : [...POSIX_DOCKER_SOCKET_CANDIDATES]; return configuredSocketPath ? [configuredSocketPath, ...platformCandidates.filter((candidate) => candidate !== configuredSocketPath)] : platformCandidates; } export function createDockerClient(socketPath?: string): Docker { const resolvedSocketPath = socketPath ?? getDockerSocketCandidates()[0]; return new Docker({ socketPath: resolvedSocketPath }); } async function pingDockerClient(docker: Docker, timeoutMs: number): Promise { let timeoutId: NodeJS.Timeout | null = null; try { await Promise.race([ docker.ping().then(() => undefined), new Promise((_, reject) => { timeoutId = setTimeout(() => reject(new Error(`Docker ping timed out after ${timeoutMs}ms`)), timeoutMs); }), ]); } finally { if (timeoutId) { clearTimeout(timeoutId); } } } export async function resolveDockerClient(timeoutMs: number = DEFAULT_DOCKER_PROBE_TIMEOUT_MS): Promise { for (const socketPath of getDockerSocketCandidates()) { const docker = createDockerClient(socketPath); try { await pingDockerClient(docker, timeoutMs); return { docker, socketPath }; } catch { continue; } } return null; } export async function probeDockerAvailability(timeoutMs: number = DEFAULT_DOCKER_PROBE_TIMEOUT_MS): Promise { const resolution = await resolveDockerClient(timeoutMs); if (!resolution) { return { available: false, reason: "Docker daemon unreachable", }; } return { available: true, docker: resolution.docker, socketPath: resolution.socketPath, }; }