swarm repositories / source
aboutsummaryrefslogtreecommitdiff
path: root/crates/phone-opus/src/mcp
diff options
context:
space:
mode:
authormain <main@swarm.moe>2026-03-24 01:19:25 -0400
committermain <main@swarm.moe>2026-03-24 01:19:25 -0400
commit690b4851ea0afd8b214ddaa5450eec3a8c3a7ec9 (patch)
tree837b41dd59ebc2e8bbf5db2ff72b9c44d7dd477e /crates/phone-opus/src/mcp
parentd64d1bd730aec23bcc5b01a78a8945863ea4d5a7 (diff)
downloadphone_opus-690b4851ea0afd8b214ddaa5450eec3a8c3a7ec9.zip
Share live Claude credentials with sandbox
Diffstat (limited to 'crates/phone-opus/src/mcp')
-rw-r--r--crates/phone-opus/src/mcp/service.rs42
1 files changed, 39 insertions, 3 deletions
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<PathBuf> {
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(