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. 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

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().

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