2026-02-23 19:57:19 +01:00
|
|
|
/**
|
|
|
|
|
* Stealth configuration and platform detection for cloakbrowser.
|
|
|
|
|
* Mirrors Python cloakbrowser/config.py.
|
|
|
|
|
*/
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
import fs from "node:fs";
|
2026-02-23 19:57:19 +01:00
|
|
|
import os from "node:os";
|
|
|
|
|
import path from "node:path";
|
2026-03-03 00:46:15 +01:00
|
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
|
|
|
|
|
|
// Read wrapper version from package.json (single source of truth)
|
|
|
|
|
let WRAPPER_VERSION = "0.0.0";
|
|
|
|
|
try {
|
|
|
|
|
const _configDir = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
|
const _pkgPath = path.resolve(_configDir, "..", "package.json");
|
|
|
|
|
const _pkg = JSON.parse(fs.readFileSync(_pkgPath, "utf-8")) as { version: string };
|
|
|
|
|
WRAPPER_VERSION = _pkg.version;
|
|
|
|
|
} catch {
|
|
|
|
|
// Fallback — package.json not found (bundled or unusual layout).
|
|
|
|
|
// Wrapper update check will compare against 0.0.0 and always suggest updating.
|
|
|
|
|
}
|
|
|
|
|
export { WRAPPER_VERSION };
|
2026-02-23 19:57:19 +01:00
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-03-02 08:09:43 +01:00
|
|
|
// Chromium version shipped with this release.
|
2026-03-03 06:31:07 +01:00
|
|
|
// Different platforms may ship different versions during transition periods.
|
2026-03-02 08:09:43 +01:00
|
|
|
// CHROMIUM_VERSION is the latest across all platforms (for display/reference).
|
|
|
|
|
// Use getChromiumVersion() for the current platform's actual version.
|
2026-02-23 19:57:19 +01:00
|
|
|
// ---------------------------------------------------------------------------
|
2026-04-15 18:20:01 +02:00
|
|
|
export const CHROMIUM_VERSION = "146.0.7680.177.2";
|
2026-02-23 19:57:19 +01:00
|
|
|
|
2026-03-02 08:09:43 +01:00
|
|
|
export const PLATFORM_CHROMIUM_VERSIONS: Record<string, string> = {
|
2026-04-10 22:42:29 +02:00
|
|
|
"linux-x64": "146.0.7680.177.2",
|
|
|
|
|
"linux-arm64": "146.0.7680.177.2",
|
2026-03-04 01:21:52 +01:00
|
|
|
"darwin-arm64": "145.0.7632.109.2",
|
|
|
|
|
"darwin-x64": "145.0.7632.109.2",
|
2026-03-15 02:42:13 +01:00
|
|
|
"windows-x64": "145.0.7632.159.7",
|
2026-03-02 08:09:43 +01:00
|
|
|
};
|
|
|
|
|
|
2026-02-23 19:57:19 +01:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Platform detection
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
const SUPPORTED_PLATFORMS: Record<string, string> = {
|
|
|
|
|
"linux-x64": "linux-x64",
|
|
|
|
|
"linux-arm64": "linux-arm64",
|
|
|
|
|
"darwin-arm64": "darwin-arm64",
|
|
|
|
|
"darwin-x64": "darwin-x64",
|
2026-03-03 06:31:07 +01:00
|
|
|
"win32-x64": "windows-x64",
|
2026-02-23 19:57:19 +01:00
|
|
|
};
|
|
|
|
|
|
2026-03-02 08:09:43 +01:00
|
|
|
// Platforms with pre-built binaries available for download (derived from version map).
|
|
|
|
|
const AVAILABLE_PLATFORMS = new Set(Object.keys(PLATFORM_CHROMIUM_VERSIONS));
|
|
|
|
|
|
|
|
|
|
export function getChromiumVersion(): string {
|
|
|
|
|
const tag = getPlatformTag();
|
|
|
|
|
return PLATFORM_CHROMIUM_VERSIONS[tag] ?? CHROMIUM_VERSION;
|
|
|
|
|
}
|
2026-02-25 19:20:57 +01:00
|
|
|
|
2026-02-23 19:57:19 +01:00
|
|
|
export function getPlatformTag(): string {
|
|
|
|
|
const platform = process.platform;
|
|
|
|
|
const arch = process.arch;
|
|
|
|
|
|
|
|
|
|
// Map Node.js platform/arch to our tag format
|
|
|
|
|
let key: string;
|
|
|
|
|
if (platform === "linux" && arch === "x64") key = "linux-x64";
|
|
|
|
|
else if (platform === "linux" && arch === "arm64") key = "linux-arm64";
|
|
|
|
|
else if (platform === "darwin" && arch === "arm64") key = "darwin-arm64";
|
|
|
|
|
else if (platform === "darwin" && arch === "x64") key = "darwin-x64";
|
2026-03-03 06:31:07 +01:00
|
|
|
else if (platform === "win32" && arch === "x64") key = "win32-x64";
|
2026-02-23 19:57:19 +01:00
|
|
|
else {
|
|
|
|
|
const supported = Object.values(SUPPORTED_PLATFORMS).join(", ");
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Unsupported platform: ${platform} ${arch}. Supported: ${supported}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return SUPPORTED_PLATFORMS[key]!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Binary cache paths
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
export function getCacheDir(): string {
|
|
|
|
|
const custom = process.env.CLOAKBROWSER_CACHE_DIR;
|
|
|
|
|
if (custom) return custom;
|
|
|
|
|
return path.join(os.homedir(), ".cloakbrowser");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export function getBinaryDir(version?: string): string {
|
2026-03-02 08:09:43 +01:00
|
|
|
return path.join(getCacheDir(), `chromium-${version || getChromiumVersion()}`);
|
2026-02-23 19:57:19 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export function getBinaryPath(version?: string): string {
|
|
|
|
|
const binaryDir = getBinaryDir(version);
|
2026-02-23 19:57:19 +01:00
|
|
|
if (process.platform === "darwin") {
|
|
|
|
|
return path.join(binaryDir, "Chromium.app", "Contents", "MacOS", "Chromium");
|
|
|
|
|
}
|
2026-03-03 06:31:07 +01:00
|
|
|
if (process.platform === "win32") {
|
|
|
|
|
return path.join(binaryDir, "chrome.exe");
|
|
|
|
|
}
|
2026-02-23 19:57:19 +01:00
|
|
|
return path.join(binaryDir, "chrome");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export function checkPlatformAvailable(): void {
|
|
|
|
|
if (getLocalBinaryOverride()) return;
|
|
|
|
|
|
|
|
|
|
const tag = getPlatformTag(); // throws if unsupported entirely
|
|
|
|
|
if (!AVAILABLE_PLATFORMS.has(tag)) {
|
|
|
|
|
const available = [...AVAILABLE_PLATFORMS].sort().join(", ");
|
|
|
|
|
throw new Error(
|
2026-03-03 06:31:07 +01:00
|
|
|
`CloakBrowser — Pre-built binaries are currently only available for: ${available}.\n\n` +
|
|
|
|
|
`To use CloakBrowser now, set CLOAKBROWSER_BINARY_PATH to a local Chromium binary.`
|
2026-02-25 19:20:57 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 19:57:19 +01:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Download URL
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-02-25 19:20:57 +01:00
|
|
|
export const DOWNLOAD_BASE_URL =
|
2026-02-23 19:57:19 +01:00
|
|
|
process.env.CLOAKBROWSER_DOWNLOAD_URL ||
|
2026-02-26 06:17:57 +01:00
|
|
|
"https://cloakbrowser.dev";
|
2026-02-23 19:57:19 +01:00
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export const GITHUB_API_URL =
|
|
|
|
|
"https://api.github.com/repos/CloakHQ/cloakbrowser/releases";
|
|
|
|
|
|
2026-02-27 04:12:49 +01:00
|
|
|
export const GITHUB_DOWNLOAD_BASE_URL =
|
|
|
|
|
"https://github.com/CloakHQ/cloakbrowser/releases/download";
|
|
|
|
|
|
2026-03-04 01:21:52 +01:00
|
|
|
export function getArchiveExt(): string {
|
|
|
|
|
return process.platform === "win32" ? ".zip" : ".tar.gz";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getArchiveName(tag?: string): string {
|
|
|
|
|
return `cloakbrowser-${tag || getPlatformTag()}${getArchiveExt()}`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export function getDownloadUrl(version?: string): string {
|
2026-03-02 08:09:43 +01:00
|
|
|
const v = version || getChromiumVersion();
|
2026-03-04 01:21:52 +01:00
|
|
|
return `${DOWNLOAD_BASE_URL}/chromium-v${v}/${getArchiveName()}`;
|
2026-02-25 19:20:57 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-27 04:12:49 +01:00
|
|
|
export function getFallbackDownloadUrl(version?: string): string {
|
2026-03-02 08:09:43 +01:00
|
|
|
const v = version || getChromiumVersion();
|
2026-03-04 01:21:52 +01:00
|
|
|
return `${GITHUB_DOWNLOAD_BASE_URL}/chromium-v${v}/${getArchiveName()}`;
|
2026-02-27 04:12:49 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-25 19:20:57 +01:00
|
|
|
export function getEffectiveVersion(): string {
|
2026-03-02 08:09:43 +01:00
|
|
|
const base = getChromiumVersion();
|
2026-03-02 09:03:57 +01:00
|
|
|
const cacheDir = getCacheDir();
|
|
|
|
|
// Try platform-scoped marker first, fall back to legacy marker for upgrades from <0.3.0
|
|
|
|
|
for (const name of [`latest_version_${getPlatformTag()}`, "latest_version"]) {
|
|
|
|
|
const marker = path.join(cacheDir, name);
|
|
|
|
|
try {
|
|
|
|
|
if (fs.existsSync(marker)) {
|
|
|
|
|
const version = fs.readFileSync(marker, "utf-8").trim();
|
|
|
|
|
if (version && versionNewer(version, base)) {
|
|
|
|
|
const binary = getBinaryPath(version);
|
|
|
|
|
if (fs.existsSync(binary)) {
|
|
|
|
|
return version;
|
|
|
|
|
}
|
2026-02-25 19:20:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-02 09:03:57 +01:00
|
|
|
} catch {
|
|
|
|
|
// Marker unreadable — try next
|
2026-02-25 19:20:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-02 08:09:43 +01:00
|
|
|
return base;
|
2026-02-25 19:20:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseVersion(v: string): number[] {
|
|
|
|
|
return v.split(".").map(Number);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function versionNewer(a: string, b: string): boolean {
|
|
|
|
|
const va = parseVersion(a);
|
|
|
|
|
const vb = parseVersion(b);
|
|
|
|
|
for (let i = 0; i < Math.max(va.length, vb.length); i++) {
|
|
|
|
|
if ((va[i] ?? 0) > (vb[i] ?? 0)) return true;
|
|
|
|
|
if ((va[i] ?? 0) < (vb[i] ?? 0)) return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2026-02-23 19:57:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Local binary override
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
export function getLocalBinaryOverride(): string | undefined {
|
|
|
|
|
return process.env.CLOAKBROWSER_BINARY_PATH || undefined;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 20:22:10 +01:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Playwright default args to suppress — these leak automation signals.
|
|
|
|
|
// --enable-automation: exposes navigator.webdriver = true
|
|
|
|
|
// --enable-unsafe-swiftshader: forces software WebGL rendering via SwiftShader,
|
|
|
|
|
// producing a distinctive renderer string that no real user browser has
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
export const IGNORE_DEFAULT_ARGS = ["--enable-automation", "--enable-unsafe-swiftshader"];
|
|
|
|
|
|
2026-02-23 19:57:19 +01:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// Default stealth arguments
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2026-02-27 03:56:54 +01:00
|
|
|
// Default viewport — realistic maximized Chrome on 1080p Windows
|
2026-03-02 18:20:57 +01:00
|
|
|
// screen=1920x1080, availHeight=1032 (minus 48px taskbar, binary default),
|
|
|
|
|
// innerHeight=947 (minus ~85px Chrome UI: tabs + address bar + bookmarks)
|
|
|
|
|
export const DEFAULT_VIEWPORT = { width: 1920, height: 947 };
|
2026-02-27 03:56:54 +01:00
|
|
|
|
2026-02-23 19:57:19 +01:00
|
|
|
export function getDefaultStealthArgs(): string[] {
|
|
|
|
|
const seed = Math.floor(Math.random() * 90000) + 10000; // 10000-99999
|
2026-02-27 02:15:36 +01:00
|
|
|
const isMac = process.platform === "darwin";
|
|
|
|
|
|
|
|
|
|
const base = [
|
2026-02-23 19:57:19 +01:00
|
|
|
"--no-sandbox",
|
|
|
|
|
`--fingerprint=${seed}`,
|
2026-02-27 02:15:36 +01:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (isMac) {
|
|
|
|
|
// macOS: run as native Mac browser — GPU/UA match natively
|
2026-04-07 07:16:22 +02:00
|
|
|
return [...base, "--fingerprint-platform=macos"];
|
2026-02-27 02:15:36 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 21:19:34 +01:00
|
|
|
// Linux/Windows: spoof as Windows desktop
|
2026-04-07 07:16:22 +02:00
|
|
|
// Hardware concurrency, device memory, screen, window size, and GPU are
|
2026-03-03 21:19:34 +01:00
|
|
|
// auto-generated by the binary from the seed (v14+).
|
2026-04-07 07:16:22 +02:00
|
|
|
return [...base, "--fingerprint-platform=windows"];
|
2026-02-23 19:57:19 +01:00
|
|
|
}
|