diff --git a/browser_cli/client.py b/browser_cli/client.py index bda3a63..2a2d0d4 100644 --- a/browser_cli/client.py +++ b/browser_cli/client.py @@ -214,6 +214,22 @@ def active_browser_targets(*, include_remotes: bool = True, key=None) -> list[Br return targets +def _is_active_local_profile(profile: str | None) -> bool: + """Return True when profile names a reachable local browser endpoint.""" + if not profile: + return False + if REGISTRY_PATH.exists(): + reg = load_registry(REGISTRY_PATH) + if profile in _active_endpoints(reg): + return True + if not is_windows(): + try: + return Path(endpoint_for_alias(profile)).exists() + except Exception: + return False + return False + + def _resolve_socket(profile: str | None = None) -> str: """Return the socket path for the given profile (or auto-detect).""" target = profile or os.environ.get("BROWSER_CLI_PROFILE") @@ -387,7 +403,7 @@ def send_command(command: str, args: dict | None = None, profile: str | None = N if remote_endpoint: remote_endpoint = _normalize_endpoint(remote_endpoint) remote_alias_target = None - if not remote_endpoint and requested_profile: + if not remote_endpoint and requested_profile and not _is_active_local_profile(requested_profile): remote_alias_target = remote_target_for_alias(requested_profile) if remote_alias_target: remote_endpoint = remote_alias_target.remote diff --git a/extension/manifest.json b/extension/manifest.json index f273f78..fedc646 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "browser-cli", - "version": "0.10.0", + "version": "0.10.1", "description": "Control your browser from the terminal or Python SDK", "permissions": [ "tabs", diff --git a/pyproject.toml b/pyproject.toml index b120ac1..887ea43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "browser-cli" -version = "0.10.0" +version = "0.10.1" description = "Control your real running browser from the terminal or Python SDK" requires-python = ">=3.10" dependencies = [ diff --git a/tests/test_client.py b/tests/test_client.py index 23f9041..44211e7 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -117,6 +117,50 @@ def test_send_command_auto_routes_single_remote_target(monkeypatch): assert "token" not in sent +def test_send_command_prefers_active_local_profile_over_saved_remote_alias(monkeypatch, tmp_path): + monkeypatch.delenv("BROWSER_CLI_REMOTE", raising=False) + monkeypatch.delenv("BROWSER_CLI_PROFILE", raising=False) + socket_path = tmp_path / "work.sock" + socket_path.write_text("") + registry_path = tmp_path / "registry.json" + registry_path.write_text(json.dumps({"work": str(socket_path)}), encoding="utf-8") + monkeypatch.setattr("browser_cli.client.REGISTRY_PATH", registry_path) + monkeypatch.setattr( + "browser_cli.client.remote_target_for_alias", + lambda alias: pytest.fail("active local profile must not trigger remote alias discovery"), + ) + + payload = json.dumps({"success": True, "data": "local-ok"}).encode("utf-8") + framed = len(payload).to_bytes(4, "little") + payload + + class FakeSocket: + def __init__(self, *args, **kwargs): + self.sent = b"" + self._response = bytearray(framed) + self.connected_to = None + + def __enter__(self): + return self + + def __exit__(self, *exc): + return False + + def connect(self, path): + self.connected_to = path + + def sendall(self, data): + self.sent += data + + def recv(self, n): + chunk = bytes(self._response[:n]) + del self._response[:n] + return chunk + + monkeypatch.setattr("browser_cli.client.socket.socket", lambda *args, **kwargs: FakeSocket()) + + assert send_command("tabs.list", profile="work") == "local-ok" + + def test_send_command_resolves_browser_alias_to_remote_target(monkeypatch): monkeypatch.delenv("BROWSER_CLI_REMOTE", raising=False) monkeypatch.setenv("BROWSER_CLI_PROFILE", "host:work") diff --git a/uv.lock b/uv.lock index f8208cf..cc989cc 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.10" [[package]] name = "browser-cli" -version = "0.10.0" +version = "0.10.1" source = { editable = "." } dependencies = [ { name = "click" },