Files
nexus/backend/Services/OpenClawGatewayClient.cs
T
developer f707dceb98
CI - Build & Test / Backend (.NET) (push) Successful in 24s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 3s
feat: Dashboard Backend Proxy – OpenClaw Gateway Integration
- DashboardController: /api/dashboard/status, agents, operations, chat/send, chat/messages, queue
- OpenClawGatewayClient: typed HttpClient mit Gateway tools/invoke
- Dashboard DTOs: DashboardAgentInfo, ChatRequest, ChatResponse, FeedEntry, QueueItem
- Gateway auth: Bearer-Password via Integrations:OpenClaw:Password
- Gateway-Down → graceful degradation (HTTP 200, leere Daten)
- Build: 0 errors, Tests: 3/3 passed
2026-06-10 00:20:49 +02:00

270 lines
8.9 KiB
C#

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<JsonNode?> InvokeToolAsync(string tool, object? args = null)
{
try
{
using var request = new HttpRequestMessage(HttpMethod.Post, "/tools/invoke");
ApplyAuth(request);
var body = new Dictionary<string, object?> { ["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<List<DashboardAgentInfo>> GetAgentsAsync()
{
var agents = new List<DashboardAgentInfo>();
// 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<string>() ?? session["id"]?.GetValue<string>() ?? "";
var name = session["name"]?.GetValue<string>() ?? id;
var model = session["model"]?.GetValue<string>() ?? "";
var status = session["status"]?.GetValue<string>() ?? "";
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<string>()
));
}
}
}
// 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<string>() ?? "";
if (agents.Any(a => a.Id == id)) continue;
var name = sub["name"]?.GetValue<string>() ?? id;
var model = sub["model"]?.GetValue<string>() ?? "";
var status = sub["status"]?.GetValue<string>() ?? "";
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<string>()
));
}
}
return agents.Count > 0 ? agents : new List<DashboardAgentInfo>();
}
public async Task<List<MessageEntry>> 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<MessageEntry>();
var messages = new List<MessageEntry>();
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<string>() ?? "";
var content = msg["content"]?.GetValue<string>() ?? "";
var timestamp = msg["timestamp"]?.GetValue<string>()
?? msg["ts"]?.GetValue<string>()
?? msg["createdAt"]?.GetValue<string>()
?? DateTimeOffset.UtcNow.ToString("o");
messages.Add(new MessageEntry(role, content, timestamp));
}
return messages;
}
catch
{
return new List<MessageEntry>();
}
}
public async Task<ChatResponse> 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<bool>() ?? result["success"]?.GetValue<bool>() ?? false;
var reply = result["reply"]?.GetValue<string>()
?? result["response"]?.GetValue<string>()
?? result["content"]?.GetValue<string>();
var error = result["error"]?.GetValue<string>();
return new ChatResponse(ok, reply, error);
}
catch (Exception ex)
{
return new ChatResponse(false, null, $"Fehler: {ex.Message}");
}
}
public async Task<List<QueueItem>> GetQueueAsync()
{
try
{
var result = await InvokeToolAsync("cron_list");
if (result is null)
return new List<QueueItem>();
var items = new List<QueueItem>();
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<string>() ?? "";
var name = entry["name"]?.GetValue<string>() ?? id;
var status = entry["status"]?.GetValue<string>() ?? "unknown";
items.Add(new QueueItem(id, name, status));
}
return items;
}
catch
{
return new List<QueueItem>();
}
}
public async Task<DashboardStatus> 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<string>() ?? "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"
};
}