fix that the cli still used the DEFAULT_SOCKET Const, give better error message when no browser found adding test for client and update cli test, and update version and readme
Package Extension / package-extension (push) Failing after 53s
Build & Publish Package / publish (push) Successful in 39s

This commit is contained in:
2026-04-10 02:14:59 +02:00
parent 147d1d4ca3
commit f18d2d5536
6 changed files with 59 additions and 11 deletions
+2 -2
View File
@@ -122,7 +122,7 @@ browser-cli/
All commands are run with `uv run browser-cli [--browser ALIAS] <command>`. All commands are run with `uv run browser-cli [--browser ALIAS] <command>`.
Use `--browser ALIAS` when multiple browser instances are connected. You can inspect the active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli rename-profile --browser <current-alias> <new-alias>`. If exactly one browser instance is connected, commands auto-target it. Use `--browser ALIAS` when multiple browser instances are connected. You can inspect the active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli rename-profile --browser <current-alias> <new-alias>`.
Important: profile aliases are browser-instance aliases, not window aliases. Window aliases created with `windows rename` are only for targeting windows in commands like `nav open --window work`. If a browser instance has no explicit profile alias set, the native host gives it a generated UUID alias so multiple unaliased browsers stay distinct. Important: profile aliases are browser-instance aliases, not window aliases. Window aliases created with `windows rename` are only for targeting windows in commands like `nav open --window work`. If a browser instance has no explicit profile alias set, the native host gives it a generated UUID alias so multiple unaliased browsers stay distinct.
@@ -268,7 +268,7 @@ browser-cli session auto-save off
### Misc ### Misc
```sh ```sh
browser-cli clients # show connected browser info browser-cli clients # show connected browser info from the registry
browser-cli rename-profile --browser abcd1234 work # rename one connected browser instance browser-cli rename-profile --browser abcd1234 work # rename one connected browser instance
browser-cli --browser abcd1234 rename-profile work # equivalent global form browser-cli --browser abcd1234 rename-profile work # equivalent global form
browser-cli install brave # (re)register the native host browser-cli install brave # (re)register the native host
+3 -5
View File
@@ -124,7 +124,7 @@ main.add_command(search_group)
def cmd_clients(): def cmd_clients():
"""Show connected browser clients.""" """Show connected browser clients."""
import json as _json import json as _json
from browser_cli.client import REGISTRY_PATH, DEFAULT_SOCKET from browser_cli.client import REGISTRY_PATH
# Build a map of profile → socket path from the registry # Build a map of profile → socket path from the registry
profiles: dict[str, str] = {} profiles: dict[str, str] = {}
@@ -133,8 +133,6 @@ def cmd_clients():
profiles = _json.loads(REGISTRY_PATH.read_text()) profiles = _json.loads(REGISTRY_PATH.read_text())
except Exception: except Exception:
pass pass
if not profiles:
profiles = {"default": DEFAULT_SOCKET}
all_clients = [] all_clients = []
for profile_name, sock_path in profiles.items(): for profile_name, sock_path in profiles.items():
@@ -148,7 +146,7 @@ def cmd_clients():
all_clients.append({"profile": profile_name, "name": "", "version": "", "platform": "disconnected"}) all_clients.append({"profile": profile_name, "name": "", "version": "", "platform": "disconnected"})
if not all_clients: if not all_clients:
console.print("[yellow]No browser clients found[/yellow]") console.print("[yellow]No browser clients found. Start a browser with the extension enabled first.[/yellow]")
sys.exit(1) sys.exit(1)
from rich.table import Table from rich.table import Table
@@ -158,7 +156,7 @@ def cmd_clients():
table.add_column("Version") table.add_column("Version")
table.add_column("Platform") table.add_column("Platform")
for c in all_clients: for c in all_clients:
table.add_row(c.get("profile", "default"), c.get("name", ""), c.get("version", ""), c.get("platform", "")) table.add_row(c.get("profile", ""), c.get("name", ""), c.get("version", ""), c.get("platform", ""))
console.print(table) console.print(table)
+6 -3
View File
@@ -6,7 +6,7 @@ Profile selection order:
1. Explicit `profile` argument to send_command() 1. Explicit `profile` argument to send_command()
2. BROWSER_CLI_PROFILE environment variable 2. BROWSER_CLI_PROFILE environment variable
3. First entry in /tmp/.browser_cli/registry.json 3. First entry in /tmp/.browser_cli/registry.json
4. Fallback: /tmp/.browser_cli/default.sock 4. Otherwise, no browser can be resolved automatically
""" """
import json import json
import os import os
@@ -18,7 +18,6 @@ from typing import Any
SOCKET_DIR = Path("/tmp/.browser_cli") SOCKET_DIR = Path("/tmp/.browser_cli")
REGISTRY_PATH = SOCKET_DIR / "registry.json" REGISTRY_PATH = SOCKET_DIR / "registry.json"
DEFAULT_SOCKET = str(SOCKET_DIR / "default.sock")
class BrowserNotConnected(Exception): class BrowserNotConnected(Exception):
@@ -64,7 +63,11 @@ def _resolve_socket(profile: str | None = None) -> str:
except Exception: except Exception:
pass pass
return DEFAULT_SOCKET raise BrowserNotConnected(
"Cannot resolve a browser socket automatically.\n"
"Make sure the browser is running with the browser-cli extension enabled,\n"
"or pass --browser <alias> / set BROWSER_CLI_PROFILE to a known alias."
)
def send_command(command: str, args: dict | None = None, profile: str | None = None) -> Any: def send_command(command: str, args: dict | None = None, profile: str | None = None) -> Any:
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "browser-cli" name = "browser-cli"
version = "0.4.0" version = "0.4.1"
description = "Control your real running browser from the terminal via a Chrome extension" description = "Control your real running browser from the terminal via a Chrome extension"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
+7
View File
@@ -47,3 +47,10 @@ def test_install_help_lists_supported_browsers():
assert result.exit_code == 0 assert result.exit_code == 0
assert "[chrome|chromium|brave|edge|vivaldi]" in result.output assert "[chrome|chromium|brave|edge|vivaldi]" in result.output
def test_clients_exits_cleanly_when_registry_is_missing():
with patch("browser_cli.client.REGISTRY_PATH", Path("/nonexistent/browser-cli-registry.json")):
result = CliRunner().invoke(main, ["clients"])
assert result.exit_code == 1
assert "No browser clients found" in result.output
+40
View File
@@ -0,0 +1,40 @@
import json
from pathlib import Path
import pytest
from browser_cli.client import BrowserNotConnected, _resolve_socket
def test_resolve_socket_raises_when_registry_missing(monkeypatch):
monkeypatch.delenv("BROWSER_CLI_PROFILE", raising=False)
monkeypatch.setattr("browser_cli.client.REGISTRY_PATH", Path("/nonexistent/browser-cli-registry.json"))
with pytest.raises(BrowserNotConnected, match="Cannot resolve a browser socket automatically"):
_resolve_socket()
def test_resolve_socket_uses_only_active_registry_entry(monkeypatch, tmp_path):
monkeypatch.delenv("BROWSER_CLI_PROFILE", raising=False)
socket_path = tmp_path / "browser.sock"
socket_path.write_text("")
registry_path = tmp_path / "registry.json"
registry_path.write_text(json.dumps({"abc-uuid": str(socket_path)}))
monkeypatch.setattr("browser_cli.client.REGISTRY_PATH", registry_path)
assert _resolve_socket() == str(socket_path)
def test_resolve_socket_raises_when_multiple_active_entries(monkeypatch, tmp_path):
monkeypatch.delenv("BROWSER_CLI_PROFILE", raising=False)
first_socket = tmp_path / "one.sock"
second_socket = tmp_path / "two.sock"
first_socket.write_text("")
second_socket.write_text("")
registry_path = tmp_path / "registry.json"
registry_path.write_text(json.dumps({"uuid-1": str(first_socket), "uuid-2": str(second_socket)}))
monkeypatch.setattr("browser_cli.client.REGISTRY_PATH", registry_path)
with pytest.raises(BrowserNotConnected, match="Multiple browser instances are active: uuid-1, uuid-2"):
_resolve_socket()