889af65ae7
- sub_agents_list → subagents (action: list)
- cron_list → cron (action: list)
- Ping / → /health
- Unwrap {ok, result} envelope in InvokeToolAsync
274 lines
9.2 KiB
C#
274 lines
9.2 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;
|
|
|
|
var node = JsonNode.Parse(json);
|
|
// Unwrap the { ok: true, result: ... } envelope
|
|
if (node?["ok"]?.GetValue<bool>() == true && node["result"] is not null)
|
|
return node["result"];
|
|
return node;
|
|
}
|
|
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("subagents", new { action = "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", new { action = "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, "/health");
|
|
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"
|
|
};
|
|
}
|