feat: default port 443 for domain remotes, strip from display (v0.9.4)
Testing / test (push) Failing after 13m12s

- Domain-like --remote endpoints default to port 443; :443 is optional
- _normalize_endpoint strips :443 before storage in remotes.json
- _load_remotes normalises keys on load (backward compat migration)
- _remote_display_name omits :443 for domain endpoints
- _resolve_connect_endpoint adds :443 back for TCP connection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 16:46:27 +02:00
parent 7fd966014f
commit 9177e989bd
6 changed files with 167 additions and 11 deletions
+45 -6
View File
@@ -10,6 +10,7 @@ Profile selection order:
"""
import json
import os
import re
import socket
import struct
import uuid
@@ -32,6 +33,39 @@ REGISTRY_PATH = registry_path()
REMOTE_REGISTRY_PATH = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "browser-cli" / "remotes.json"
_DEFAULT_KEY_PATH = Path(os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))) / "browser-cli" / "client.key.pem"
_DEFAULT_REMOTE_PORT = 443
def _looks_like_domain(host: str) -> bool:
"""True if host looks like a domain name rather than an IP address or localhost."""
if host in {"localhost", "127.0.0.1", "::1"}:
return False
if re.match(r'^\d{1,3}(\.\d{1,3}){3}$', host):
return False
return '.' in host and any(c.isalpha() for c in host)
def _normalize_endpoint(endpoint: str) -> str:
"""Strip :443 from domain-like endpoints so they are stored without the default port."""
if not endpoint:
return endpoint
host, sep, port = endpoint.rpartition(":")
if sep and port == "443" and _looks_like_domain(host):
return host
return endpoint
def _resolve_connect_endpoint(endpoint: str) -> str:
"""Return host:port for TCP connection; domain without port defaults to :443."""
_, sep, _ = endpoint.rpartition(":")
if not sep:
if _looks_like_domain(endpoint):
return f"{endpoint}:{_DEFAULT_REMOTE_PORT}"
raise BrowserNotConnected(
f"Invalid remote endpoint '{endpoint}': expected host:port"
)
return endpoint
class BrowserNotConnected(Exception):
"""Raised when the native host socket is not available."""
@@ -67,7 +101,8 @@ def _load_remotes() -> dict[str, dict[str, str]]:
return {}
if not isinstance(data, dict):
return {}
return {str(endpoint): cfg for endpoint, cfg in data.items() if isinstance(cfg, dict)}
# normalize keys so old entries stored as "domain:443" match current lookups
return {_normalize_endpoint(str(endpoint)): cfg for endpoint, cfg in data.items() if isinstance(cfg, dict)}
@@ -108,8 +143,11 @@ def key_for_remote(endpoint: str | None) -> str | None:
def _remote_display_name(endpoint: str, profile_name: str, display_name: str) -> str:
host, sep, port = endpoint.rpartition(":")
remote_name = host if sep and port == "8765" else endpoint
return f"{remote_name}:{display_name or profile_name}"
if sep and (port == "8765" or (port == "443" and _looks_like_domain(host))):
display_endpoint = host
else:
display_endpoint = endpoint # normalized domain (no port) or non-default port
return f"{display_endpoint}:{display_name or profile_name}"
def remote_browser_targets(endpoint: str, key=None) -> list[BrowserTarget]:
@@ -239,9 +277,8 @@ def _load_private_key(key_path: "Path | str | None" = None):
def _send_remote(endpoint: str, msg: dict, private_key=None) -> bytes | None:
host, _, port_str = endpoint.rpartition(":")
if not host or not port_str:
raise BrowserNotConnected(f"Invalid remote endpoint '{endpoint}': expected host:port")
connect_ep = _resolve_connect_endpoint(endpoint)
host, _, port_str = connect_ep.rpartition(":")
port = int(port_str)
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
raw_sock.settimeout(30)
@@ -313,6 +350,8 @@ def send_command(command: str, args: dict | None = None, profile: str | None = N
"""Send a command to the browser and return the response data."""
requested_profile = profile or os.environ.get("BROWSER_CLI_PROFILE")
remote_endpoint = remote or os.environ.get("BROWSER_CLI_REMOTE")
if remote_endpoint:
remote_endpoint = _normalize_endpoint(remote_endpoint)
remote_alias_target = None
if not remote_endpoint and requested_profile:
remote_alias_target = remote_target_for_alias(requested_profile)