Send browser cookies/UA for live auth
Client builds the Cookie header as 'auth=<value>; oc_locale=en' from the bare
auth value the config supplies, so oc_locale defaults to English and the
scrape selectors match. Legacy full-header configs pass through. Default
User-Agent is a desktop Chrome string; --user-agent still overrides. Fix
relative-time past tense ('1m ago' not 'ago1m'). Verified live against a real
OpenCode account: balance, three usage frames, and reset times all parse.
This commit is contained in:
parent
41f35aec34
commit
4463a9b51d
@ -22,6 +22,24 @@ pub fn go_url(workspace: &str) -> String {
|
||||
GO_BASE_URL.replace("{workspace}", workspace)
|
||||
}
|
||||
|
||||
/// Build the Cookie header from the bare `auth` value. OpenCode expects an
|
||||
/// `auth` cookie plus an `oc_locale` cookie. We pin `oc_locale=en` so the page
|
||||
/// renders in English and the scrape selectors match the captured pages.
|
||||
///
|
||||
/// If `cookie` already looks like a full header (contains `=` and `;`), pass
|
||||
/// it through unchanged so legacy configs keep working.
|
||||
fn cookie_header(cookie: &str) -> String {
|
||||
let trimmed = cookie.trim();
|
||||
if trimmed.contains('=') && (trimmed.contains(';') || trimmed.starts_with("auth=")) {
|
||||
// Caller supplied a full header. Use as-is, but ensure oc_locale.
|
||||
if trimmed.contains("oc_locale") {
|
||||
return trimmed.to_string();
|
||||
}
|
||||
return format!("{trimmed}; oc_locale=en");
|
||||
}
|
||||
format!("auth={trimmed}; oc_locale=en")
|
||||
}
|
||||
|
||||
pub struct HttpClient {
|
||||
client: reqwest::Client,
|
||||
retries: u8,
|
||||
@ -59,8 +77,9 @@ impl HttpClient {
|
||||
let resp = self
|
||||
.client
|
||||
.get(url)
|
||||
.header("cookie", cookie)
|
||||
.header("accept", "text/html,application/xhtml+xml,*/*;q=0.8")
|
||||
.header("cookie", cookie_header(cookie))
|
||||
.header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
.header("accept-language", "en-US,en;q=0.9")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
|
||||
@ -18,6 +18,11 @@ use clap::Parser;
|
||||
use cli::{CacheAction, Cli, Command, ConfigAction, RawFormat};
|
||||
use model::{AccountReport, ReportStatus};
|
||||
|
||||
/// User-Agent sent on every request. Looks like a normal desktop browser so
|
||||
/// OpenCode serves the same HTML the scrape selectors were built against. The
|
||||
/// user can override with `--user-agent`.
|
||||
const DEFAULT_USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let cli = Cli::parse();
|
||||
let rt = match tokio::runtime::Builder::new_multi_thread()
|
||||
@ -131,7 +136,7 @@ fn load_and_options(cli: &Cli) -> anyhow::Result<(config::Config, fetch::FetchOp
|
||||
let user_agent = cli
|
||||
.user_agent
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("ocd/{}", env!("CARGO_PKG_VERSION")));
|
||||
.unwrap_or_else(|| DEFAULT_USER_AGENT.to_string());
|
||||
let ttl = cli.ttl.or(cfg.default_ttl_secs).unwrap_or(180);
|
||||
let opts = fetch::FetchOptions {
|
||||
ttl_secs: ttl,
|
||||
|
||||
@ -14,9 +14,8 @@ pub fn relative(iso: &str) -> String {
|
||||
let now = Utc::now();
|
||||
let d = t.signed_duration_since(now);
|
||||
let secs = d.num_seconds();
|
||||
let sign = if secs >= 0 { "in " } else { " ago" };
|
||||
let body = dur_words(secs.abs());
|
||||
format!("{sign}{body}")
|
||||
if secs >= 0 { format!("in {body}") } else { format!("{body} ago") }
|
||||
}
|
||||
|
||||
/// Absolute UTC string like "2025-08-31 17:00 UTC".
|
||||
|
||||
Loading…
Reference in New Issue
Block a user