feat: harden remote serve and reuse connections
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s

- Gate TCP serve commands with safe-by-default policies, per-key allow tokens, per-key rate limiting, and audit labels.
- Reuse authenticated encrypted remote sessions and parallelize/caches multi-browser fanout to reduce repeated handshake roundtrips.
- Increase paged native-host batch size with extension-side byte budgeting to speed large tab listings safely.
- Point install output at public Chrome Web Store / Firefox AMO listings by default, with --dev preserving unpacked workflows.
- Share search-engine metadata between CLI and SDK and bump the package/extension version to 0.16.0.
- Cover the new security, pooling, paging, install, and fanout behavior with expanded Python and extension tests.
This commit is contained in:
2026-06-18 14:24:15 +02:00
parent 8dece7800f
commit 6fa931aa36
49 changed files with 3407 additions and 1878 deletions
+1 -1
View File
@@ -114,7 +114,7 @@ class NavigationNS(Namespace):
) -> None:
"""Open a search query in the given engine (e.g. 'google', 'youtube', 'ddg')."""
from urllib.parse import quote_plus
from browser_cli.commands.search import ENGINES
from browser_cli.search.engines import ENGINES
template = ENGINES.get(engine)
if template is None:
raise ValueError(f"Unknown search engine '{engine}'. Available: {', '.join(ENGINES)}")
+22 -10
View File
@@ -7,12 +7,14 @@ helpers; single-browser mode falls straight through to ``_cmd``.
"""
from __future__ import annotations
import asyncio
import importlib
import sys
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING, Protocol, cast
from browser_cli.client import BrowserTarget
from browser_cli.client.core import _run_concurrent
from browser_cli.errors import BrowserNotConnected
from browser_cli.models import BrowserCounts, Tab
@@ -81,18 +83,28 @@ class RoutingMixin:
return targets
def _collect_multi_browser(self, command: str, args: dict | None = None):
results = []
targets = self._multi_browser_targets()
for target in targets:
try:
if target.remote:
data = _browser_cli_package().send_command(
command, args, profile=target.profile, remote=target.remote, key=self._client._key
)
else:
data = _browser_cli_package().send_command(command, args, profile=target.profile)
except (BrowserNotConnected, RuntimeError):
def _send(target: BrowserTarget):
package = _browser_cli_package()
if target.remote:
return package.send_command(
command, args, profile=target.profile, remote=target.remote, key=self._client._key
)
return package.send_command(command, args, profile=target.profile)
# Run per-target roundtrips concurrently — each is a blocking, network-bound
# send_command, so offloading to threads gives real overlap while still
# invoking the (test-patchable) sync entry point.
raw = _run_concurrent([
(lambda t=t: asyncio.to_thread(_send, t)) for t in targets
])
results = []
for target, data in zip(targets, raw):
if isinstance(data, (BrowserNotConnected, RuntimeError)):
continue
if isinstance(data, BaseException):
raise data
results.append((target, data))
if results:
return results