tail-fin-core 0.7.5

Public session-lifecycle abstractions for tail-fin: Site trait, SessionManager, Credentials, SessionStatus, auth errors. The stable API surface that downstream agents (Flock A2A etc.) consume.
Documentation
# tail-fin-core

Public session-lifecycle abstractions for [tail-fin](https://github.com/motosan-dev/tail-fin). The **stable API surface** that downstream agents (Flock A2A, external orchestration platforms) import — separated from the larger `tail-fin-common` so consumers don't pull in HTTP, CDP, and other internal helpers they don't need.

## What's here

| Type / Trait | Purpose |
|---|---|
| `Site` trait | Per-site lifecycle + identity contract (`id`, `display_name`, `cookie_domain_patterns`, `refresh_url`, `refresh`, `validate`, `attempt_login`, `detect_auth_failure`) |
| `SessionManager` | Single-account coordinator with `refresh` / `refresh_with_seed` / `refresh_if_stale` / `validate` / `reload_cookies` |
| `SessionStatus` | `Valid` / `Degrading` / `Expired` / `Blocked` / `Unknown` enum |
| `Credentials` | `UsernamePassword` / `OAuth` / `CookieJar` / `Manual` — with `Debug` impl that masks secrets |
| `AuthFailureKind` | `CookieExpired` / `CredentialsRejected` / `RateLimited` / `AntibotBlock` / `AccountSuspended` |
| `FailureIndicators` | HTTP observation struct for `Site::detect_auth_failure` (status, body preview, URL, headers) |
| `SiteError` | Per-site variant of `TailFinError` (`ValidationFailed` / `RefreshFailed` / `ManualLoginRequired` / `AuthFailed`) |

Re-exports `night_fury_core::BrowserSession` so consumers only need to pull in `tail-fin-core`.

## Usage

```rust
use tail_fin_core::{Site, SessionStatus, SessionManager, BrowserSession};

async fn check_session<S: Site>(site: &S, session: &BrowserSession) -> anyhow::Result<()> {
    match site.validate(session).await? {
        SessionStatus::Valid => println!("{} session is valid", site.display_name()),
        SessionStatus::Expired => println!("refresh needed"),
        SessionStatus::Blocked { reason, retry_after } => {
            println!("blocked: {reason}, retry after {retry_after:?}");
        }
        _ => {}
    }
    Ok(())
}
```

Every tail-fin adapter (twitter, sa, reddit, …, tradingview) implements `Site`. The `tfd` daemon uses an `Arc<dyn Site>` registry to dispatch commands by site id.

### `refresh_with_seed`

When a caller already holds a (possibly stale) cookie set the site treats as "proof of prior session", `SessionManager::refresh_with_seed(&seed)` injects those cookies into the browser first and then calls `Site::refresh`. Passing an empty slice is equivalent to `refresh()`.

```rust
let fresh = manager.refresh_with_seed(&existing_cookies).await?;
```

Used by downstream agents (e.g. flock's cookie-refresh workflow) that need to hand a stale cookie jar to the site so the refresh navigation can upgrade it to fresh server-issued cookies.

## License

[MIT](../../LICENSE)