lobe-cli 0.1.4

Lobe — local-first HTTP performance profiling CLI for developers. Spins up a capture proxy, records DNS/TCP/TLS/TTFB/download phases per request, and flags anomalies against grounded network baselines.
//! POSTs a captured session to Convex `/upload-session`. Reads the bearer
//! token from `~/.config/lobe/config.toml` (fill via `lobe login`).

use lobe_core::engine::capture::CaptureSessionExport;
use lobe_core::error::{Result, TloxError};
use serde::Deserialize;

use crate::config;

/// Response body from a successful upload.
#[derive(Debug, Deserialize)]
pub struct UploadResponse {
    #[allow(dead_code)]
    pub session_id: String,
    #[allow(dead_code)]
    pub project_id: String,
    #[allow(dead_code)]
    pub project_name: String,
    pub event_count: usize,
}

/// POST the session to Convex. On success returns the identifiers so the
/// caller can print a URL back to the user (e.g. `getlobe.dev/projects/...`).
pub async fn upload_session(session: &CaptureSessionExport) -> Result<UploadResponse> {
    let config = config::load()?;
    let token = config.auth.token.ok_or_else(|| {
        TloxError::Auth(
            "not logged in — run `lobe login` first to authorize this machine".to_string(),
        )
    })?;

    let url = format!("{}/upload-session", config.urls.api);
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {token}"))
        .json(session)
        .send()
        .await
        .map_err(|e| TloxError::Network(format!("upload failed to reach cloud: {e}")))?;

    let status = response.status();
    if status == reqwest::StatusCode::UNAUTHORIZED {
        return Err(TloxError::Auth(
            "token was rejected — run `lobe login` to mint a fresh one".to_string(),
        ));
    }
    if !status.is_success() {
        let body = response.text().await.unwrap_or_default();
        return Err(TloxError::Network(format!(
            "upload rejected ({status}): {body}"
        )));
    }

    let payload: UploadResponse = response
        .json()
        .await
        .map_err(|e| TloxError::Network(format!("could not parse upload response: {e}")))?;

    Ok(payload)
}

/// Build a browser URL for a freshly-uploaded session so the CLI can print
/// something clickable. Currently unused by the TUI (kept short-flash) but
/// callers like `lobe capture --print-url` can adopt it.
#[allow(dead_code)]
pub fn session_url(response: &UploadResponse) -> String {
    let config = config::load().unwrap_or_default();
    format!(
        "{}/projects/{}/sessions/{}",
        config.urls.web, response.project_id, response.session_id
    )
}