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