From 690b4851ea0afd8b214ddaa5450eec3a8c3a7ec9 Mon Sep 17 00:00:00 2001 From: main Date: Tue, 24 Mar 2026 01:19:25 -0400 Subject: Share live Claude credentials with sandbox --- crates/phone-opus/src/mcp/service.rs | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'crates/phone-opus/src/mcp/service.rs') diff --git a/crates/phone-opus/src/mcp/service.rs b/crates/phone-opus/src/mcp/service.rs index 64cb778..d958a81 100644 --- a/crates/phone-opus/src/mcp/service.rs +++ b/crates/phone-opus/src/mcp/service.rs @@ -1,6 +1,8 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::io::{self, BufRead, Write}; +#[cfg(unix)] +use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -543,8 +545,7 @@ const CONSULT_OUTPUT_KEEP_COUNT: usize = 256; const CONSULT_OUTPUT_MAX_AGE: Duration = Duration::from_secs(7 * 24 * 60 * 60); const CONSULT_TIMESTAMP_FORMAT: &[time::format_description::FormatItem<'static>] = time::macros::format_description!("[year][month][day]T[hour][minute][second]Z"); -const CLAUDE_SEED_FILES: [&str; 5] = [ - ".credentials.json", +const CLAUDE_MIRROR_FILES: [&str; 4] = [ "settings.json", "settings.local.json", ".claude/settings.local.json", @@ -622,15 +623,24 @@ impl ClaudeSandbox { self.source_home.join(".claude") } + fn source_credentials_path(&self) -> PathBuf { + self.source_claude_dir().join(".credentials.json") + } + + fn destination_credentials_path(&self) -> PathBuf { + self.claude_config_dir().join(".credentials.json") + } + fn sync_seed_claude_files(&self) -> io::Result<()> { let source_root = self.source_claude_dir(); let destination_root = self.claude_config_dir(); - for relative in CLAUDE_SEED_FILES { + for relative in CLAUDE_MIRROR_FILES { sync_optional_seed_file( source_root.join(relative).as_path(), destination_root.join(relative).as_path(), )?; } + self.sync_live_credentials()?; Ok(()) } @@ -653,6 +663,9 @@ impl ClaudeSandbox { fn read_write_paths(&self) -> BTreeSet { let mut paths = BTreeSet::new(); let _ = paths.insert(self.state_root.clone()); + if self.source_credentials_path().exists() { + let _ = paths.insert(self.source_credentials_path()); + } for root in SHARED_TMP_ROOTS { let _ = paths.insert(PathBuf::from(root)); } @@ -700,6 +713,20 @@ impl ClaudeSandbox { } environment } + + fn sync_live_credentials(&self) -> io::Result<()> { + let source = self.source_credentials_path(); + let destination = self.destination_credentials_path(); + if !source.exists() { + remove_optional_path(destination.as_path())?; + return Ok(()); + } + if fs::read_link(destination.as_path()).ok().as_ref() == Some(&source) { + return Ok(()); + } + remove_optional_path(destination.as_path())?; + symlink(source.as_path(), destination.as_path()) + } } #[derive(Debug, Clone)] @@ -1329,6 +1356,15 @@ fn sync_optional_seed_file(source: &Path, destination: &Path) -> io::Result<()> Ok(()) } +fn remove_optional_path(path: &Path) -> io::Result<()> { + match fs::symlink_metadata(path) { + Ok(metadata) if metadata.is_dir() => fs::remove_dir_all(path), + Ok(_) => fs::remove_file(path), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()), + Err(error) => Err(error), + } +} + fn write_bytes_file(path: &Path, bytes: &[u8]) -> io::Result<()> { let parent = path.parent().ok_or_else(|| { io::Error::new( -- cgit v1.2.3