141 lines
4.8 KiB
C#
141 lines
4.8 KiB
C#
using System.Text.RegularExpressions;
|
|
using Nexus.Api.Models;
|
|
|
|
namespace Nexus.Api.Services;
|
|
|
|
public sealed partial class IncidentService(
|
|
IOpenClawAgentConfigurationService configuration) : IIncidentService
|
|
{
|
|
private const string IncidentDirectory = "memory/incidents";
|
|
|
|
public async Task<IReadOnlyList<IncidentSummary>> GetAllAsync(
|
|
string agentId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var normalizedAgentId = NormalizeAgentId(agentId);
|
|
OpenClawWorkspaceCollectionDto listing;
|
|
try
|
|
{
|
|
listing = await configuration.GetWorkspaceAsync(
|
|
normalizedAgentId,
|
|
IncidentDirectory,
|
|
0,
|
|
OpenClawContentReadHelpers.MaxFiles,
|
|
cancellationToken);
|
|
}
|
|
catch (OpenClawGatewayRpcException exception)
|
|
when (OpenClawContentReadHelpers.IsNotFound(exception))
|
|
{
|
|
return [];
|
|
}
|
|
|
|
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,
|
|
string agentId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (!OpenClawContentReadHelpers.IsSafeFileName(name))
|
|
return null;
|
|
var normalizedAgentId = NormalizeAgentId(agentId);
|
|
var fileName = name.EndsWith(".md", StringComparison.OrdinalIgnoreCase)
|
|
? name
|
|
: name + ".md";
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
return match.Success ? match.Groups[1].Value.Trim() : name;
|
|
}
|
|
|
|
private static string? ExtractDate(string fileName)
|
|
{
|
|
var match = DateRegex().Match(fileName);
|
|
return match.Success ? match.Groups[1].Value : null;
|
|
}
|
|
|
|
private static string ExtractSeverity(string content)
|
|
{
|
|
var match = SeverityRegex().Match(content);
|
|
return match.Success ? match.Groups[1].Value.Trim() : "unknown";
|
|
}
|
|
|
|
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();
|
|
return excerpt.Length > 200 ? excerpt[..200] + "…" : excerpt;
|
|
}
|
|
|
|
[GeneratedRegex(@"^#\s+(.+)$", RegexOptions.Multiline)]
|
|
private static partial Regex TitleRegex();
|
|
|
|
[GeneratedRegex(@"^(\d{4}-\d{2}-\d{2})")]
|
|
private static partial Regex DateRegex();
|
|
|
|
[GeneratedRegex(@"\*\*Severity:\*\*\s*(.+)$", RegexOptions.Multiline)]
|
|
private static partial Regex SeverityRegex();
|
|
}
|