use std::path::Path;
use crate::materialize::manifest::{AuthSource, AuthStatus, CacheManifest};
pub fn sync_auth_on_export(config_dir: &Path, adapter_root: &Path, manifest: &mut CacheManifest) {
if let Err(e) = try_sync(config_dir, adapter_root, manifest) {
tracing::warn!("auth sync failed (non-fatal): {e}");
}
}
fn try_sync(
config_dir: &Path,
adapter_root: &Path,
manifest: &mut CacheManifest,
) -> anyhow::Result<()> {
let Some(entry) = super::read_auth_from_dir(config_dir)? else {
return Ok(());
};
let recorded_id = manifest.auth_status.id.as_deref().unwrap_or("");
if entry.uuid == recorded_id {
return Ok(());
}
super::save_auth_entry(adapter_root, &entry)?;
eprintln!("[llmenv] auth: {} (session login detected)", entry.email);
manifest.auth_status = AuthStatus {
source: AuthSource::Inherited,
id: Some(entry.uuid),
email: Some(entry.email),
};
manifest.write(config_dir)?;
Ok(())
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use std::path::PathBuf;
use super::*;
use crate::materialize::manifest::CacheManifest;
fn make_manifest(uuid: Option<&str>) -> CacheManifest {
let mut m = CacheManifest::new("hash", vec![PathBuf::from("CLAUDE.md")]);
if let Some(id) = uuid {
m.auth_status = AuthStatus {
source: AuthSource::Inherited,
id: Some(id.to_string()),
email: Some("existing@test.com".to_string()),
};
}
m
}
fn write_claude_json(dir: &std::path::Path, uuid: &str, email: &str) {
let doc = serde_json::json!({
"oauthAccount": { "id": uuid, "emailAddress": email }
});
std::fs::write(
dir.join(".claude.json"),
serde_json::to_string(&doc).unwrap(),
)
.unwrap();
}
#[test]
fn noop_when_uuids_match() {
let tmp = tempfile::tempdir().unwrap();
let uuid = "aaaa0000-0000-0000-0000-000000000000";
write_claude_json(tmp.path(), uuid, "same@test.com");
let mut manifest = make_manifest(Some(uuid));
let original_source = manifest.auth_status.source;
sync_auth_on_export(tmp.path(), tmp.path(), &mut manifest);
assert_eq!(manifest.auth_status.source, original_source);
}
#[test]
fn updates_cache_when_uuid_differs() {
let config_tmp = tempfile::tempdir().unwrap();
let cache_tmp = tempfile::tempdir().unwrap();
let new_uuid = "bbbb1111-0000-0000-0000-000000000000";
write_claude_json(config_tmp.path(), new_uuid, "new@test.com");
let mut manifest = make_manifest(Some("old-uuid-0000-0000-0000-000000000000"));
manifest.write(config_tmp.path()).unwrap();
sync_auth_on_export(config_tmp.path(), cache_tmp.path(), &mut manifest);
assert_eq!(manifest.auth_status.id.as_deref(), Some(new_uuid));
let entries = crate::auth::load_all_auth_entries(cache_tmp.path()).unwrap();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].uuid, new_uuid);
}
#[test]
fn noop_when_no_claude_json() {
let tmp = tempfile::tempdir().unwrap();
let mut manifest = make_manifest(None);
sync_auth_on_export(tmp.path(), tmp.path(), &mut manifest);
assert_eq!(manifest.auth_status.source, AuthSource::None);
}
}