- Добавлен RazorConsole.Core для интерактивного TUI-дашборда - ToolRegistryService: живое включение/отключение модулей и отдельных методов - InMemoryLogSink: кольцевой буфер логов с фильтрацией по модулю - TUI: 3 таба (Overview, Logs, Settings) - IToolModule: generic-интерфейс для легкого добавления новых MCP-модулей - Guard-проверка TryCheckEnabled() во всех существующих инструментах
97 lines
4.0 KiB
C#
97 lines
4.0 KiB
C#
using System.Collections.Concurrent;
|
|
|
|
namespace LazyBear.MCP.Services.ToolRegistry;
|
|
|
|
/// <summary>
|
|
/// Singleton. Хранит runtime-состояние включённости модулей и отдельных инструментов.
|
|
/// Thread-safe. Уведомляет подписчиков при любом изменении.
|
|
/// </summary>
|
|
public sealed class ToolRegistryService
|
|
{
|
|
private readonly ConcurrentDictionary<string, bool> _moduleEnabled = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly ConcurrentDictionary<string, bool> _toolEnabled = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly List<IToolModule> _modules = [];
|
|
private readonly Lock _modulesLock = new();
|
|
|
|
/// <summary>Вызывается при любом изменении состояния.</summary>
|
|
public event Action? StateChanged;
|
|
|
|
// ── Регистрация ──────────────────────────────────────────────────────────
|
|
|
|
public void RegisterModule(IToolModule module)
|
|
{
|
|
lock (_modulesLock)
|
|
{
|
|
if (_modules.Any(m => string.Equals(m.ModuleName, module.ModuleName, StringComparison.OrdinalIgnoreCase)))
|
|
return;
|
|
|
|
_modules.Add(module);
|
|
}
|
|
|
|
_moduleEnabled.TryAdd(module.ModuleName, true);
|
|
|
|
foreach (var tool in module.ToolNames)
|
|
{
|
|
_toolEnabled.TryAdd(MakeKey(module.ModuleName, tool), true);
|
|
}
|
|
}
|
|
|
|
// ── Запросы состояния ────────────────────────────────────────────────────
|
|
|
|
public IReadOnlyList<IToolModule> GetModules()
|
|
{
|
|
lock (_modulesLock)
|
|
{
|
|
return _modules.ToList();
|
|
}
|
|
}
|
|
|
|
public bool IsModuleEnabled(string moduleName) =>
|
|
_moduleEnabled.GetValueOrDefault(moduleName, true);
|
|
|
|
public bool IsToolEnabled(string moduleName, string toolName) =>
|
|
IsModuleEnabled(moduleName) &&
|
|
_toolEnabled.GetValueOrDefault(MakeKey(moduleName, toolName), true);
|
|
|
|
// ── Переключение ─────────────────────────────────────────────────────────
|
|
|
|
public void SetModuleEnabled(string moduleName, bool enabled)
|
|
{
|
|
_moduleEnabled[moduleName] = enabled;
|
|
StateChanged?.Invoke();
|
|
}
|
|
|
|
public void SetToolEnabled(string moduleName, string toolName, bool enabled)
|
|
{
|
|
_toolEnabled[MakeKey(moduleName, toolName)] = enabled;
|
|
StateChanged?.Invoke();
|
|
}
|
|
|
|
public void ToggleModule(string moduleName) =>
|
|
SetModuleEnabled(moduleName, !IsModuleEnabled(moduleName));
|
|
|
|
public void ToggleTool(string moduleName, string toolName) =>
|
|
SetToolEnabled(moduleName, toolName, !IsToolEnabled(moduleName, toolName));
|
|
|
|
// ── Счётчики для Overview ─────────────────────────────────────────────────
|
|
|
|
public (int Active, int Total) GetToolCounts(string moduleName)
|
|
{
|
|
lock (_modulesLock)
|
|
{
|
|
var module = _modules.FirstOrDefault(m =>
|
|
string.Equals(m.ModuleName, moduleName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (module is null) return (0, 0);
|
|
|
|
var total = module.ToolNames.Count;
|
|
var active = module.ToolNames.Count(t => IsToolEnabled(moduleName, t));
|
|
return (active, total);
|
|
}
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
|
|
private static string MakeKey(string module, string tool) => $"{module}::{tool}";
|
|
}
|