UNPKG

camoufox-mcp-server

Version:

MCP server for browser automation using Camoufox - a privacy-focused Firefox fork with advanced anti-detection features

135 lines (134 loc) 3.89 kB
import { MAX_DIAGNOSTIC_TEXT_CHARS, SUPPORTED_OSES } from "./config.js"; export function describeError(error) { return error instanceof Error ? error.message : String(error); } export async function withTimeout(promise, ms, label) { let timer; const timeout = new Promise((_, reject) => { timer = setTimeout(() => { reject(new Error(`${label} timed out.`)); }, ms); }); try { return await Promise.race([promise, timeout]); } finally { if (timer) { clearTimeout(timer); } } } export function redactUrl(raw) { try { const url = new URL(raw); url.username = ""; url.password = ""; url.search = url.search ? "?..." : ""; url.hash = ""; return url.toString(); } catch { return "<invalid-url>"; } } export function getProxyServer(proxy) { if (!proxy) { return undefined; } return typeof proxy === "string" ? proxy : proxy.server; } export function getProxySecrets(proxy) { if (!proxy || typeof proxy === "string") { return []; } return [proxy.username, proxy.password].filter((secret) => Boolean(secret)); } export function sanitizeErrorMessage(message, rawUrls, secrets = []) { let sanitized = message; for (const secret of secrets) { sanitized = sanitized.replaceAll(secret, "<redacted>"); } for (const rawUrl of rawUrls) { sanitized = sanitized.replaceAll(rawUrl, redactUrl(rawUrl)); } return sanitized.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, (matchedUrl) => { let suffix = ""; let candidate = matchedUrl; while (candidate.length > 0 && /[),.;\]]$/.test(candidate)) { suffix = `${candidate[candidate.length - 1]}${suffix}`; candidate = candidate.slice(0, -1); } return `${redactUrl(candidate)}${suffix}`; }); } export function truncateString(value, maxChars) { return { value: value.slice(0, maxChars), truncated: value.length > maxChars, }; } export function sanitizeDiagnosticText(value, rawUrls, secrets) { return truncateString(sanitizeErrorMessage(value, rawUrls, secrets), MAX_DIAGNOSTIC_TEXT_CHARS).value; } export function serializeBounded(value, maxChars, rawUrls, secrets) { let serialized; try { const json = JSON.stringify(value); serialized = json === undefined ? "undefined" : json; } catch { serialized = String(value); } return truncateString(sanitizeErrorMessage(serialized, rawUrls, secrets), maxChars); } export function selectOperatingSystem(os) { if (os) { return os; } return SUPPORTED_OSES[Math.floor(Math.random() * SUPPORTED_OSES.length)]; } export function defaultHeadlessMode(headless) { if (headless !== undefined) { return headless; } return process.platform === "linux" ? "virtual" : true; } export function applyStealthProfile(input) { const profile = input.stealthProfile ?? "normal"; const defaults = { humanize: true, geoip: true, block_webrtc: true, block_images: false, block_webgl: false, disable_coop: false, enable_cache: false, includeConsole: false, includeNetwork: false, }; const profileDefaults = { normal: {}, privacy: { block_webgl: true, }, human_assisted: { headless: false, enable_cache: true, captchaPolicy: "pause", }, fast: { block_images: true, humanize: false, }, debug: { includeConsole: true, includeNetwork: true, }, }; return { ...defaults, ...profileDefaults[profile], ...input, stealthProfile: profile, }; }