feat: Config and pluggable auth stub (manual/none)
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Protocol
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
target_url: str
|
||||||
|
out_dir: str | None = None
|
||||||
|
proxy_port: int = 8080
|
||||||
|
headless: bool = False
|
||||||
|
profile: str | None = None
|
||||||
|
gen_client: bool = False
|
||||||
|
model: str = "claude-opus-4-8"
|
||||||
|
scope_hosts: set[str] = field(default_factory=set[str])
|
||||||
|
no_llm_doc: bool = False
|
||||||
|
resume: str | None = None
|
||||||
|
auth: str = "manual"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_host(self) -> str:
|
||||||
|
return urlsplit(self.target_url).hostname or ""
|
||||||
|
|
||||||
|
def all_scope_hosts(self) -> set[str]:
|
||||||
|
return {self.target_host, *self.scope_hosts}
|
||||||
|
|
||||||
|
|
||||||
|
class AuthStrategy(Protocol):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
async def authenticate(self, page: object) -> None:
|
||||||
|
"""Prepare an authenticated session on the given Playwright page."""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class NoAuth:
|
||||||
|
name = "none"
|
||||||
|
|
||||||
|
async def authenticate(self, page: object) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ManualPauseAuth:
|
||||||
|
"""Default stub: pause so the human can log in by hand, then continue."""
|
||||||
|
|
||||||
|
name = "manual"
|
||||||
|
|
||||||
|
async def authenticate(self, page: object) -> None:
|
||||||
|
# Implemented against the real page in browser.py wiring; the stub
|
||||||
|
# simply records intent. The REPL prompts the user to log in and
|
||||||
|
# press enter before autonomous exploration begins.
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def make_auth(name: str) -> AuthStrategy:
|
||||||
|
strategies: dict[str, AuthStrategy] = {"manual": ManualPauseAuth(), "none": NoAuth()}
|
||||||
|
if name not in strategies:
|
||||||
|
raise ValueError(f"unknown auth strategy: {name!r}")
|
||||||
|
return strategies[name]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from auto_reverse.config import Config, ManualPauseAuth, NoAuth, make_auth
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_derives_target_host():
|
||||||
|
cfg = Config(target_url="https://app.example.com/dashboard")
|
||||||
|
assert cfg.target_host == "app.example.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_scope_hosts_includes_target_plus_extra():
|
||||||
|
cfg = Config(target_url="https://app.example.com", scope_hosts={"api.example.com"})
|
||||||
|
assert cfg.all_scope_hosts() == {"app.example.com", "api.example.com"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_model():
|
||||||
|
assert Config(target_url="https://x.com").model == "claude-opus-4-8"
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_auth_returns_strategy():
|
||||||
|
assert isinstance(make_auth("manual"), ManualPauseAuth)
|
||||||
|
assert isinstance(make_auth("none"), NoAuth)
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_auth_unknown_raises():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
make_auth("oauth-magic")
|
||||||
Reference in New Issue
Block a user