using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; using Nexus.Api.Models; namespace Nexus.Api.Services; public sealed class OpenClawGatewayClient(HttpClient httpClient, IConfiguration configuration) { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; private string? GetPassword() { var password = configuration["Integrations:OpenClaw:Password"]; if (string.IsNullOrWhiteSpace(password)) password = configuration["Integrations:OpenClaw:Token"]; return string.IsNullOrWhiteSpace(password) ? null : password; } private void ApplyAuth(HttpRequestMessage request) { var password = GetPassword(); if (password is not null) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", password); } public async Task InvokeToolAsync(string tool, object? args = null) { try { using var request = new HttpRequestMessage(HttpMethod.Post, "/tools/invoke"); ApplyAuth(request); var body = new Dictionary { ["tool"] = tool }; if (args is not null) body["args"] = args; request.Content = JsonContent.Create(body); using var response = await httpClient.SendAsync(request); if (!response.IsSuccessStatusCode) return null; var json = await response.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(json)) return null; return JsonNode.Parse(json); } catch { return null; } } public async Task> GetAgentsAsync() { var agents = new List(); // Get sessions list (active agents/sessions) var sessionsResult = await InvokeToolAsync("sessions_list"); if (sessionsResult is not null) { var sessions = sessionsResult.AsArray(); if (sessions is not null) { foreach (var session in sessions) { if (session is null) continue; var id = session["sessionKey"]?.GetValue() ?? session["id"]?.GetValue() ?? ""; var name = session["name"]?.GetValue() ?? id; var model = session["model"]?.GetValue() ?? ""; var status = session["status"]?.GetValue() ?? ""; var isActive = status.Equals("active", StringComparison.OrdinalIgnoreCase); agents.Add(new DashboardAgentInfo( Id: id, Name: name, Role: DeriveRole(id), Model: model, IsActive: isActive, CurrentTask: session["currentTask"]?.GetValue() )); } } } // Also get subagents list var subagentsResult = await InvokeToolAsync("sub_agents_list"); if (subagentsResult is not null && subagentsResult is JsonArray subArray) { foreach (var sub in subArray) { if (sub is null) continue; var id = sub["id"]?.GetValue() ?? ""; if (agents.Any(a => a.Id == id)) continue; var name = sub["name"]?.GetValue() ?? id; var model = sub["model"]?.GetValue() ?? ""; var status = sub["status"]?.GetValue() ?? ""; var isActive = status.Equals("active", StringComparison.OrdinalIgnoreCase) || status.Equals("running", StringComparison.OrdinalIgnoreCase); agents.Add(new DashboardAgentInfo( Id: id, Name: name, Role: DeriveRole(id), Model: model, IsActive: isActive, CurrentTask: sub["currentTask"]?.GetValue() )); } } return agents.Count > 0 ? agents : new List(); } public async Task> GetSessionHistoryAsync(string sessionKey, int limit = 50, int offset = 0) { try { var result = await InvokeToolAsync("sessions_history", new { sessionKey, limit, offset }); if (result is null) return new List(); var messages = new List(); var array = result as JsonArray ?? result.AsArray(); if (array is null) return messages; foreach (var msg in array) { if (msg is null) continue; var role = msg["role"]?.GetValue() ?? ""; var content = msg["content"]?.GetValue() ?? ""; var timestamp = msg["timestamp"]?.GetValue() ?? msg["ts"]?.GetValue() ?? msg["createdAt"]?.GetValue() ?? DateTimeOffset.UtcNow.ToString("o"); messages.Add(new MessageEntry(role, content, timestamp)); } return messages; } catch { return new List(); } } public async Task SendChatMessageAsync(string sessionKey, string message) { try { var result = await InvokeToolAsync("sessions_send", new { sessionKey, message }); if (result is null) return new ChatResponse(false, null, "Gateway nicht erreichbar"); var ok = result["ok"]?.GetValue() ?? result["success"]?.GetValue() ?? false; var reply = result["reply"]?.GetValue() ?? result["response"]?.GetValue() ?? result["content"]?.GetValue(); var error = result["error"]?.GetValue(); return new ChatResponse(ok, reply, error); } catch (Exception ex) { return new ChatResponse(false, null, $"Fehler: {ex.Message}"); } } public async Task> GetQueueAsync() { try { var result = await InvokeToolAsync("cron_list"); if (result is null) return new List(); var items = new List(); var array = result as JsonArray ?? result.AsArray(); if (array is null) return items; foreach (var entry in array) { if (entry is null) continue; var id = entry["id"]?.GetValue() ?? ""; var name = entry["name"]?.GetValue() ?? id; var status = entry["status"]?.GetValue() ?? "unknown"; items.Add(new QueueItem(id, name, status)); } return items; } catch { return new List(); } } public async Task GetStatusAsync() { var gatewayOk = false; var irisStatus = "Offline"; var activeAgents = 0; var pendingTasks = 0; try { // Check gateway health using var pingRequest = new HttpRequestMessage(HttpMethod.Get, "/"); ApplyAuth(pingRequest); using var pingResponse = await httpClient.SendAsync(pingRequest); gatewayOk = pingResponse.IsSuccessStatusCode; if (gatewayOk) { // Get session status var sessionResult = await InvokeToolAsync("session_status"); if (sessionResult is not null) { irisStatus = sessionResult["status"]?.GetValue() ?? "Active"; } // Get agents for active count var agents = await GetAgentsAsync(); activeAgents = agents.Count(a => a.IsActive); // Get queue/cron for pending tasks var queue = await GetQueueAsync(); pendingTasks = queue.Count; } } catch { gatewayOk = false; } return new DashboardStatus(gatewayOk, irisStatus, activeAgents, pendingTasks); } private static string DeriveRole(string agentId) => agentId.ToLowerInvariant() switch { "iris" => "Orchestrator", "programmer" => "Developer", "reviewer" => "Reviewer", "architekt" => "Architect", "main" => "Assistant", _ => "Custom" }; }