1use anyhow::Result;
2use std::path::{Path, PathBuf};
3
4use crate::auth;
5use crate::fs;
6use crate::paths;
7
8pub fn run() -> Result<i32> {
9 let auth_file = match paths::resolve_auth_file() {
10 Some(path) => path,
11 None => return Ok(0),
12 };
13
14 if !auth_file.is_file() {
15 return Ok(0);
16 }
17
18 let auth_key = match auth::identity_key_from_auth_file(&auth_file) {
19 Ok(Some(key)) => key,
20 _ => return Ok(0),
21 };
22
23 let auth_last_refresh = auth::last_refresh_from_auth_file(&auth_file).unwrap_or(None);
24 let auth_hash = match fs::sha256_file(&auth_file) {
25 Ok(hash) => hash,
26 Err(_) => {
27 eprintln!("codex: failed to hash {}", auth_file.display());
28 return Ok(1);
29 }
30 };
31
32 let secret_dir = paths::resolve_secret_dir();
33 if let Some(secret_dir) = secret_dir
34 && let Ok(entries) = std::fs::read_dir(&secret_dir)
35 {
36 for entry in entries.flatten() {
37 let path = entry.path();
38 if path.extension().and_then(|s| s.to_str()) != Some("json") {
39 continue;
40 }
41 let candidate_key = match auth::identity_key_from_auth_file(&path) {
42 Ok(Some(key)) => key,
43 _ => continue,
44 };
45 if candidate_key != auth_key {
46 continue;
47 }
48
49 let secret_hash = match fs::sha256_file(&path) {
50 Ok(hash) => hash,
51 Err(_) => {
52 eprintln!("codex: failed to hash {}", path.display());
53 return Ok(1);
54 }
55 };
56 if secret_hash == auth_hash {
57 continue;
58 }
59
60 let contents = std::fs::read(&auth_file)?;
61 fs::write_atomic(&path, &contents, fs::SECRET_FILE_MODE)?;
62
63 let timestamp_path = secret_timestamp_path(&path)?;
64 fs::write_timestamp(×tamp_path, auth_last_refresh.as_deref())?;
65 }
66 }
67
68 let auth_timestamp = secret_timestamp_path(&auth_file)?;
69 fs::write_timestamp(&auth_timestamp, auth_last_refresh.as_deref())?;
70
71 Ok(0)
72}
73
74fn secret_timestamp_path(target_file: &Path) -> Result<PathBuf> {
75 let cache_dir = paths::resolve_secret_cache_dir()
76 .ok_or_else(|| anyhow::anyhow!("CODEX_SECRET_CACHE_DIR not resolved"))?;
77 let name = target_file
78 .file_name()
79 .and_then(|name| name.to_str())
80 .unwrap_or("auth.json");
81 Ok(cache_dir.join(format!("{name}.timestamp")))
82}