diff --git a/Cargo.lock b/Cargo.lock index 9e4a577..fa6f289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1324,7 +1324,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lore" -version = "0.9.4" +version = "0.9.5" dependencies = [ "asupersync", "async-stream", diff --git a/Cargo.toml b/Cargo.toml index 93441eb..9bb8f7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lore" -version = "0.9.4" +version = "0.9.5" edition = "2024" description = "Gitlore - Local GitLab data management with semantic search" authors = ["Taylor Eernisse"] diff --git a/src/core/ollama_mgmt.rs b/src/core/ollama_mgmt.rs index 4a53185..2f350dd 100644 --- a/src/core/ollama_mgmt.rs +++ b/src/core/ollama_mgmt.rs @@ -47,14 +47,34 @@ fn is_local_url(base_url: &str) -> bool { // ── Detection (sync, fast) ── -/// Check if the `ollama` binary is on PATH. Returns the path if found. +/// Find the `ollama` binary. Checks PATH first, then well-known install +/// locations as fallback (cron jobs have a minimal PATH that typically +/// excludes Homebrew and other user-installed paths). pub fn find_ollama_binary() -> Option { - Command::new("which") + // Try PATH first (works in interactive shells) + let from_path = Command::new("which") .arg("ollama") .output() .ok() .filter(|o| o.status.success()) - .map(|o| PathBuf::from(String::from_utf8_lossy(&o.stdout).trim().to_string())) + .map(|o| PathBuf::from(String::from_utf8_lossy(&o.stdout).trim().to_string())); + + if from_path.is_some() { + return from_path; + } + + // Fallback: check well-known locations (for cron/launchd contexts) + const WELL_KNOWN: &[&str] = &[ + "/opt/homebrew/bin/ollama", // macOS Apple Silicon (Homebrew) + "/usr/local/bin/ollama", // macOS Intel (Homebrew) / Linux manual + "/usr/bin/ollama", // Linux package manager + "/snap/bin/ollama", // Linux Snap + ]; + + WELL_KNOWN + .iter() + .map(PathBuf::from) + .find(|p| p.is_file()) } /// Quick sync check: can we TCP-connect to Ollama's HTTP port? @@ -118,9 +138,9 @@ pub fn ensure_ollama(base_url: &str) -> OllamaEnsureResult { let is_local = is_local_url(base_url); // Step 1: Is the binary installed? (only relevant for local) - if is_local { - let installed = find_ollama_binary().is_some(); - if !installed { + let binary_path = if is_local { + let path = find_ollama_binary(); + if path.is_none() { return OllamaEnsureResult { installed: false, was_running: false, @@ -130,7 +150,10 @@ pub fn ensure_ollama(base_url: &str) -> OllamaEnsureResult { install_hint: Some(install_instructions().to_string()), }; } - } + path + } else { + None + }; // Step 2: Already running? if is_ollama_reachable(base_url) { @@ -158,8 +181,11 @@ pub fn ensure_ollama(base_url: &str) -> OllamaEnsureResult { }; } - // Step 4: Try to start it (local only) - let spawn_result = Command::new("ollama") + // Step 4: Try to start it (local only, using discovered absolute path) + // Using the absolute path is critical — cron has a minimal PATH that + // typically excludes Homebrew and other user-installed locations. + let ollama_bin = binary_path.expect("binary_path is Some for local URLs after step 1"); + let spawn_result = Command::new(&ollama_bin) .arg("serve") .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null())