From 6007a6e5118536d93985ed84a653eba94bfe176a Mon Sep 17 00:00:00 2001 From: CloakHQ Date: Wed, 11 Mar 2026 03:53:39 +0100 Subject: [PATCH] feat: add CLI for binary management (Python + JavaScript) Adds install, info, update, and clear-cache subcommands with visible download progress. Python: `python -m cloakbrowser install`. JavaScript: `npx cloakbrowser install`. Useful for Dockerfiles where silent first-use downloads are hard to debug. Closes #43. --- README.md | 20 +++++++ cloakbrowser/__main__.py | 111 +++++++++++++++++++++++++++++++++++++++ js/README.md | 11 ++++ js/package.json | 3 ++ js/src/cli.ts | 97 ++++++++++++++++++++++++++++++++++ pyproject.toml | 3 ++ 6 files changed, 245 insertions(+) create mode 100644 cloakbrowser/__main__.py create mode 100644 js/src/cli.ts diff --git a/README.md b/README.md index a67b435..796c38c 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,17 @@ Supports all the same options as `launch_context()`: `proxy`, `user_agent`, `vie Async version: `launch_persistent_context_async()`. +### CLI + +Pre-download the binary or check installation status from the command line: + +```bash +python -m cloakbrowser install # Download binary with progress output +python -m cloakbrowser info # Show version, path, platform +python -m cloakbrowser update # Check for and download newer binary +python -m cloakbrowser clear-cache # Remove cached binaries +``` + ### Utility Functions ```python @@ -706,6 +717,15 @@ COPY your_script.py /app/ CMD ["python", "your_script.py"] ``` +**Building your own image from pip** — use `python -m cloakbrowser install` to download the binary during build with visible progress: + +```dockerfile +FROM python:3.12-slim +RUN pip install cloakbrowser && python -m cloakbrowser install +COPY your_script.py /app/ +CMD ["python", "/app/your_script.py"] +``` + **Building from source** — a [`Dockerfile`](Dockerfile) is also included if you prefer to build your own image: ```bash diff --git a/cloakbrowser/__main__.py b/cloakbrowser/__main__.py new file mode 100644 index 0000000..d935976 --- /dev/null +++ b/cloakbrowser/__main__.py @@ -0,0 +1,111 @@ +"""CLI for cloakbrowser — download and manage the stealth Chromium binary. + +Usage: + python -m cloakbrowser install # Download binary (with progress) + python -m cloakbrowser info # Show binary version, path, platform + python -m cloakbrowser update # Check for and download newer binary + python -m cloakbrowser clear-cache # Remove cached binaries +""" + +from __future__ import annotations + +import argparse +import logging +import sys + + +def _setup_logging() -> None: + """Route cloakbrowser logger to stderr with clean output.""" + logging.basicConfig( + level=logging.INFO, + format="%(message)s", + stream=sys.stderr, + force=True, + ) + # Suppress noisy HTTP request logs from httpx + logging.getLogger("httpx").setLevel(logging.WARNING) + + +def cmd_install(args: argparse.Namespace) -> None: + from .download import ensure_binary + + path = ensure_binary() + print(path) + + +def cmd_info(args: argparse.Namespace) -> None: + from .config import get_local_binary_override + from .download import binary_info + + info = binary_info() + override = get_local_binary_override() + + print(f"Version: {info['version']}") + print(f"Platform: {info['platform']}") + print(f"Binary: {info['binary_path']}") + print(f"Installed: {info['installed']}") + print(f"Cache: {info['cache_dir']}") + if override: + print(f"Override: {override} (CLOAKBROWSER_BINARY_PATH)") + + +def cmd_update(args: argparse.Namespace) -> None: + from .download import check_for_update + + logger = logging.getLogger("cloakbrowser") + logger.info("Checking for updates...") + new_version = check_for_update() + if new_version: + print(f"Updated to Chromium {new_version}") + else: + print("Already up to date.") + + +def cmd_clear_cache(args: argparse.Namespace) -> None: + from .config import get_cache_dir + from .download import clear_cache + + if not get_cache_dir().exists(): + print("No cache to clear.") + return + clear_cache() + print("Cache cleared.") + + +def main() -> None: + parser = argparse.ArgumentParser( + prog="cloakbrowser", + description="Manage the CloakBrowser stealth Chromium binary.", + ) + sub = parser.add_subparsers(dest="command") + + sub.add_parser("install", help="Download the Chromium binary") + sub.add_parser("info", help="Show binary version, path, and platform") + sub.add_parser("update", help="Check for and download a newer binary") + sub.add_parser("clear-cache", help="Remove all cached binaries") + + args = parser.parse_args() + if not args.command: + parser.print_help() + sys.exit(2) + + _setup_logging() + + commands = { + "install": cmd_install, + "info": cmd_info, + "update": cmd_update, + "clear-cache": cmd_clear_cache, + } + + try: + commands[args.command](args) + except KeyboardInterrupt: + sys.exit(130) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/js/README.md b/js/README.md index f1d5855..3a2b58f 100644 --- a/js/README.md +++ b/js/README.md @@ -133,6 +133,17 @@ const browser = await launch({ proxy: 'http://proxy:8080', geoip: true, timezone > **Note:** For rotating residential proxies, the DNS-resolved IP may differ from the exit IP. Pass explicit `timezone`/`locale` in those cases. +### CLI + +Pre-download the binary or check installation status from the command line: + +```bash +npx cloakbrowser install # Download binary with progress output +npx cloakbrowser info # Show version, path, platform +npx cloakbrowser update # Check for and download newer binary +npx cloakbrowser clear-cache # Remove cached binaries +``` + ### Utilities ```javascript diff --git a/js/package.json b/js/package.json index 45982f8..188786e 100644 --- a/js/package.json +++ b/js/package.json @@ -15,6 +15,9 @@ "import": "./dist/puppeteer.js" } }, + "bin": { + "cloakbrowser": "./dist/cli.js" + }, "files": [ "dist" ], diff --git a/js/src/cli.ts b/js/src/cli.ts new file mode 100644 index 0000000..b13b0d1 --- /dev/null +++ b/js/src/cli.ts @@ -0,0 +1,97 @@ +#!/usr/bin/env node +/** + * CLI for cloakbrowser — download and manage the stealth Chromium binary. + * + * Usage: + * npx cloakbrowser install # Download binary (with progress) + * npx cloakbrowser info # Show binary version, path, platform + * npx cloakbrowser update # Check for and download newer binary + * npx cloakbrowser clear-cache # Remove cached binaries + */ + +import { ensureBinary, binaryInfo, checkForUpdate, clearCache } from "./download.js"; +import { getLocalBinaryOverride, getCacheDir } from "./config.js"; +import fs from "node:fs"; + +const USAGE = `Usage: cloakbrowser + +Commands: + install Download the Chromium binary + info Show binary version, path, and platform + update Check for and download a newer binary + clear-cache Remove all cached binaries`; + +async function cmdInstall(): Promise { + const binaryPath = await ensureBinary(); + console.log(binaryPath); +} + +function cmdInfo(): void { + const info = binaryInfo(); + const override = getLocalBinaryOverride(); + + console.log(`Version: ${info.version}`); + console.log(`Platform: ${info.platform}`); + console.log(`Binary: ${info.binaryPath}`); + console.log(`Installed: ${info.installed}`); + console.log(`Cache: ${info.cacheDir}`); + if (override) { + console.log(`Override: ${override} (CLOAKBROWSER_BINARY_PATH)`); + } +} + +async function cmdUpdate(): Promise { + console.error("Checking for updates..."); + const newVersion = await checkForUpdate(); + if (newVersion) { + console.log(`Updated to Chromium ${newVersion}`); + } else { + console.log("Already up to date."); + } +} + +function cmdClearCache(): void { + const cacheDir = getCacheDir(); + if (!fs.existsSync(cacheDir)) { + console.log("No cache to clear."); + return; + } + clearCache(); + console.log("Cache cleared."); +} + +async function main(): Promise { + const command = process.argv[2]; + + if (!command || command === "--help" || command === "-h") { + console.log(USAGE); + process.exit(command ? 0 : 2); + } + + try { + switch (command) { + case "install": + await cmdInstall(); + break; + case "info": + cmdInfo(); + break; + case "update": + await cmdUpdate(); + break; + case "clear-cache": + cmdClearCache(); + break; + default: + console.error(`Unknown command: ${command}\n`); + console.log(USAGE); + process.exit(2); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } +} + +main(); diff --git a/pyproject.toml b/pyproject.toml index c0b6a43..fc0796e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,9 @@ geoip = ["geoip2>=4.0"] patchright = ["patchright>=1.40"] dev = ["pytest>=7.0", "pytest-asyncio>=0.23"] +[project.scripts] +cloakbrowser = "cloakbrowser.__main__:main" + [project.urls] Homepage = "https://github.com/CloakHQ/CloakBrowser" Documentation = "https://github.com/CloakHQ/CloakBrowser#readme"