Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- 新增 66 个 @reserved 标注 (已有 22 个) - 覆盖: agent/butler/classroom/hand/mcp/pipeline/skill/trigger/viking/zclaw 等模块 - MCP 命令增加 @connected 注释说明前端接入路径 - @reserved 总数: 89 (含 identity_init)
267 lines
7.7 KiB
Rust
267 lines
7.7 KiB
Rust
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<String>,
|
|
build_date: Option<String>,
|
|
runtime_source: Option<String>,
|
|
raw: Value,
|
|
}
|
|
|
|
/// Process information structure
|
|
#[derive(Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct ProcessInfo {
|
|
pid: u32,
|
|
name: String,
|
|
status: String,
|
|
cpu_percent: Option<f64>,
|
|
memory_mb: Option<f64>,
|
|
uptime_seconds: Option<u64>,
|
|
}
|
|
|
|
/// Process list response
|
|
#[derive(Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct ProcessListResponse {
|
|
processes: Vec<ProcessInfo>,
|
|
total_count: usize,
|
|
runtime_source: Option<String>,
|
|
}
|
|
|
|
/// Process logs response
|
|
#[derive(Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct ProcessLogsResponse {
|
|
pid: Option<u32>,
|
|
logs: String,
|
|
lines: usize,
|
|
runtime_source: Option<String>,
|
|
}
|
|
|
|
/// Get ZCLAW Kernel status
|
|
// @reserved: system control
|
|
// @connected
|
|
#[tauri::command]
|
|
pub fn zclaw_status(app: AppHandle) -> Result<LocalGatewayStatus, String> {
|
|
read_gateway_status(&app)
|
|
}
|
|
|
|
/// Start ZCLAW Kernel
|
|
// @reserved: system control
|
|
// @connected
|
|
#[tauri::command]
|
|
pub fn zclaw_start(app: AppHandle) -> Result<LocalGatewayStatus, String> {
|
|
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<LocalGatewayStatus, String> {
|
|
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<LocalGatewayStatus, String> {
|
|
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<LocalGatewayAuth, String> {
|
|
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<LocalGatewayPrepareResult, String> {
|
|
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<String>,
|
|
) -> Result<LocalGatewayPairingApprovalResult, String> {
|
|
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<String, String> {
|
|
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<ProcessListResponse, String> {
|
|
let result = run_zclaw(&app, &["process", "list", "--json"])?;
|
|
|
|
let raw = parse_json_output(&result.stdout).unwrap_or_else(|_| json!({"processes": []}));
|
|
|
|
let processes: Vec<ProcessInfo> = 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<u32>,
|
|
lines: Option<usize>,
|
|
) -> Result<ProcessLogsResponse, String> {
|
|
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<String> = 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::<Vec<_>>()
|
|
.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<VersionResponse, String> {
|
|
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,
|
|
})
|
|
}
|