130 lines
4.3 KiB
C#
130 lines
4.3 KiB
C#
using Nexus.Api.Models;
|
|
|
|
namespace Nexus.Api.Services;
|
|
|
|
public sealed class DocService(
|
|
IOpenClawAgentConfigurationService configuration) : IDocService
|
|
{
|
|
private static readonly HashSet<string> AllowedExtensions =
|
|
new(
|
|
[".md", ".json", ".txt", ".yaml", ".yml", ".html", ".css"],
|
|
StringComparer.OrdinalIgnoreCase);
|
|
|
|
private static readonly (string Path, string Category)[] ScanDirectories =
|
|
[
|
|
("", "workspace"),
|
|
("nexus-phases", "phases"),
|
|
("skills", "skills"),
|
|
("nexus", "nexus"),
|
|
("nexus/phases", "nexus-phases")
|
|
];
|
|
|
|
public async Task<IReadOnlyList<DocFileInfo>> GetAllAsync(
|
|
string agentId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var normalizedAgentId = NormalizeAgentId(agentId);
|
|
var directories = await OpenClawContentReadHelpers.SelectBoundedAsync<
|
|
(string Path, string Category),
|
|
DirectoryResult>(
|
|
ScanDirectories,
|
|
async (directory, token) =>
|
|
{
|
|
try
|
|
{
|
|
var listing = await configuration.GetWorkspaceAsync(
|
|
normalizedAgentId,
|
|
directory.Path,
|
|
0,
|
|
100,
|
|
token);
|
|
return new DirectoryResult(
|
|
directory.Category,
|
|
listing);
|
|
}
|
|
catch (OpenClawGatewayRpcException exception)
|
|
when (OpenClawContentReadHelpers.IsNotFound(exception))
|
|
{
|
|
return null;
|
|
}
|
|
},
|
|
cancellationToken);
|
|
|
|
return directories
|
|
.SelectMany(directory => directory.Listing.Entries
|
|
.Where(entry =>
|
|
string.Equals(entry.Kind, "file", StringComparison.Ordinal)
|
|
&& !(string.IsNullOrEmpty(directory.Listing.Path)
|
|
&& string.Equals(
|
|
entry.Name,
|
|
"MEMORY.md",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
&& AllowedExtensions.Contains(
|
|
Path.GetExtension(entry.Name)))
|
|
.Select(entry => new DocFileInfo(
|
|
entry.Name,
|
|
entry.Path,
|
|
directory.Category,
|
|
Path.GetExtension(entry.Name).TrimStart('.')
|
|
.ToLowerInvariant(),
|
|
entry.Size ?? 0,
|
|
(entry.UpdatedAt
|
|
?? directory.Listing.CheckedAt).UtcDateTime,
|
|
normalizedAgentId,
|
|
entry.Path)))
|
|
.OrderByDescending(item => item.ModifiedAt)
|
|
.Take(100)
|
|
.ToArray();
|
|
}
|
|
|
|
public async Task<DocFileContent?> GetFileAsync(
|
|
string path,
|
|
string agentId,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)
|
|
|| !AllowedExtensions.Contains(Path.GetExtension(path)))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var normalizedAgentId = NormalizeAgentId(agentId);
|
|
try
|
|
{
|
|
var file = await configuration.GetWorkspaceFileAsync(
|
|
normalizedAgentId,
|
|
path,
|
|
cancellationToken);
|
|
var content = OpenClawContentReadHelpers.ReadText(file);
|
|
if (content is null
|
|
|| !AllowedExtensions.Contains(Path.GetExtension(file.Name)))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new DocFileContent(
|
|
file.Name,
|
|
file.Path,
|
|
content,
|
|
file.Size,
|
|
(file.UpdatedAt ?? file.CheckedAt).UtcDateTime,
|
|
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 sealed record DirectoryResult(
|
|
string Category,
|
|
OpenClawWorkspaceCollectionDto Listing);
|
|
}
|