mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-31 03:12:16 +00:00
- Rewrite .github/workflows/desktop.yml: 2-job pipeline (validate + build-and-release) with rolling desktop-latest pre-release on push to main and stable desktop-v* releases - Add UpdateChecker component: checks for updates on startup + every 30 min, background download with progress bar, one-click relaunch - Configure Tauri updater: endpoints pointing to desktop-latest release, pubkey placeholder - Add tauri-plugin-process for relaunch support (Cargo.toml, lib.rs, package.json) - Add macOS Entitlements.plist for notarization (network + file access, no sandbox) - Add scripts/bump-desktop-version.sh for atomic version bumps across 3 config files - Add desktop/README.md with dev setup, auto-update architecture, signing docs - Update .gitignore for desktop/node_modules, dist, target - Configure macOS minimumSystemVersion, Windows timestampUrl - Include all Phase 14-21 work: agent hardening, RBAC, taint tracking, workflows, skills, knowledge graph, sessions, A2A, MCP templates, WASM sandbox, TUI dashboard, production tools, CLI expansion, API expansion, learning productionization, Tauri desktop app, and 10 new channels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
209 lines
6.5 KiB
Rust
209 lines
6.5 KiB
Rust
use serde::Serialize;
|
|
use tauri::Manager;
|
|
use tauri_plugin_autostart::MacosLauncher;
|
|
|
|
/// Fetch health status from the OpenJarvis API server.
|
|
#[tauri::command]
|
|
async fn check_health(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/health", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch energy monitoring data from the API.
|
|
#[tauri::command]
|
|
async fn fetch_energy(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/telemetry/energy", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch telemetry statistics from the API.
|
|
#[tauri::command]
|
|
async fn fetch_telemetry(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/telemetry/stats", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch recent traces from the API.
|
|
#[tauri::command]
|
|
async fn fetch_traces(api_url: String, limit: u32) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/traces?limit={}", api_url, limit);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch a single trace by ID.
|
|
#[tauri::command]
|
|
async fn fetch_trace(api_url: String, trace_id: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/traces/{}", api_url, trace_id);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch learning system statistics.
|
|
#[tauri::command]
|
|
async fn fetch_learning_stats(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/learning/stats", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch learning policy configuration.
|
|
#[tauri::command]
|
|
async fn fetch_learning_policy(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/learning/policy", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch memory backend statistics.
|
|
#[tauri::command]
|
|
async fn fetch_memory_stats(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/memory/stats", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Search memory for relevant chunks.
|
|
#[tauri::command]
|
|
async fn search_memory(
|
|
api_url: String,
|
|
query: String,
|
|
top_k: u32,
|
|
) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/memory/search", api_url);
|
|
let client = reqwest::Client::new();
|
|
let resp = client
|
|
.post(&url)
|
|
.json(&serde_json::json!({"query": query, "top_k": top_k}))
|
|
.send()
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Fetch list of available agents.
|
|
#[tauri::command]
|
|
async fn fetch_agents(api_url: String) -> Result<serde_json::Value, String> {
|
|
let url = format!("{}/v1/agents", api_url);
|
|
let resp = reqwest::get(&url)
|
|
.await
|
|
.map_err(|e| format!("Connection failed: {}", e))?;
|
|
let body: serde_json::Value = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("Invalid response: {}", e))?;
|
|
Ok(body)
|
|
}
|
|
|
|
/// Launch the `jarvis` CLI command via shell.
|
|
#[tauri::command]
|
|
async fn run_jarvis_command(args: Vec<String>) -> Result<String, String> {
|
|
let output = tokio::process::Command::new("jarvis")
|
|
.args(&args)
|
|
.output()
|
|
.await
|
|
.map_err(|e| format!("Failed to launch jarvis: {}", e))?;
|
|
|
|
if output.status.success() {
|
|
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
|
} else {
|
|
Err(String::from_utf8_lossy(&output.stderr).to_string())
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
pub fn run() {
|
|
tauri::Builder::default()
|
|
.plugin(tauri_plugin_notification::init())
|
|
.plugin(tauri_plugin_shell::init())
|
|
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
|
.plugin(tauri_plugin_autostart::init(
|
|
MacosLauncher::LaunchAgent,
|
|
Some(vec!["--hidden"]),
|
|
))
|
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
|
.plugin(tauri_plugin_process::init())
|
|
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
|
// Focus the main window if another instance is launched
|
|
if let Some(window) = app.get_webview_window("main") {
|
|
let _ = window.set_focus();
|
|
}
|
|
}))
|
|
.setup(|app| {
|
|
// Set up system tray menu
|
|
let _tray = app.tray_by_id("main");
|
|
|
|
Ok(())
|
|
})
|
|
.invoke_handler(tauri::generate_handler![
|
|
check_health,
|
|
fetch_energy,
|
|
fetch_telemetry,
|
|
fetch_traces,
|
|
fetch_trace,
|
|
fetch_learning_stats,
|
|
fetch_learning_policy,
|
|
fetch_memory_stats,
|
|
search_memory,
|
|
fetch_agents,
|
|
run_jarvis_command,
|
|
])
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running OpenJarvis Desktop");
|
|
}
|