using Nexus.Api.Models; namespace Nexus.Api.Services; internal static class OpenClawContentReadHelpers { public const int MaxFiles = 50; public const int MaxContentBytes = 1_000_000; private const int MaxConcurrency = 4; public static async Task> SelectBoundedAsync( IEnumerable source, Func> selector, CancellationToken cancellationToken) where TResult : class { var items = source.Take(MaxFiles).ToArray(); var results = new TResult?[items.Length]; await Parallel.ForEachAsync( Enumerable.Range(0, items.Length), new ParallelOptions { CancellationToken = cancellationToken, MaxDegreeOfParallelism = MaxConcurrency }, async (index, token) => { results[index] = await selector(items[index], token); }); return results.Where(item => item is not null).Select(item => item!).ToArray(); } public static string? ReadText(OpenClawWorkspaceFileDto file) { if (file.Size > MaxContentBytes || !string.Equals(file.Encoding, "utf8", StringComparison.OrdinalIgnoreCase) || System.Text.Encoding.UTF8.GetByteCount(file.Content) > MaxContentBytes) { return null; } return file.Content; } public static bool IsMarkdownFile(OpenClawWorkspaceEntryDto entry) => string.Equals(entry.Kind, "file", StringComparison.Ordinal) && entry.Path.EndsWith(".md", StringComparison.OrdinalIgnoreCase) && (entry.Size is null || entry.Size <= MaxContentBytes); public static bool IsNotFound(OpenClawGatewayRpcException exception) => exception.Code.Equals("NOT_FOUND", StringComparison.OrdinalIgnoreCase) || exception.Code.Equals( "FILE_NOT_FOUND", StringComparison.OrdinalIgnoreCase) || exception.Code.Equals( "PATH_NOT_FOUND", StringComparison.OrdinalIgnoreCase); public static bool IsSafeFileName(string? value) => !string.IsNullOrWhiteSpace(value) && value.Length <= 240 && value is not "." and not ".." && !value.StartsWith('.') && !value.Contains('/') && !value.Contains('\\') && !value.Contains('\0') && !value.Any(char.IsControl); public static string LegacyPath(string workspacePath, string prefix) => workspacePath.StartsWith( prefix + "/", StringComparison.OrdinalIgnoreCase) ? workspacePath[(prefix.Length + 1)..] : workspacePath; }