refactor: modularize auth transport and markdown
Testing / remote-protocol-compat (0.9.5) (push) Successful in 1m4s
Testing / test (push) Successful in 1m22s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 1m7s
Package Extension / package-extension (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 1m5s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 1m4s
Testing / test (push) Successful in 1m22s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 1m7s
Package Extension / package-extension (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 1m5s
- Split auth into focused package modules for agent keys, file keys, signing, and post-quantum transport helpers while keeping the public browser_cli.auth import surface intact. - Move transport encoding internals into a package with separate codec and binary-hoisting helpers, preserving browser_cli.transport compatibility. - Extract remote TCP auth/socket helpers and serve challenge setup out of the runtime paths to make connection handling easier to reason about. - Move the extension markdown extractor into a dedicated content/markdown folder with separate root selection, code normalization, renderer, and utils. - Centralize CLI Rich rendering helpers for tab/window tree and table output, and add rendering tests for the shared builders. - Remove local typing ignores in SDK/decorator/script plumbing and bump the package and extension version to 0.15.3.
This commit is contained in:
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from functools import wraps
|
||||
from typing import Any, TypeVar
|
||||
from typing import Any, TypeVar, cast
|
||||
|
||||
F = TypeVar("F", bound=Callable)
|
||||
_MISSING = object()
|
||||
@@ -54,8 +54,8 @@ def sdk_command(
|
||||
return _clone_default(default)
|
||||
return result
|
||||
|
||||
wrapper._browser_cli_command = name # type: ignore[attr-defined]
|
||||
return wrapper # type: ignore[return-value]
|
||||
setattr(wrapper, "_browser_cli_command", name)
|
||||
return cast(F, wrapper)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import asyncio
|
||||
import functools
|
||||
import inspect
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar
|
||||
from typing import TypeVar, cast
|
||||
|
||||
from browser_cli.sdk.base import Namespace
|
||||
from browser_cli.sdk.workflow_decorators import WorkflowDecoratorsMixin, _NO_INJECT
|
||||
@@ -53,7 +53,7 @@ class DecoratorsNS(WorkflowDecoratorsMixin, Namespace):
|
||||
finally:
|
||||
if cleanup is not None:
|
||||
await asyncio.to_thread(cleanup, value)
|
||||
return async_wrapper # type: ignore[return-value]
|
||||
return cast(F, async_wrapper)
|
||||
return WorkflowDecoratorsMixin._value_decorator(
|
||||
self, fn, get_value, keyword=keyword, cleanup=cleanup
|
||||
)
|
||||
@@ -74,7 +74,7 @@ class DecoratorsNS(WorkflowDecoratorsMixin, Namespace):
|
||||
finally:
|
||||
if previous:
|
||||
await asyncio.to_thread(self._c.perf.set_profile, previous)
|
||||
return async_wrapper # type: ignore[return-value]
|
||||
return cast(F, async_wrapper)
|
||||
return WorkflowDecoratorsMixin.performance_profile(self, profile, restore=restore)(fn)
|
||||
return decorator
|
||||
|
||||
@@ -101,7 +101,7 @@ class DecoratorsNS(WorkflowDecoratorsMixin, Namespace):
|
||||
raise
|
||||
if delay > 0:
|
||||
await asyncio.sleep(delay)
|
||||
raise last_error # type: ignore[misc]
|
||||
return async_wrapper # type: ignore[return-value]
|
||||
raise cast(BaseException, last_error)
|
||||
return cast(F, async_wrapper)
|
||||
return WorkflowDecoratorsMixin.retry(self, times=times, delay=delay, exceptions=exceptions)(fn)
|
||||
return decorator
|
||||
|
||||
@@ -4,11 +4,32 @@ from __future__ import annotations
|
||||
import functools
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar
|
||||
from typing import Protocol, TypeVar, cast
|
||||
|
||||
F = TypeVar("F", bound=Callable)
|
||||
_NO_INJECT = object()
|
||||
|
||||
class _WorkflowTabs(Protocol):
|
||||
def active(self): ...
|
||||
def open(self, *args, **kwargs): ...
|
||||
def watch_url(self, *args, **kwargs): ...
|
||||
|
||||
class _WorkflowDom(Protocol):
|
||||
def wait_for(self, *args, **kwargs): ...
|
||||
|
||||
class _WorkflowPerf(Protocol):
|
||||
def status(self): ...
|
||||
def set_profile(self, profile: str): ...
|
||||
|
||||
class _WorkflowSession(Protocol):
|
||||
def save(self, name: str): ...
|
||||
|
||||
class _WorkflowClient(Protocol):
|
||||
tabs: _WorkflowTabs
|
||||
dom: _WorkflowDom
|
||||
perf: _WorkflowPerf
|
||||
session: _WorkflowSession
|
||||
|
||||
class WorkflowDecoratorsMixin:
|
||||
"""Shared implementation for sync and async workflow decorators.
|
||||
|
||||
@@ -17,7 +38,7 @@ class WorkflowDecoratorsMixin:
|
||||
in lockstep.
|
||||
"""
|
||||
|
||||
_c: object
|
||||
_c: _WorkflowClient
|
||||
|
||||
@staticmethod
|
||||
def _inject(kwargs: dict, keyword: str | None, value):
|
||||
@@ -62,7 +83,7 @@ class WorkflowDecoratorsMixin:
|
||||
finally:
|
||||
if cleanup is not None:
|
||||
self._run(cleanup, value)
|
||||
return wrapper # type: ignore[return-value]
|
||||
return cast(F, wrapper)
|
||||
|
||||
return decorator(func) if func is not None else decorator
|
||||
|
||||
@@ -72,7 +93,7 @@ class WorkflowDecoratorsMixin:
|
||||
By default the tab is injected as ``tab=...``. Pass ``keyword=None`` to
|
||||
pass it as the first positional argument instead.
|
||||
"""
|
||||
return self._value_decorator(func, self._c.tabs.active, keyword=keyword) # type: ignore[attr-defined]
|
||||
return self._value_decorator(func, self._c.tabs.active, keyword=keyword)
|
||||
|
||||
def new_tab(
|
||||
self,
|
||||
@@ -93,7 +114,7 @@ class WorkflowDecoratorsMixin:
|
||||
wrapped function returns or raises.
|
||||
"""
|
||||
def open_tab():
|
||||
return self._c.tabs.open( # type: ignore[attr-defined]
|
||||
return self._c.tabs.open(
|
||||
url,
|
||||
wait=wait,
|
||||
timeout=timeout,
|
||||
@@ -124,7 +145,7 @@ class WorkflowDecoratorsMixin:
|
||||
the wrapped function. By default the result is not injected.
|
||||
"""
|
||||
def wait():
|
||||
return self._c.dom.wait_for( # type: ignore[attr-defined]
|
||||
return self._c.dom.wait_for(
|
||||
selector,
|
||||
timeout=timeout,
|
||||
visible=visible,
|
||||
@@ -145,7 +166,7 @@ class WorkflowDecoratorsMixin:
|
||||
):
|
||||
"""Wait until a tab URL matches *pattern* before calling the function."""
|
||||
def wait():
|
||||
return self._c.tabs.watch_url(pattern, tab_id=tab_id, timeout=timeout) # type: ignore[attr-defined]
|
||||
return self._c.tabs.watch_url(pattern, tab_id=tab_id, timeout=timeout)
|
||||
|
||||
inject = keyword if keyword is not None else _NO_INJECT
|
||||
return self._value_decorator(None, wait, keyword=inject)
|
||||
@@ -157,19 +178,19 @@ class WorkflowDecoratorsMixin:
|
||||
def wrapper(*args, **kwargs):
|
||||
previous = None
|
||||
if restore:
|
||||
previous = self._run(self._c.perf.status).get("performanceProfile") # type: ignore[attr-defined]
|
||||
self._run(self._c.perf.set_profile, profile) # type: ignore[attr-defined]
|
||||
previous = self._run(self._c.perf.status).get("performanceProfile")
|
||||
self._run(self._c.perf.set_profile, profile)
|
||||
try:
|
||||
return self._call_wrapped(fn, *args, **kwargs)
|
||||
finally:
|
||||
if previous:
|
||||
self._run(self._c.perf.set_profile, previous) # type: ignore[attr-defined]
|
||||
return wrapper # type: ignore[return-value]
|
||||
self._run(self._c.perf.set_profile, previous)
|
||||
return cast(F, wrapper)
|
||||
return decorator
|
||||
|
||||
def save_session_before(self, name: str):
|
||||
"""Save the current browser session before running the function."""
|
||||
return self._value_decorator(None, lambda: self._c.session.save(name), keyword=_NO_INJECT) # type: ignore[attr-defined]
|
||||
return self._value_decorator(None, lambda: self._c.session.save(name), keyword=_NO_INJECT)
|
||||
|
||||
def retry(
|
||||
self,
|
||||
@@ -194,7 +215,7 @@ class WorkflowDecoratorsMixin:
|
||||
raise
|
||||
if delay > 0:
|
||||
self._sleep(delay)
|
||||
raise last_error # type: ignore[misc]
|
||||
return wrapper # type: ignore[return-value]
|
||||
raise cast(BaseException, last_error)
|
||||
return cast(F, wrapper)
|
||||
return decorator
|
||||
|
||||
|
||||
Reference in New Issue
Block a user