Backport: Agentic Shell Command Execution (v1.0.0 → v1.0.8) #66
38
scripts/download-kubectl.sh
Executable file
38
scripts/download-kubectl.sh
Executable file
@ -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"
|
||||||
3
src-tauri/Cargo.lock
generated
3
src-tauri/Cargo.lock
generated
@ -6370,6 +6370,7 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
|
"http 1.4.0",
|
||||||
"infer 0.15.0",
|
"infer 0.15.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lopdf",
|
"lopdf",
|
||||||
@ -6391,7 +6392,7 @@ dependencies = [
|
|||||||
"tauri-plugin-http",
|
"tauri-plugin-http",
|
||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tauri-plugin-stronghold",
|
"tauri-plugin-stronghold",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|||||||
@ -30,7 +30,7 @@ docx-rs = "0.4"
|
|||||||
sha2 = { version = "0.10", features = ["std"] }
|
sha2 = { version = "0.10", features = ["std"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
thiserror = "1"
|
thiserror = "2"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
@ -53,6 +53,7 @@ rmcp = { version = "1.7.0", features = [
|
|||||||
"transport-child-process",
|
"transport-child-process",
|
||||||
"transport-streamable-http-client-reqwest",
|
"transport-streamable-http-client-reqwest",
|
||||||
] }
|
] }
|
||||||
|
http = "1.4"
|
||||||
flate2 = { version = "1", features = ["rust_backend"] }
|
flate2 = { version = "1", features = ["rust_backend"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@ -333,6 +333,7 @@ pub async fn initiate_oauth(
|
|||||||
app_data_dir,
|
app_data_dir,
|
||||||
integration_webviews,
|
integration_webviews,
|
||||||
mcp_connections,
|
mcp_connections,
|
||||||
|
pending_approvals: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
|
||||||
};
|
};
|
||||||
while let Some(callback) = callback_rx.recv().await {
|
while let Some(callback) = callback_rx.recv().await {
|
||||||
tracing::info!("Received OAuth callback for state: {}", callback.state);
|
tracing::info!("Received OAuth callback for state: {}", callback.state);
|
||||||
|
|||||||
@ -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<std::path::PathBuf> {
|
||||||
|
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<bool> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user