@@ -10079,6 +10079,33 @@ async fn sync_grok_oauth_auth_file(
1007910079 return Ok(false);
1008010080 }
1008110081
10082+ let source_expires_at = grok_auth_file_expires_at(&auth_json);
10083+ let existing_output = workspace_exec
10084+ .output(
10085+ cwd,
10086+ "/bin/sh",
10087+ &[
10088+ "-lc".to_string(),
10089+ "test -s \"${HOME:-/root}/.grok/auth.json\" && cat \"${HOME:-/root}/.grok/auth.json\""
10090+ .to_string(),
10091+ ],
10092+ HashMap::new(),
10093+ )
10094+ .await
10095+ .map_err(|e| format!("Failed to inspect workspace Grok auth file: {}", e))?;
10096+ if existing_output.status.success() {
10097+ let existing_json = String::from_utf8_lossy(&existing_output.stdout);
10098+ let existing_expires_at = grok_auth_file_expires_at(&existing_json);
10099+ if existing_expires_at >= source_expires_at {
10100+ tracing::debug!(
10101+ source_expires_at,
10102+ existing_expires_at,
10103+ "Skipping Grok auth sync because workspace auth is at least as fresh"
10104+ );
10105+ return Ok(false);
10106+ }
10107+ }
10108+
1008210109 let encoded = {
1008310110 use base64::Engine;
1008410111 base64::engine::general_purpose::STANDARD.encode(auth_json.as_bytes())
@@ -10116,6 +10143,22 @@ async fn sync_grok_oauth_auth_file(
1011610143 }
1011710144}
1011810145
10146+ fn grok_auth_file_expires_at(contents: &str) -> i64 {
10147+ const GROK_OAUTH_CLIENT_KEY: &str = "https://auth.x.ai::b1a00492-073a-47ea-816f-4c329264a828";
10148+
10149+ serde_json::from_str::<serde_json::Value>(contents)
10150+ .ok()
10151+ .and_then(|auth| auth.get(GROK_OAUTH_CLIENT_KEY).cloned())
10152+ .and_then(|entry| {
10153+ entry
10154+ .get("expires_at")
10155+ .and_then(|v| v.as_str())
10156+ .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
10157+ .map(|dt| dt.timestamp_millis())
10158+ })
10159+ .unwrap_or(0)
10160+ }
10161+
1011910162/// Result of a backend preflight check
1012010163#[derive(Debug, Clone, serde::Serialize)]
1012110164pub struct BackendPreflightResult {
0 commit comments