diff --git a/scripts/download-kubectl.sh b/scripts/download-kubectl.sh new file mode 100755 index 00000000..0bb8b137 --- /dev/null +++ b/scripts/download-kubectl.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +KUBECTL_VERSION="v1.30.0" +BINARIES_DIR="src-tauri/binaries" + +echo "Downloading kubectl binaries version ${KUBECTL_VERSION}..." + +mkdir -p "$BINARIES_DIR" + +# Download for all platforms +# Tauri uses this structure: binaries/kubectl-{target-triple} or kubectl-{target-triple}.exe +echo "Downloading kubectl for Linux x86_64..." +curl -L -o "$BINARIES_DIR/kubectl-x86_64-unknown-linux-gnu" \ + "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" + +echo "Downloading kubectl for Linux aarch64..." +curl -L -o "$BINARIES_DIR/kubectl-aarch64-unknown-linux-gnu" \ + "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/arm64/kubectl" + +echo "Downloading kubectl for macOS x86_64..." +curl -L -o "$BINARIES_DIR/kubectl-x86_64-apple-darwin" \ + "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/darwin/amd64/kubectl" + +echo "Downloading kubectl for macOS aarch64..." +curl -L -o "$BINARIES_DIR/kubectl-aarch64-apple-darwin" \ + "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/darwin/arm64/kubectl" + +echo "Downloading kubectl for Windows x86_64..." +curl -L -o "$BINARIES_DIR/kubectl-x86_64-pc-windows-gnu.exe" \ + "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/windows/amd64/kubectl.exe" + +# Make binaries executable (not needed for Windows .exe) +chmod +x "$BINARIES_DIR"/kubectl-*-linux-* "$BINARIES_DIR"/kubectl-*-darwin + +echo "kubectl binaries downloaded successfully to $BINARIES_DIR" +echo "Total size:" +du -sh "$BINARIES_DIR" diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 10938958..2add6d5a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -6370,6 +6370,7 @@ dependencies = [ "flate2", "futures", "hex", + "http 1.4.0", "infer 0.15.0", "lazy_static", "lopdf", @@ -6391,7 +6392,7 @@ dependencies = [ "tauri-plugin-http", "tauri-plugin-shell", "tauri-plugin-stronghold", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tokio-test", "tracing", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index a0e24a9e..2a94fdc8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -30,7 +30,7 @@ docx-rs = "0.4" sha2 = { version = "0.10", features = ["std"] } hex = "0.4" anyhow = "1" -thiserror = "1" +thiserror = "2" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } chrono = { version = "0.4", features = ["serde"] } @@ -53,6 +53,7 @@ rmcp = { version = "1.7.0", features = [ "transport-child-process", "transport-streamable-http-client-reqwest", ] } +http = "1.4" flate2 = { version = "1", features = ["rust_backend"] } [dev-dependencies] diff --git a/src-tauri/src/commands/integrations.rs b/src-tauri/src/commands/integrations.rs index f33ad70a..5f5d47c8 100644 --- a/src-tauri/src/commands/integrations.rs +++ b/src-tauri/src/commands/integrations.rs @@ -333,6 +333,7 @@ pub async fn initiate_oauth( app_data_dir, integration_webviews, mcp_connections, + pending_approvals: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())), }; while let Some(callback) = callback_rx.recv().await { tracing::info!("Received OAuth callback for state: {}", callback.state); diff --git a/src-tauri/src/ollama/installer.rs b/src-tauri/src/ollama/installer.rs index 352b4660..77b734ce 100644 --- a/src-tauri/src/ollama/installer.rs +++ b/src-tauri/src/ollama/installer.rs @@ -95,6 +95,189 @@ pub fn get_install_instructions(platform: &str) -> InstallGuide { } } +/// Helper to find Ollama binary in common locations +fn find_ollama_binary() -> Option { + let common_paths = [ + "/usr/local/bin/ollama", + "/opt/homebrew/bin/ollama", + "/usr/bin/ollama", + "/home/linuxbrew/.linuxbrew/bin/ollama", + ]; + + for path in &common_paths { + let p = std::path::Path::new(path); + if p.exists() { + return Some(p.to_path_buf()); + } + } + + // Fallback to which/where command + let which_cmd = if cfg!(target_os = "windows") { + "where" + } else { + "which" + }; + + std::process::Command::new(which_cmd) + .arg("ollama") + .output() + .ok() + .and_then(|output| { + if output.status.success() { + String::from_utf8(output.stdout) + .ok() + .map(|s| std::path::PathBuf::from(s.trim())) + } else { + None + } + }) +} + +/// Attempt to start Ollama service if installed but not running +pub async fn start_ollama_service() -> anyhow::Result { + let status = check_ollama().await?; + + // If already running, nothing to do + if status.running { + tracing::info!("Ollama is already running"); + return Ok(true); + } + + // If not installed, can't start it + if !status.installed { + tracing::warn!("Ollama is not installed, cannot auto-start"); + return Ok(false); + } + + tracing::info!("Ollama is installed but not running, attempting to start..."); + + // Platform-specific start logic + #[cfg(target_os = "macos")] + { + // On macOS, try to launch Ollama.app which manages the service + let ollama_app = "/Applications/Ollama.app"; + if std::path::Path::new(ollama_app).exists() { + tracing::info!("Launching Ollama.app..."); + let result = std::process::Command::new("open").arg(ollama_app).spawn(); + + match result { + Ok(_) => { + // Wait a few seconds for Ollama to start + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + + // Check if it's now running + let new_status = check_ollama().await?; + if new_status.running { + tracing::info!("Ollama started successfully via Ollama.app"); + return Ok(true); + } else { + tracing::warn!("Ollama.app launched but service not responding yet"); + return Ok(false); + } + } + Err(e) => { + tracing::error!("Failed to launch Ollama.app: {}", e); + } + } + } + + // Fallback: try direct ollama serve with full path + if let Some(ollama_bin) = find_ollama_binary() { + tracing::info!( + "Attempting to start ollama serve directly at {:?}...", + ollama_bin + ); + let result = std::process::Command::new(&ollama_bin) + .arg("serve") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn(); + + match result { + Ok(_) => { + // Wait for service to become available + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let new_status = check_ollama().await?; + Ok(new_status.running) + } + Err(e) => { + tracing::error!("Failed to start ollama serve: {}", e); + Ok(false) + } + } + } else { + tracing::error!("Ollama binary not found in PATH or common locations"); + Ok(false) + } + } + + #[cfg(target_os = "linux")] + { + // On Linux, start ollama serve in background using full path + if let Some(ollama_bin) = find_ollama_binary() { + tracing::info!("Starting ollama serve at {:?}...", ollama_bin); + let result = std::process::Command::new(&ollama_bin) + .arg("serve") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn(); + + match result { + Ok(_) => { + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let new_status = check_ollama().await?; + if new_status.running { + tracing::info!("Ollama started successfully"); + Ok(true) + } else { + tracing::warn!("ollama serve started but not responding yet"); + Ok(false) + } + } + Err(e) => { + tracing::error!("Failed to start ollama serve: {}", e); + Ok(false) + } + } + } else { + tracing::error!("Ollama binary not found"); + Ok(false) + } + } + + #[cfg(target_os = "windows")] + { + // On Windows, Ollama runs as a service, check if we can start it + tracing::info!("Attempting to start Ollama on Windows..."); + if let Some(ollama_bin) = find_ollama_binary() { + let result = std::process::Command::new(&ollama_bin) + .arg("serve") + .spawn(); + + match result { + Ok(_) => { + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let new_status = check_ollama().await?; + Ok(new_status.running) + } + Err(e) => { + tracing::error!("Failed to start Ollama: {}", e); + Ok(false) + } + } + } else { + tracing::error!("Ollama binary not found"); + Ok(false) + } + } + + #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] + { + tracing::warn!("Auto-start not supported on this platform"); + Ok(false) + } +} + #[cfg(test)] mod tests { use super::*;