#![allow(dead_code)]
use serde_json::{Map, Value};
use std::path::{Path, PathBuf};
use std::process::Stdio;
use tokio::process::{Child, Command};
pub fn output(value: &Value) -> &Value {
value
.get("response")
.and_then(|value| value.get("output"))
.or_else(|| value.get("output"))
.unwrap_or(value)
}
pub fn object(value: Value) -> Result<Map<String, Value>, Box<dyn std::error::Error>> {
let mut value = value.as_object().cloned().ok_or("expected an object")?;
value.retain(|_, entry| !entry.is_null());
Ok(value)
}
pub async fn read_clipboard() -> Result<String, Box<dyn std::error::Error>> {
let bytes = clipboard_bytes().await?;
Ok(String::from_utf8(bytes)?.trim_end_matches(['\r', '\n']).to_string())
}
#[cfg(target_os = "macos")]
async fn clipboard_bytes() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(Command::new("pbpaste").output().await?.stdout)
}
#[cfg(target_os = "windows")]
async fn clipboard_bytes() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let output = Command::new("powershell")
.args(["-NoProfile", "-Command", "Get-Clipboard"])
.output()
.await?;
Ok(output.stdout)
}
#[cfg(target_os = "linux")]
async fn clipboard_bytes() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
async fn try_read(bin: &str, args: &[&str]) -> Option<Vec<u8>> {
let output = Command::new(bin).args(args).output().await.ok()?;
output.status.success().then_some(output.stdout)
}
if let Some(bytes) = try_read("wl-paste", &["--no-newline"]).await {
return Ok(bytes);
}
if let Some(bytes) = try_read("xclip", &["-selection", "clipboard", "-o"]).await {
return Ok(bytes);
}
if let Some(bytes) = try_read("xsel", &["--clipboard", "--output"]).await {
return Ok(bytes);
}
Err("install wl-clipboard, xclip, or xsel to verify the clipboard on Linux".into())
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
async fn clipboard_bytes() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Err("clipboard verification is not supported on this platform".into())
}
pub fn supervisor_dir(entrypoint: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
if let Ok(value) = std::env::var("COMPUTER_USE_SUPERVISOR_DIR") {
return Ok(PathBuf::from(value));
}
let root = Path::new(entrypoint)
.parent()
.and_then(Path::parent)
.ok_or("cannot derive computer-use-mcp root from entrypoint")?;
Ok(root.join("packages/computer-use-supervisor"))
}
pub fn spawn_pip(
directory: &Path,
socket: &Path,
token: &str,
principal: &str,
session_id: &str,
) -> Result<Child, Box<dyn std::error::Error>> {
let mut command = if let Ok(electron) = std::env::var("COMPUTER_USE_ELECTRON") {
let mut command = Command::new(electron);
command.arg(".");
command
} else {
let mut command = Command::new("npx");
command.args(["--yes", "--package=electron@43.1.0", "electron", "."]);
command
};
Ok(command
.current_dir(directory)
.env("COMPUTER_USE_SUPERVISOR_SOCKET", socket)
.env("COMPUTER_USE_SUPERVISOR_TOKEN", token)
.env("COMPUTER_USE_PRINCIPAL_ID", principal)
.env("COMPUTER_USE_SESSION_ID", session_id)
.env("COMPUTER_USE_SUPERVISOR_DEBUG", "true")
.stdout(Stdio::inherit())
.kill_on_drop(true)
.spawn()?)
}