From 3ee4796a81f676e4faa3273033fa6ef061f01adb Mon Sep 17 00:00:00 2001 From: MrFadiAi Date: Thu, 19 Feb 2026 06:04:50 +0100 Subject: [PATCH] fix: gateway start on windows, auth flow, and ui enhancements --- README.md | 2 ++ src-tauri/src/commands/config.rs | 44 +++++++++++++++++++++++++------- src-tauri/src/utils/shell.rs | 22 ++++++++-------- src/components/Agents/index.tsx | 38 +++------------------------ 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 6f4e566..32faead 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,12 @@ Real-time monitoring and full lifecycle management of the OpenClaw service. - Live service status (port, PID, memory usage, uptime) - **Start / Stop / Restart / Kill All** actions - Real-time log viewer with auto-refresh +- **Web Control UI**: Direct chat interface with your agents (served at `http://localhost:18789`) **How to Use:** - **Start Service:** Click the **Start** button in the dashboard top-right corner. - **View Logs:** Check the "Live Logs" card for immediate output or go to the **Logs** tab for history. +- **Control UI:** Once the service is running, open `http://localhost:18789` to chat with your agents directly. - **System Check:** Use the "System Requirements" card to verify your environment health. ### 🤖 AI Model Configuration diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index f4f5ec5..bb9c603 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -1939,7 +1939,8 @@ pub async fn save_telegram_account(account: TelegramAccount) -> Result Result Result { // Update or add the agent if let Some(idx) = match_index { let existing = &mut list[idx]; + // Merge: only overwrite fields the user explicitly set (non-empty) + if let Some(name) = &agent.name { + if !name.is_empty() { + existing["name"] = json!(name); + } + } if let Some(model) = &agent.model { if !model.is_empty() { existing["model"] = json!({ "primary": model }); @@ -2634,6 +2642,14 @@ pub async fn save_agent(agent: AgentInfo) -> Result { existing["default"] = json!(true); } } + + // Enforce "Main" agent properties + if agent.id.eq_ignore_ascii_case("main") { + // "Main" should always be default unless user explicitly sets another default (which handles itself) + // But to ensure fallback behavior, we mark it. + existing["default"] = json!(true); + } + if let Some(sub) = &agent.subagents { if let Some(allow) = &sub.allow_agents { if !allow.is_empty() { @@ -2801,19 +2817,29 @@ pub async fn delete_agent(agent_id: String) -> Result { if let Some(agent_dir) = agent_dir_to_delete { let path = std::path::Path::new(&agent_dir); - if path.exists() { - info!("[Agents] Removing agent directory: {}", agent_dir); - if let Err(e) = std::fs::remove_dir_all(path) { - warn!("[Agents] Failed to remove agent directory {}: {}", agent_dir, e); + // Check if this is a nested 'agent' directory (standard structure: .../agents//agent) + // If so, we want to delete the PARENT directory (e.g. .../agents/) to clean up everything including sessions. + let path_to_remove = if path.ends_with("agent") { + path.parent().unwrap_or(path) + } else { + path + }; + + if path_to_remove.exists() { + info!("[Agents] Removing agent directory tree: {:?}", path_to_remove); + if let Err(e) = std::fs::remove_dir_all(path_to_remove) { + warn!("[Agents] Failed to remove agent directory {:?}: {}", path_to_remove, e); } } } else { // Fallback: try default location if not specified in config let openclaw_home = platform::get_config_dir(); - let default_agent_dir = std::path::Path::new(&openclaw_home).join("agents").join(&agent_id); - if default_agent_dir.exists() { - info!("[Agents] Removing default agent directory: {:?}", default_agent_dir); - if let Err(e) = std::fs::remove_dir_all(&default_agent_dir) { + // Default structure is now ~/.openclaw/agents/ (which contains agent/, sessions/, etc.) + let default_agent_root = std::path::Path::new(&openclaw_home).join("agents").join(&agent_id); + + if default_agent_root.exists() { + info!("[Agents] Removing default agent directory tree: {:?}", default_agent_root); + if let Err(e) = std::fs::remove_dir_all(&default_agent_root) { warn!("[Agents] Failed to remove default agent directory: {}", e); } } diff --git a/src-tauri/src/utils/shell.rs b/src-tauri/src/utils/shell.rs index 5f0483c..eb38d34 100644 --- a/src-tauri/src/utils/shell.rs +++ b/src-tauri/src/utils/shell.rs @@ -364,13 +364,11 @@ pub fn run_openclaw(args: &[&str]) -> Result { let extended_path = get_extended_path(); debug!("[Shell] Extended PATH: {}", extended_path); - let output = if openclaw_path.ends_with(".cmd") { - // Windows: .cmd files need to be executed via cmd /c - let mut cmd_args = vec!["/c", &openclaw_path]; - cmd_args.extend(args); - let mut cmd = Command::new("cmd"); + let output = if platform::is_windows() && openclaw_path.ends_with(".cmd") { + // Windows: .cmd files can be executed directly + let mut cmd = Command::new(&openclaw_path); let gw_token = get_gateway_token_from_config(); - cmd.args(&cmd_args) + cmd.args(args) .env("OPENCLAW_GATEWAY_TOKEN", &gw_token) .env("PATH", &extended_path); @@ -559,15 +557,15 @@ pub fn spawn_openclaw_gateway() -> io::Result<()> { let extended_path = get_extended_path(); info!("[Shell] Extended PATH: {}", extended_path); - // On Windows, .cmd files need to be executed via cmd /c + // On Windows, .cmd files can be executed directly by Command::new // Set environment variable OPENCLAW_GATEWAY_TOKEN so all subcommands can use it automatically - let mut cmd = if openclaw_path.ends_with(".cmd") { - info!("[Shell] Windows mode: executing via cmd /c"); - let mut c = Command::new("cmd"); - c.args(["/c", &openclaw_path, "gateway", "--port", "18789"]); + let mut cmd = if platform::is_windows() && openclaw_path.ends_with(".cmd") { + info!("[Shell] Windows mode: executing .cmd directly"); + let mut c = Command::new(&openclaw_path); + c.args(["gateway", "--port", "18789"]); c } else { - info!("[Shell] Unix mode: executing directly"); + info!("[Shell] Unix/Direct mode: executing directly"); let mut c = Command::new(&openclaw_path); c.args(["gateway", "--port", "18789"]); c diff --git a/src/components/Agents/index.tsx b/src/components/Agents/index.tsx index 2c4ac45..c6ba57a 100644 --- a/src/components/Agents/index.tsx +++ b/src/components/Agents/index.tsx @@ -742,27 +742,7 @@ export function Agents() { placeholder="e.g. coder" /> -
- { - const isDefault = e.target.checked; - const id = agentForm.id || 'agent'; - const base = openclawHomeDir ? openclawHomeDir.replace(/\\/g, '/') : ''; - setAgentForm({ - ...agentForm, - default: isDefault || null, - workspace: base ? (isDefault ? `${base}/workspace` : `${base}/workspace-${id}`) : agentForm.workspace, - agent_dir: base ? `${base}/agents/${id}/agent` : agentForm.agent_dir, - }); - }} - className="w-4 h-4 rounded bg-dark-600 border-dark-500 text-claw-500 focus:ring-claw-500/50" - /> - - (uses main workspace) -
+ {/* Default Agent checkbox removed - Main agent is always default */}\n
-

Default: {openclawHomeDir ? (agentForm.default ? `${openclawHomeDir.replace(/\\/g, '/')}/workspace` : `${openclawHomeDir.replace(/\\/g, '/')}/workspace-${agentForm.id || '{id}'}`) : '~/.openclaw/workspace-{id}'}

+

Default: {openclawHomeDir ? `${openclawHomeDir.replace(/\\/g, '/')}/workspace-${agentForm.id || '{id}'}` : '~/.openclaw/workspace-{id}'}

@@ -1038,19 +1018,9 @@ export function Agents() { className="input-base" placeholder="e.g. coder" /> -

Workspace: {wizardForm.isDefault ? 'workspace/' : `workspace-${wizardForm.agentId || '{id}'}/`}

-
-
- setWizardForm({ ...wizardForm, isDefault: e.target.checked })} - className="w-4 h-4 rounded bg-dark-600 border-dark-500 text-claw-500 focus:ring-claw-500/50" - /> - - (uses main workspace) +

Workspace: {`workspace-${wizardForm.agentId || '{id}'}/`}

+ {/* Default Agent checkbox removed */}\n