Local vs Server Environment Detection in Agent Reach: How It Works

Agent Reach distinguishes between local desktop and headless server environments using a multi-signal scoring system in agent_reach/cli.py to automatically select the appropriate backend for each platform.

The Agent Reach repository (Panniantong/Agent-Reach) provides intelligent CLI automation for social media platforms, but it must adapt its behavior depending on whether it runs on a developer's laptop or a remote server. Understanding how local and server environment detection works in Agent Reach is crucial for debugging installation issues and predicting which backends the tool will provision.

How Environment Detection Works

The core detection logic resides in the private helper _detect_environment() inside agent_reach/cli.py (lines 955–994). This function aggregates multiple OS-level indicators and returns either "local" or "server" based on a weighted scoring threshold.

def _detect_environment():
    """Auto‑detect if running on local computer or server."""
    import os
    indicators = 0

    # 1️⃣ SSH session – typical for remote logins

    if os.environ.get("SSH_CONNECTION") or os.environ.get("SSH_CLIENT"):
        indicators += 2

    # 2️⃣ Docker / container environment

    if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
        indicators += 2

    # 3️⃣ Headless display (no X11/Wayland)

    if not os.environ.get("DISPLAY") and not os.environ.get("WAYLAND_DISPLAY"):
        indicators += 1

    # 4️⃣ Cloud‑VM identifiers (Amazon, Google, Azure, DigitalOcean, …)

    for cloud_file in ["/sys/hypervisor/uuid",
                       "/sys/class/dmi/id/product_name"]:
        if os.path.exists(cloud_file):
            try:
                with open(cloud_file) as f:
                    content = f.read().lower()
                if any(x in content
                       for x in ["amazon", "google", "microsoft",
                                 "digitalocean", "linode", "vultr", "hetzner"]):
                    indicators += 2
            except Exception:
                pass

    # 5️⃣ systemd‑detect‑virt (detects virtualisation)

    try:
        import subprocess
        result = subprocess.run(
            ["systemd-detect-virt"], capture_output=True,
            encoding="utf-8", errors="replace", timeout=3)
        if result.returncode == 0 and result.stdout.strip() != "none":
            indicators += 1
    except Exception:
        pass

    return "server" if indicators >= 2 else "local"

Detection Signals and Scoring

The function checks five distinct signals, each assigned a weight reflecting its reliability:

  • SSH sessions (SSH_CONNECTION or SSH_CLIENT environment variables): Adds 2 points — a strong indicator of remote server access.
  • Container environments (/.dockerenv or /run/.containerenv): Adds 2 points — unambiguous proof of containerized deployment.
  • Headless display (missing DISPLAY or WAYLAND_DISPLAY): Adds 1 point — suggests no GUI, but alone could indicate a minimal desktop setup.
  • Cloud VM identifiers (reading /sys/hypervisor/uuid or /sys/class/dmi/id/product_name for Amazon, Google, Microsoft, DigitalOcean, etc.): Adds 2 points — confirms virtualized cloud infrastructure.
  • Virtualization detection (systemd-detect-virt returning non-"none"): Adds 1 point — catches virtual machines not explicitly identified by cloud files.

Platform-Specific Backend Selection

Agent Reach uses the environment string to choose between graphical browser-based backends (for local) and headless CLI binaries (for server).

Platform Local Environment Server Environment
XiaoHongShu (xhs) Installs OpenCLI, reusing the user's existing Chrome session. Recommends the xiaohongshu-mcp binary and provides QR-login guidance.
Reddit Prefers OpenCLI; falls back to existing rdt-cli if present. Installs rdt-cli, a pure-CLI backend that operates without UI requirements.
Twitter Standard CLI operation; assumes graphical environment available. Standard CLI operation, but requires manual headless configuration (e.g., proxies).

Reddit Installation Logic Example

The conditional routing is implemented in cli.py (lines 778–892). When installing Reddit dependencies, the code explicitly branches based on the detection result:

def _install_reddit_deps():
    """Set up Reddit — desktop prefers OpenCLI, rdt-cli for servers/legacy."""
    if _detect_environment() != "server":
        _install_opencli_deps()
        print("  Reddit 走 OpenCLI(浏览器里登录过 reddit.com 即可用)")
        # ... optionally use rdt‑cli if already present ...

        return

    _install_rdt_cli()

Why the Threshold Is Set to ≥2

The function returns "server" only when the indicator sum reaches two or more. This conservative threshold prevents false positives:

  • Strong signals (SSH, containers, cloud VMs) each contribute 2 points, immediately triggering server mode.
  • Weak signals (missing display, virtualization) contribute only 1 point, requiring combination with another signal to classify as server.

Therefore, a desktop workstation running without a GUI but without SSH or containerization remains classified as local, while a Docker container or EC2 instance is correctly identified as server.

Practical Code Examples

You can invoke the detection directly from your Python code to verify the current environment:


# Quick check from the CLI

>>> from agent_reach.cli import _detect_environment
>>> _detect_environment()
'local'   # on my laptop

For testing environment-specific logic, the test suite in tests/test_cli.py (lines 88–100) demonstrates how to mock the detection function:


# Simulating a server environment in a test (see tests/test_cli.py)

def test_install_reddit_deps_routes_by_environment(monkeypatch):
    monkeypatch.setattr(cli, "_detect_environment", lambda: "local")
    # ...assert OpenCLI path...

    monkeypatch.setattr(cli, "_detect_environment", lambda: "server")
    # ...assert rdt‑cli path...

Summary

  • Local environments trigger installation of OpenCLI and browser-based backends that require a graphical session.
  • Server environments trigger installation of headless binaries like rdt-cli and xiaohongshu-mcp that function without UI dependencies.
  • The detection logic in agent_reach/cli.py uses a weighted scoring system (≥2 points) checking SSH variables, container files, display environment variables, cloud VM metadata, and systemd virtualization.
  • Key files involved include agent_reach/cli.py for detection logic, tests/test_cli.py for validation, and agent_reach/backends/opencli.py for the desktop-only backend implementation.

Frequently Asked Questions

How does Agent Reach detect if it is running on a server?

Agent Reach checks five OS-level indicators in _detect_environment(): SSH environment variables, Docker/container files, display server variables (DISPLAY/WAYLAND_DISPLAY), cloud provider metadata in /sys/, and systemd-detect-virt output. Each indicator carries a weight of 1 or 2 points, and reaching a total of 2 or more points classifies the environment as "server".

Why does my headless desktop still show as "local"?

A single weak signal (such as missing DISPLAY or WAYLAND_DISPLAY variables) only adds 1 point to the score. The threshold requires ≥2 points to trigger server mode. Since headless workstations typically lack SSH sessions, containers, or cloud VM signatures, they remain classified as local unless multiple indicators are present.

What backends are installed on servers versus local machines?

On local machines, Agent Reach installs OpenCLI for Reddit and XiaoHongShu, which leverages the user's existing Chrome browser session. On servers, it installs pure CLI alternatives like rdt-cli for Reddit and xiaohongshu-mcp for XiaoHongShu, ensuring functionality without graphical dependencies.

Can I override the environment detection manually?

While the source code in agent_reach/cli.py does not expose a public override flag, you can manipulate the underlying indicators (such as setting SSH_CONNECTION or creating /.dockerenv) to influence the scoring, or patch the _detect_environment() function in tests using monkeypatch as demonstrated in tests/test_cli.py.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →