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
+64
View File
@@ -0,0 +1,64 @@
"""Local IPC transport for browser-cli client commands."""
from __future__ import annotations
import asyncio
import socket
from collections.abc import Callable
from multiprocessing.connection import Client as PipeClient
from browser_cli.framing import async_recv_exact as framing_async_recv_exact
from browser_cli.framing import async_recv_frame, async_send_frame, frame
from browser_cli.platform import is_windows
from browser_cli.remote.transport import _recv_all
async def async_recv_exact(reader: asyncio.StreamReader, n: int) -> bytes | None:
try:
return await framing_async_recv_exact(reader, n, allow_eof=True)
except ConnectionError:
return None
async def async_recv_all(reader: asyncio.StreamReader) -> bytes | None:
try:
return await async_recv_frame(reader, allow_eof=True)
except ConnectionError:
return None
async def async_send_all(writer: asyncio.StreamWriter, data: bytes) -> None:
await async_send_frame(writer, data)
def send_local_sync(profile: str | None, payload: bytes, resolve_socket: Callable[[str | None], str]) -> bytes | None:
sock_path = resolve_socket(profile)
if is_windows():
with PipeClient(sock_path, family="AF_PIPE") as conn:
conn.send_bytes(payload)
return conn.recv_bytes()
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
sock.connect(sock_path)
sock.sendall(frame(payload))
return _recv_all(sock)
async def send_local_async(profile: str | None, payload: bytes, resolve_socket: Callable[[str | None], str]) -> bytes | None:
sock_path = await asyncio.to_thread(resolve_socket, profile)
if is_windows():
return await _send_windows_pipe_async(sock_path, payload)
return await _send_local_unix_async(sock_path, payload)
async def _send_local_unix_async(sock_path: str, payload: bytes) -> bytes | None:
reader, writer = await asyncio.open_unix_connection(sock_path)
try:
await async_send_all(writer, payload)
return await async_recv_all(reader)
finally:
writer.close()
try:
await writer.wait_closed()
except Exception:
pass
async def _send_windows_pipe_async(sock_path: str, payload: bytes) -> bytes:
def _roundtrip():
with PipeClient(sock_path, family="AF_PIPE") as conn:
conn.send_bytes(payload)
return conn.recv_bytes()
return await asyncio.to_thread(_roundtrip)