mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-08-15 04:11:16 +00:00
190 lines
5.4 KiB
Rust
190 lines
5.4 KiB
Rust
use crate::local_ai::resource::WatchDiskEvent;
|
|
use flowy_error::{FlowyError, FlowyResult};
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
|
use tracing::{error, trace};
|
|
|
|
#[cfg(windows)]
|
|
use winreg::{enums::*, RegKey};
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
|
#[allow(dead_code)]
|
|
pub struct WatchContext {
|
|
watcher: notify::RecommendedWatcher,
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
|
#[allow(dead_code)]
|
|
pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver<WatchDiskEvent>)> {
|
|
use notify::{Event, Watcher};
|
|
|
|
let install_path = install_path().ok_or_else(|| {
|
|
FlowyError::internal().with_context("Unsupported platform for offline app watching")
|
|
})?;
|
|
let (tx, rx) = unbounded_channel();
|
|
let app_path = ollama_plugin_path();
|
|
let mut watcher = notify::recommended_watcher(move |res: Result<Event, _>| match res {
|
|
Ok(event) => {
|
|
if event.paths.iter().any(|path| path == &app_path) {
|
|
trace!("watch event: {:?}", event);
|
|
match event.kind {
|
|
notify::EventKind::Create(_) => {
|
|
if let Err(err) = tx.send(WatchDiskEvent::Create) {
|
|
error!("watch send error: {:?}", err)
|
|
}
|
|
},
|
|
notify::EventKind::Remove(_) => {
|
|
if let Err(err) = tx.send(WatchDiskEvent::Remove) {
|
|
error!("watch send error: {:?}", err)
|
|
}
|
|
},
|
|
_ => {
|
|
trace!("unhandle watch event: {:?}", event);
|
|
},
|
|
}
|
|
}
|
|
},
|
|
Err(e) => error!("watch error: {:?}", e),
|
|
})
|
|
.map_err(|err| FlowyError::internal().with_context(err))?;
|
|
watcher
|
|
.watch(&install_path, notify::RecursiveMode::NonRecursive)
|
|
.map_err(|err| FlowyError::internal().with_context(err))?;
|
|
|
|
Ok((
|
|
WatchContext {
|
|
watcher,
|
|
path: install_path,
|
|
},
|
|
rx,
|
|
))
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
|
pub(crate) fn install_path() -> Option<PathBuf> {
|
|
None
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
|
pub(crate) fn install_path() -> Option<PathBuf> {
|
|
#[cfg(target_os = "windows")]
|
|
return None;
|
|
|
|
#[cfg(target_os = "macos")]
|
|
return Some(PathBuf::from("/usr/local/bin"));
|
|
|
|
#[cfg(target_os = "linux")]
|
|
return None;
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
|
pub fn is_plugin_ready() -> bool {
|
|
ollama_plugin_path().exists() || ollama_plugin_command_available()
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
|
pub fn is_plugin_ready() -> bool {
|
|
false
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
|
pub(crate) fn ollama_plugin_path() -> PathBuf {
|
|
PathBuf::new()
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
|
pub(crate) fn ollama_plugin_path() -> std::path::PathBuf {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
// Use LOCALAPPDATA for a user-specific installation path on Windows.
|
|
let local_appdata =
|
|
std::env::var("LOCALAPPDATA").unwrap_or_else(|_| "C:\\Program Files".to_string());
|
|
std::path::PathBuf::from(local_appdata).join("Programs\\appflowy_plugin\\ollama_ai_plugin.exe")
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
let offline_app = "ollama_ai_plugin";
|
|
std::path::PathBuf::from(format!("/usr/local/bin/{}", offline_app))
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
let offline_app = "ollama_ai_plugin";
|
|
std::path::PathBuf::from(format!("/usr/local/bin/{}", offline_app))
|
|
}
|
|
}
|
|
|
|
pub(crate) fn ollama_plugin_command_available() -> bool {
|
|
if cfg!(windows) {
|
|
#[cfg(windows)]
|
|
{
|
|
// 1. Try "where" command first
|
|
let output = Command::new("cmd")
|
|
.args(["/C", "where", "ollama_ai_plugin"])
|
|
.output();
|
|
if let Ok(output) = output {
|
|
if !output.stdout.is_empty() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// 2. Fallback: Check registry PATH for the executable
|
|
let path_dirs = get_windows_path_dirs();
|
|
let plugin_exe = "ollama_ai_plugin.exe"; // Adjust name if needed
|
|
|
|
path_dirs.iter().any(|dir| {
|
|
let full_path = std::path::Path::new(dir).join(plugin_exe);
|
|
full_path.exists()
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
false
|
|
} else {
|
|
let output = Command::new("command")
|
|
.args(["-v", "ollama_ai_plugin"])
|
|
.output();
|
|
match output {
|
|
Ok(o) => !o.stdout.is_empty(),
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
fn get_windows_path_dirs() -> Vec<String> {
|
|
let mut paths = Vec::new();
|
|
|
|
// Check HKEY_CURRENT_USER\Environment
|
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
|
if let Ok(env) = hkcu.open_subkey("Environment") {
|
|
if let Ok(path) = env.get_value::<String, _>("Path") {
|
|
paths.extend(path.split(';').map(|s| s.trim().to_string()));
|
|
}
|
|
}
|
|
|
|
// Check HKEY_LOCAL_MACHINE\SYSTEM\...\Environment
|
|
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
|
if let Ok(env) = hklm.open_subkey(r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")
|
|
{
|
|
if let Ok(path) = env.get_value::<String, _>("Path") {
|
|
paths.extend(path.split(';').map(|s| s.trim().to_string()));
|
|
}
|
|
}
|
|
paths
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::local_ai::watch::ollama_plugin_command_available;
|
|
|
|
#[test]
|
|
fn test_command_import() {
|
|
let result = ollama_plugin_command_available();
|
|
println!("ollama plugin exist: {:?}", result);
|
|
}
|
|
}
|