use super::{Account, AuthTool, Snapshot};
use crate::paths::Paths;
use crate::secret::Secret;
use anyhow::{bail, Context, Result};
use serde_json::Value;
use sha2::{Digest, Sha256};
pub struct Antigravity;
pub(crate) fn token_fingerprint(v: &Value) -> String {
let rt = v["token"]["refresh_token"].as_str().unwrap_or("");
if rt.is_empty() {
return String::new();
}
let d = Sha256::digest(format!("swapdex-antigravity:{rt}").as_bytes());
let mut hex = String::with_capacity(12);
for b in &d[..6] {
hex.push_str(&format!("{b:02x}"));
}
hex
}
impl AuthTool for Antigravity {
fn name(&self) -> &'static str {
"antigravity"
}
fn present(&self, paths: &Paths) -> bool {
paths.antigravity_token().exists()
}
fn capture(&self, paths: &Paths) -> Result<Snapshot> {
let path = paths.antigravity_token();
if !path.exists() {
bail!("not logged in to Antigravity (no {})", path.display());
}
let bytes = crate::atomic::read_regular(&path)?;
serde_json::from_slice::<Value>(&bytes)
.context("antigravity-oauth-token is not valid JSON")?;
Ok(Snapshot {
tool: "antigravity",
blobs: vec![("token".into(), Secret::new(bytes))],
})
}
fn apply(&self, paths: &Paths, snap: &Snapshot) -> Result<()> {
let token = snap.part("token").context("snapshot missing token")?;
serde_json::from_slice::<Value>(token.expose())
.context("saved antigravity token is not valid JSON; refusing to apply")?;
crate::atomic::write_secret(&paths.antigravity_token(), token.expose())
}
fn identity(&self, paths: &Paths) -> Result<Option<Account>> {
let path = paths.antigravity_token();
if !path.exists() {
return Ok(None);
}
let v: Value = serde_json::from_slice(&crate::atomic::read_regular(&path)?)
.context("parse antigravity-oauth-token")?;
Ok(Some(Account {
tool: "antigravity",
account_id: token_fingerprint(&v),
display: "Google account (Antigravity)".into(),
email: None,
tier: v["auth_method"].as_str().map(|s| s.to_string()),
expires_at: None,
}))
}
}