fix: gateway start on windows, auth flow, and ui enhancements

This commit is contained in:
MrFadiAi
2026-02-19 06:04:50 +01:00
parent fd50316d24
commit 3ee4796a81
4 changed files with 51 additions and 55 deletions
+2
View File
@@ -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
+35 -9
View File
@@ -1939,7 +1939,8 @@ pub async fn save_telegram_account(account: TelegramAccount) -> Result<String, S
if account.primary == Some(true) {
manager_config["primaryBotAccount"] = json!(account.id);
// --- NEW LOGIC: Configure primary agent workspace ---
// --- NEW LOGIC DISABLED: Do NOT auto-create main agent or binding ---
/*
// 1. Ensure "main" agent exists pointing to ~/.openclaw/workspace
let openclaw_home = platform::get_config_dir();
// Resolve ~/.openclaw/workspace
@@ -2041,6 +2042,7 @@ pub async fn save_telegram_account(account: TelegramAccount) -> Result<String, S
}));
}
config["bindings"] = json!(bindings);
*/
// --- END NEW LOGIC ---
} else {
@@ -2623,7 +2625,13 @@ pub async fn save_agent(agent: AgentInfo) -> Result<String, String> {
// 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<String, String> {
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<String, String> {
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/<id>/agent)
// If so, we want to delete the PARENT directory (e.g. .../agents/<id>) 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/<id> (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);
}
}
+10 -12
View File
@@ -364,13 +364,11 @@ pub fn run_openclaw(args: &[&str]) -> Result<String, String> {
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
+4 -34
View File
@@ -742,27 +742,7 @@ export function Agents() {
placeholder="e.g. coder"
/>
</div>
<div className="flex items-center gap-2 pt-1">
<input
type="checkbox"
id="defaultAgent"
checked={agentForm.default || false}
onChange={e => {
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"
/>
<label htmlFor="defaultAgent" className="text-sm text-gray-300 select-none">Default Agent</label>
<span className="text-xs text-gray-500 ml-1">(uses main workspace)</span>
</div>
{/* Default Agent checkbox removed - Main agent is always default */}\n
<div>
<label className="block text-sm text-gray-400 mb-1">Workspace Path</label>
<input
@@ -772,7 +752,7 @@ export function Agents() {
className="input-base"
placeholder={openclawHomeDir ? (agentForm.default ? `${openclawHomeDir.replace(/\\/g, '/')}/workspace` : `${openclawHomeDir.replace(/\\/g, '/')}/workspace-${agentForm.id || 'agent'}`) : '/path/to/workspace'}
/>
<p className="text-xs text-gray-500 mt-1">Default: <code className="text-gray-400">{openclawHomeDir ? (agentForm.default ? `${openclawHomeDir.replace(/\\/g, '/')}/workspace` : `${openclawHomeDir.replace(/\\/g, '/')}/workspace-${agentForm.id || '{id}'}`) : '~/.openclaw/workspace-{id}'}</code></p>
<p className="text-xs text-gray-500 mt-1">Default: <code className="text-gray-400">{openclawHomeDir ? `${openclawHomeDir.replace(/\\/g, '/')}/workspace-${agentForm.id || '{id}'}` : '~/.openclaw/workspace-{id}'}</code></p>
</div>
<div>
<label className="block text-sm text-gray-400 mb-1">Agent Directory</label>
@@ -1038,19 +1018,9 @@ export function Agents() {
className="input-base"
placeholder="e.g. coder"
/>
<p className="text-xs text-gray-500 mt-1">Workspace: <code className="text-gray-400">{wizardForm.isDefault ? 'workspace/' : `workspace-${wizardForm.agentId || '{id}'}/`}</code></p>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="wizardDefaultAgent"
checked={wizardForm.isDefault}
onChange={e => 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"
/>
<label htmlFor="wizardDefaultAgent" className="text-sm text-gray-300 select-none">Default Agent</label>
<span className="text-xs text-gray-500 ml-1">(uses main workspace)</span>
<p className="text-xs text-gray-500 mt-1">Workspace: <code className="text-gray-400">{`workspace-${wizardForm.agentId || '{id}'}/`}</code></p>
</div>
{/* Default Agent checkbox removed */}\n
<div>
<label className="block text-sm text-gray-400 mb-1">Model Override (Optional)</label>
<input