use serde::Serialize; use serde_json::{json, Value}; use std::thread; use std::time::Duration; use tauri::AppHandle; use super::config::{ approve_local_device_pairing, ensure_local_gateway_ready_for_tauri, read_local_gateway_auth, LocalGatewayAuth, LocalGatewayPairingApprovalResult, LocalGatewayPrepareResult, }; use super::io::{parse_json_output, read_gateway_status, run_zclaw, LocalGatewayStatus}; #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct VersionResponse { version: String, commit: Option, build_date: Option, runtime_source: Option, raw: Value, } /// Process information structure #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct ProcessInfo { pid: u32, name: String, status: String, cpu_percent: Option, memory_mb: Option, uptime_seconds: Option, } /// Process list response #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct ProcessListResponse { processes: Vec, total_count: usize, runtime_source: Option, } /// Process logs response #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct ProcessLogsResponse { pid: Option, logs: String, lines: usize, runtime_source: Option, } /// Get ZCLAW Kernel status // @reserved: system control // @connected #[tauri::command] pub fn zclaw_status(app: AppHandle) -> Result { read_gateway_status(&app) } /// Start ZCLAW Kernel // @reserved: system control // @connected #[tauri::command] pub fn zclaw_start(app: AppHandle) -> Result { ensure_local_gateway_ready_for_tauri(&app)?; run_zclaw(&app, &["gateway", "start", "--json"])?; thread::sleep(Duration::from_millis(800)); read_gateway_status(&app) } /// Stop ZCLAW Kernel // @reserved: system control // @connected #[tauri::command] pub fn zclaw_stop(app: AppHandle) -> Result { run_zclaw(&app, &["gateway", "stop", "--json"])?; thread::sleep(Duration::from_millis(800)); read_gateway_status(&app) } /// Restart ZCLAW Kernel // @reserved: system control // @connected #[tauri::command] pub fn zclaw_restart(app: AppHandle) -> Result { ensure_local_gateway_ready_for_tauri(&app)?; run_zclaw(&app, &["gateway", "restart", "--json"])?; thread::sleep(Duration::from_millis(1200)); read_gateway_status(&app) } /// Get local auth token from ZCLAW config // @reserved: system control // @connected #[tauri::command] pub fn zclaw_local_auth() -> Result { read_local_gateway_auth() } /// Prepare ZCLAW for Tauri (update allowed origins) // @reserved: system control // @connected #[tauri::command] pub fn zclaw_prepare_for_tauri(app: AppHandle) -> Result { ensure_local_gateway_ready_for_tauri(&app) } /// Approve device pairing request // @reserved: system control // @connected #[tauri::command] pub fn zclaw_approve_device_pairing( app: AppHandle, device_id: String, public_key_base64: String, url: Option, ) -> Result { approve_local_device_pairing(&app, &device_id, &public_key_base64, url.as_deref()) } /// @reserved — no frontend UI yet /// Run ZCLAW doctor to diagnose issues #[tauri::command] pub fn zclaw_doctor(app: AppHandle) -> Result { let result = run_zclaw(&app, &["doctor", "--json"])?; Ok(result.stdout) } /// List ZCLAW processes // @reserved: system control // @connected #[tauri::command] pub fn zclaw_process_list(app: AppHandle) -> Result { let result = run_zclaw(&app, &["process", "list", "--json"])?; let raw = parse_json_output(&result.stdout).unwrap_or_else(|_| json!({"processes": []})); let processes: Vec = raw .get("processes") .and_then(Value::as_array) .map(|arr| { arr.iter() .filter_map(|p| { Some(ProcessInfo { pid: p.get("pid").and_then(Value::as_u64)?.try_into().ok()?, name: p.get("name").and_then(Value::as_str)?.to_string(), status: p .get("status") .and_then(Value::as_str) .unwrap_or("unknown") .to_string(), cpu_percent: p.get("cpuPercent").and_then(Value::as_f64), memory_mb: p.get("memoryMb").and_then(Value::as_f64), uptime_seconds: p.get("uptimeSeconds").and_then(Value::as_u64), }) }) .collect() }) .unwrap_or_default(); Ok(ProcessListResponse { total_count: processes.len(), processes, runtime_source: Some(result.runtime.source), }) } /// Get ZCLAW process logs // @reserved: system control // @connected #[tauri::command] pub fn zclaw_process_logs( app: AppHandle, pid: Option, lines: Option, ) -> Result { let line_count = lines.unwrap_or(100); let lines_str = line_count.to_string(); // Build owned strings first to avoid lifetime issues let args: Vec = if let Some(pid_value) = pid { vec![ "process".to_string(), "logs".to_string(), "--pid".to_string(), pid_value.to_string(), "--lines".to_string(), lines_str, "--json".to_string(), ] } else { vec![ "process".to_string(), "logs".to_string(), "--lines".to_string(), lines_str, "--json".to_string(), ] }; // Convert to &str for the command let args_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); let result = run_zclaw(&app, &args_refs)?; // Parse the logs - could be JSON array or plain text let logs = if let Ok(json) = parse_json_output(&result.stdout) { // If JSON format, extract logs array or convert to string if let Some(log_lines) = json.get("logs").and_then(Value::as_array) { log_lines .iter() .filter_map(|l| l.as_str()) .collect::>() .join("\n") } else if let Some(log_text) = json.get("log").and_then(Value::as_str) { log_text.to_string() } else { result.stdout.clone() } } else { result.stdout.clone() }; let log_lines_count = logs.lines().count(); Ok(ProcessLogsResponse { pid, logs, lines: log_lines_count, runtime_source: Some(result.runtime.source), }) } /// Get ZCLAW version information // @reserved: system control // @connected #[tauri::command] pub fn zclaw_version(app: AppHandle) -> Result { let result = run_zclaw(&app, &["--version", "--json"])?; let raw = parse_json_output(&result.stdout).unwrap_or_else(|_| { // Fallback: try to parse plain text version output json!({ "version": result.stdout.trim(), "raw": result.stdout.trim() }) }); let version = raw .get("version") .and_then(Value::as_str) .unwrap_or("unknown") .to_string(); let commit = raw.get("commit").and_then(Value::as_str).map(ToOwned::to_owned); let build_date = raw.get("buildDate").and_then(Value::as_str).map(ToOwned::to_owned); Ok(VersionResponse { version, commit, build_date, runtime_source: Some(result.runtime.source), raw, }) }