refactor: reorganize client transport and extension internals

- Split client, native, remote, serve, markdown, and SDK internals into focused packages with direct imports.
- Move local and remote transport framing/protocol helpers behind clearer module boundaries.
- Break up the extension injected DOM logic into a separate content dispatch bundle and dedicated content modules.
- Add explicit client handling for passive remote discovery without noisy PQ warnings.
- Keep behavior covered with updated unit, integration, and extension tests.
This commit is contained in:
2026-06-13 23:31:24 +02:00
parent fd5447cbb9
commit 076914e5b7
88 changed files with 7491 additions and 5228 deletions
+47 -8
View File
@@ -206,7 +206,7 @@ class TestAuthSuccess:
key_path.write_bytes(pem)
priv = load_private_key(key_path)
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
client, server = _pair()
t = threading.Thread(
@@ -238,7 +238,7 @@ class TestAuthSuccess:
key_path.write_bytes(pem)
priv = load_private_key(key_path)
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
client, server = _pair()
t = _spawn(server, path)
@@ -258,7 +258,7 @@ class TestAuthSuccess:
def test_post_quantum_kex_auth_reaches_proxy(self, tmp_path, monkeypatch):
"""ML-KEM shared secret is decapsulated and bound to the auth signature."""
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.auth.pq_kex_server_keypair", lambda: ("fake-private", b"fake-public"))
monkeypatch.setattr("browser_cli.auth.pq_kex_server_decapsulate", lambda priv, ct: b"pq-secret")
@@ -298,7 +298,7 @@ class TestAuthSuccess:
def test_post_quantum_encrypted_transport_reaches_proxy(self, tmp_path, monkeypatch):
"""New clients encrypt the command payload and receive encrypted responses."""
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.auth.pq_kex_server_keypair", lambda: ("fake-private", b"fake-public"))
monkeypatch.setattr("browser_cli.auth.pq_kex_server_decapsulate", lambda priv, ct: b"pq-secret")
@@ -347,7 +347,7 @@ class TestAuthSuccess:
def test_no_auth_mode_reaches_proxy(self, monkeypatch):
"""auth_keys_path=None (--no-auth): no pubkey required, reaches proxy layer."""
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
client, server = _pair()
t = threading.Thread(
@@ -421,7 +421,7 @@ class TestResponseEncoding:
"data": {"items": [{"url": f"https://example.com/{i}", "title": f"Tab {i}"} for i in range(300)]}}
host_path = tmp_path / "native.sock"
host = _FakeNativeHost(host_path, big)
monkeypatch.setattr("browser_cli.client._resolve_socket", lambda *_a, **_k: str(host_path))
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", lambda *_a, **_k: str(host_path))
client, server = _pair()
t = _spawn(server, None) # no auth
@@ -446,7 +446,7 @@ class TestResponseEncoding:
big = {"id": "y", "success": True, "data": {"items": list(range(500))}}
host_path = tmp_path / "native2.sock"
host = _FakeNativeHost(host_path, big)
monkeypatch.setattr("browser_cli.client._resolve_socket", lambda *_a, **_k: str(host_path))
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", lambda *_a, **_k: str(host_path))
client, server = _pair()
t = _spawn(server, None)
@@ -467,7 +467,7 @@ class TestResponseEncoding:
"data": {"items": [{"url": f"https://e/{i}"} for i in range(300)]}}
host_path = tmp_path / "native3.sock"
host = _FakeNativeHost(host_path, big)
monkeypatch.setattr("browser_cli.client._resolve_socket", lambda *_a, **_k: str(host_path))
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", lambda *_a, **_k: str(host_path))
client, server = _pair()
t = threading.Thread(
@@ -489,3 +489,42 @@ class TestResponseEncoding:
client.close()
host.close()
t.join(timeout=2)
# ── async serve path ─────────────────────────────────────────────────────────
def test_async_handle_client_sends_challenge_and_proxies_no_auth(monkeypatch):
"""Async TCP handler mirrors the sync challenge + proxy error path."""
import asyncio
from browser_cli.commands import serve as serve_mod
async def run():
monkeypatch.setattr("browser_cli.client.targets.resolve_socket", _mock_no_browser)
async def handle(reader, writer):
await serve_mod._async_handle_client(
reader,
writer,
("127.0.0.1", 9999),
None,
None,
True,
asyncio.Semaphore(64),
)
server = await asyncio.start_server(handle, "127.0.0.1", 0)
host, port = server.sockets[0].getsockname()
async with server:
reader, writer = await asyncio.open_connection(host, port)
challenge = json.loads(await serve_mod._async_recv_all(reader))
assert challenge["type"] == "challenge"
msg = {"id": "x", "command": "tabs.list", "args": {}, "user_agent": FAKE_UA}
await serve_mod._async_framed_send(writer, json.dumps(msg).encode())
resp = json.loads(await serve_mod._async_recv_all(reader))
writer.close()
await writer.wait_closed()
assert resp["success"] is False
assert "browser" in resp["error"].lower() or "connected" in resp["error"].lower()
asyncio.run(run())