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
+88 -37
View File
@@ -1,58 +1,107 @@
using Nexus.Api.Helpers;
using System.Text.RegularExpressions;
using Nexus.Api.Models;
namespace Nexus.Api.Services;
public sealed partial class IncidentService : IIncidentService
public sealed partial class IncidentService(
IOpenClawAgentConfigurationService configuration) : IIncidentService
{
private const string BasePath = "/mnt/workspace-iris/memory/incidents";
private const string IncidentDirectory = "memory/incidents";
public async Task<IReadOnlyList<IncidentSummary>> GetAllAsync()
public async Task<IReadOnlyList<IncidentSummary>> GetAllAsync(
string agentId,
CancellationToken cancellationToken = default)
{
if (!Directory.Exists(BasePath))
return Array.Empty<IncidentSummary>();
var incidents = new List<IncidentSummary>();
foreach (var file in Directory.GetFiles(BasePath, "*.md").OrderByDescending(f => f).Take(50))
var normalizedAgentId = NormalizeAgentId(agentId);
OpenClawWorkspaceCollectionDto listing;
try
{
var fi = new FileInfo(file);
if (fi.Length > 1_000_000) continue;
var name = Path.GetFileNameWithoutExtension(file);
var content = await File.ReadAllTextAsync(file);
var title = ExtractTitle(name, content);
var date = ExtractDate(name);
var severity = ExtractSeverity(content);
var excerpt = ExtractExcerpt(content);
incidents.Add(new IncidentSummary(Path.GetFileName(file), title, date, severity, excerpt, fi.Length));
listing = await configuration.GetWorkspaceAsync(
normalizedAgentId,
IncidentDirectory,
0,
OpenClawContentReadHelpers.MaxFiles,
cancellationToken);
}
catch (OpenClawGatewayRpcException exception)
when (OpenClawContentReadHelpers.IsNotFound(exception))
{
return [];
}
return incidents;
var entries = listing.Entries
.Where(OpenClawContentReadHelpers.IsMarkdownFile)
.OrderByDescending(entry => entry.Name, StringComparer.OrdinalIgnoreCase);
return await OpenClawContentReadHelpers.SelectBoundedAsync<
OpenClawWorkspaceEntryDto,
IncidentSummary>(
entries,
async (entry, token) =>
{
var file = await configuration.GetWorkspaceFileAsync(
normalizedAgentId,
entry.Path,
token);
var content = OpenClawContentReadHelpers.ReadText(file);
if (content is null)
return null;
var baseName = Path.GetFileNameWithoutExtension(file.Name);
return new IncidentSummary(
file.Name,
ExtractTitle(baseName, content),
ExtractDate(file.Name),
ExtractSeverity(content),
ExtractExcerpt(content),
file.Size,
normalizedAgentId,
file.Path);
},
cancellationToken);
}
public async Task<IncidentDetail?> GetByNameAsync(string name)
public async Task<IncidentDetail?> GetByNameAsync(
string name,
string agentId,
CancellationToken cancellationToken = default)
{
if (!PathSecurityHelper.TryResolveSafePath(BasePath, name, out var filePath))
if (!OpenClawContentReadHelpers.IsSafeFileName(name))
return null;
if (!File.Exists(filePath!))
var normalizedAgentId = NormalizeAgentId(agentId);
var fileName = name.EndsWith(".md", StringComparison.OrdinalIgnoreCase)
? name
: name + ".md";
try
{
if (!name.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
filePath = Path.Combine(BasePath, name + ".md");
if (!File.Exists(filePath!))
var file = await configuration.GetWorkspaceFileAsync(
normalizedAgentId,
$"{IncidentDirectory}/{fileName}",
cancellationToken);
var content = OpenClawContentReadHelpers.ReadText(file);
if (content is null)
return null;
return new IncidentDetail(
file.Name,
ExtractTitle(
Path.GetFileNameWithoutExtension(file.Name),
content),
ExtractDate(file.Name),
content,
file.Size,
normalizedAgentId,
file.Path);
}
catch (OpenClawGatewayRpcException exception)
when (OpenClawContentReadHelpers.IsNotFound(exception))
{
return null;
}
var content = await File.ReadAllTextAsync(filePath!);
var fi = new FileInfo(filePath!);
var fileName = Path.GetFileName(filePath!);
var title = ExtractTitle(Path.GetFileNameWithoutExtension(filePath!), content);
var date = ExtractDate(fileName);
return new IncidentDetail(fileName, title, date, content, fi.Length);
}
private static string NormalizeAgentId(string? agentId)
=> string.IsNullOrWhiteSpace(agentId)
? "iris"
: agentId.Trim().ToLowerInvariant();
private static string ExtractTitle(string name, string content)
{
var match = TitleRegex().Match(content);
@@ -74,7 +123,9 @@ public sealed partial class IncidentService : IIncidentService
private static string ExtractExcerpt(string content)
{
var excerptEnd = content.IndexOf("\n## ", StringComparison.Ordinal);
var excerpt = excerptEnd > 0 ? content[..excerptEnd].Trim() : content[..Math.Min(300, content.Length)].Trim();
var excerpt = excerptEnd > 0
? content[..excerptEnd].Trim()
: content[..Math.Min(300, content.Length)].Trim();
return excerpt.Length > 200 ? excerpt[..200] + "…" : excerpt;
}