This commit is contained in:
Gilles Soulier
2026-01-05 13:13:08 +01:00
parent 8e14adafc6
commit 1d177e96a6
149 changed files with 29541 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
# Created by: Codex
# Date: 2026-01-05
# Purpose: Tauri backend for Mesh Agent UI
# Refs: CLAUDE.md
[package]
name = "mesh-agent-ui"
version = "0.1.0"
description = "Desktop UI for Mesh Agent"
authors = ["Mesh Team"]
edition = "2021"
[dependencies]
mesh_agent = { path = "../..", package = "mesh-agent" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "2.0", features = [] }
tokio = { version = "1.35", features = ["rt-multi-thread", "macros"] }
[build-dependencies]
tauri-build = { version = "2.0", features = [] }

View File

@@ -0,0 +1,8 @@
// Created by: Codex
// Date: 2026-01-05
// Purpose: Build script for Tauri
// Refs: CLAUDE.md
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,94 @@
// Created by: Codex
// Date: 2026-01-05
// Purpose: Tauri commands for Mesh Agent UI
// Refs: CLAUDE.md
use serde::Serialize;
use tokio::sync::Mutex;
use mesh_agent::config::Config;
use mesh_agent::runner::AgentHandle;
#[derive(Default)]
pub struct AgentState {
pub running: bool,
pub last_error: Option<String>,
pub handle: Option<AgentHandle>,
}
pub struct AppState {
pub inner: Mutex<AgentState>,
}
#[derive(Serialize)]
pub struct AgentStatus {
pub running: bool,
pub last_error: Option<String>,
}
impl AgentStatus {
fn from_state(state: &AgentState) -> Self {
Self {
running: state.running,
last_error: state.last_error.clone(),
}
}
}
#[tauri::command]
pub async fn get_status(state: tauri::State<'_, AppState>) -> Result<AgentStatus, String> {
let guard = state.inner.lock().await;
Ok(AgentStatus::from_state(&guard))
}
#[tauri::command]
pub async fn get_config() -> Result<Config, String> {
Config::load().map_err(|err| err.to_string())
}
#[tauri::command]
pub async fn save_config(config: Config) -> Result<(), String> {
config.save_default_path().map_err(|err| err.to_string())
}
#[tauri::command]
pub async fn start_agent(state: tauri::State<'_, AppState>) -> Result<AgentStatus, String> {
{
let guard = state.inner.lock().await;
if guard.running {
return Ok(AgentStatus::from_state(&guard));
}
}
let config = Config::load().map_err(|err| err.to_string())?;
let handle = mesh_agent::runner::start_agent(config)
.await
.map_err(|err| err.to_string())?;
let mut guard = state.inner.lock().await;
guard.handle = Some(handle);
guard.running = true;
guard.last_error = None;
Ok(AgentStatus::from_state(&guard))
}
#[tauri::command]
pub async fn stop_agent(state: tauri::State<'_, AppState>) -> Result<AgentStatus, String> {
let handle = {
let mut guard = state.inner.lock().await;
guard.running = false;
guard.last_error = None;
guard.handle.take()
};
if let Some(handle) = handle {
if let Err(err) = handle.stop().await {
let mut guard = state.inner.lock().await;
guard.last_error = Some(err.to_string());
}
}
let guard = state.inner.lock().await;
Ok(AgentStatus::from_state(&guard))
}

View File

@@ -0,0 +1,30 @@
// Created by: Codex
// Date: 2026-01-05
// Purpose: Tauri entrypoint for Mesh Agent UI
// Refs: CLAUDE.md
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod commands;
use commands::{AppState, AgentState};
use tokio::sync::Mutex;
fn main() {
let result = tauri::Builder::default()
.manage(AppState {
inner: Mutex::new(AgentState::default()),
})
.invoke_handler(tauri::generate_handler![
commands::get_status,
commands::get_config,
commands::save_config,
commands::start_agent,
commands::stop_agent,
])
.run(tauri::generate_context!());
if let Err(err) = result {
eprintln!("Mesh Agent UI failed to start: {}", err);
}
}

View File

@@ -0,0 +1,25 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Mesh Agent",
"version": "0.1.0",
"identifier": "com.mesh.agent",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devUrl": "http://localhost:5173",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "Mesh Agent",
"width": 1080,
"height": 720,
"resizable": true
}
],
"security": {
"csp": null
}
}
}