feat: ship agent-first mission control v0.2.57
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s

This commit is contained in:
AzuTear
2026-07-31 22:39:47 +02:00
parent 3bc7622977
commit f5552218bc
535 changed files with 95242 additions and 8791 deletions
+30 -74
View File
@@ -1,86 +1,42 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Nexus.Api.DTOs;
namespace Nexus.Api.Services;
/// <summary>
/// Compatibility adapter for the existing calendar API. Its data now comes
/// exclusively from the real OpenClaw cron control plane; disconnected or
/// unsupported Gateways return an empty list instead of fabricated jobs.
/// </summary>
public sealed class CalendarService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<CalendarService> logger) : ICalendarService
IOpenClawControlService openClaw) : ICalendarService
{
public async Task<IReadOnlyList<CronJobEntry>> GetCronJobsAsync(CancellationToken ct = default)
public async Task<IReadOnlyList<CronJobEntry>> GetCronJobsAsync(
CancellationToken ct = default)
{
try
{
var client = CreateGatewayClient();
var response = await client.GetAsync("/api/cron", ct);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadFromJsonAsync<List<CronJobEntry>>(ct);
return data ?? new List<CronJobEntry>();
}
}
catch (Exception ex)
{
logger.LogDebug(ex, "Gateway cron endpoint not reachable, using fallback data");
}
return BuildFallbackCronJobs();
var response = await openClaw.GetCronJobsAsync(200, ct);
return response.Items
.Select(job => new CronJobEntry(
job.Id,
job.Name,
job.Schedule,
job.LastRunAt?.ToString("O") ?? string.Empty,
job.NextRunAt?.ToString("O") ?? string.Empty,
job.Status))
.ToArray();
}
public async Task<IReadOnlyList<UpcomingCronEntry>> GetUpcomingCronJobsAsync(CancellationToken ct = default)
public async Task<IReadOnlyList<UpcomingCronEntry>> GetUpcomingCronJobsAsync(
CancellationToken ct = default)
{
try
{
var client = CreateGatewayClient();
var response = await client.GetAsync("/api/cron/upcoming", ct);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadFromJsonAsync<List<UpcomingCronEntry>>(ct);
return data ?? new List<UpcomingCronEntry>();
}
}
catch (Exception ex)
{
logger.LogDebug(ex, "Gateway upcoming cron endpoint not reachable, using fallback data");
}
return BuildFallbackUpcomingJobs();
}
private HttpClient CreateGatewayClient()
{
var client = httpClientFactory.CreateClient("gateway");
var token = configuration["Integrations:OpenClaw:Token"];
if (!string.IsNullOrWhiteSpace(token))
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return client;
}
private static IReadOnlyList<CronJobEntry> BuildFallbackCronJobs()
{
var now = DateTimeOffset.UtcNow;
return
[
new("health-check", "Health Check", "*/5 * * * *", now.AddMinutes(-3).ToString("O"), now.AddMinutes(2).ToString("O"), "completed"),
new("memory-sync", "Memory Sync", "0 */6 * * *", now.AddHours(-2).ToString("O"), now.AddHours(4).ToString("O"), "completed"),
new("task-cleanup", "Task Cleanup", "0 3 * * *", now.AddDays(-1).ToString("O"), now.AddDays(1).AddHours(3).ToString("O"), "completed"),
new("backup", "Database Backup", "0 4 * * *", now.AddDays(-1).AddHours(-1).ToString("O"), now.AddDays(1).AddHours(4).ToString("O"), "completed"),
new("model-routing-refresh", "Model Routing Refresh", "*/30 * * * *", now.AddMinutes(-12).ToString("O"), now.AddMinutes(18).ToString("O"), "running")
];
}
private static IReadOnlyList<UpcomingCronEntry> BuildFallbackUpcomingJobs()
{
var now = DateTimeOffset.UtcNow;
return
[
new("health-check", "Health Check", now.AddMinutes(2).ToString("O"), "*/5 * * * *"),
new("model-routing-refresh", "Model Routing Refresh", now.AddMinutes(18).ToString("O"), "*/30 * * * *"),
new("memory-sync", "Memory Sync", now.AddHours(4).ToString("O"), "0 */6 * * *"),
new("task-cleanup", "Task Cleanup", now.AddDays(1).AddHours(3).ToString("O"), "0 3 * * *"),
new("backup", "Database Backup", now.AddDays(1).AddHours(4).ToString("O"), "0 4 * * *")
];
var response = await openClaw.GetCronJobsAsync(200, ct);
return response.Items
.Where(job => job.Enabled && job.NextRunAt is not null)
.OrderBy(job => job.NextRunAt)
.Select(job => new UpcomingCronEntry(
job.Id,
job.Name,
job.NextRunAt!.Value.ToString("O"),
job.Schedule))
.ToArray();
}
}