feat: внедрение RazorConsole TUI с runtime-управлением MCP-инструментами

- Добавлен RazorConsole.Core для интерактивного TUI-дашборда
- ToolRegistryService: живое включение/отключение модулей и отдельных методов
- InMemoryLogSink: кольцевой буфер логов с фильтрацией по модулю
- TUI: 3 таба (Overview, Logs, Settings)
- IToolModule: generic-интерфейс для легкого добавления новых MCP-модулей
- Guard-проверка TryCheckEnabled() во всех существующих инструментах
This commit is contained in:
2026-04-13 17:31:28 +03:00
parent c117d928b0
commit 879becadfe
24 changed files with 826 additions and 11 deletions

View File

@@ -0,0 +1,81 @@
@using LazyBear.MCP.Services.Logging
@using LazyBear.MCP.Services.ToolRegistry
@inject ToolRegistryService Registry
@inject InMemoryLogSink LogSink
@implements IDisposable
<Rows>
<Panel Title="LazyBear MCP" BorderColor="@Spectre.Console.Color.Gold1" Expand="true">
<Rows>
@* Таб-навигация *@
<Columns>
<TextButton Content="[1] Overview"
OnClick="@(() => SetTab(Tab.Overview))"
BackgroundColor="@(_activeTab == Tab.Overview ? Spectre.Console.Color.DarkBlue : Spectre.Console.Color.Grey23)"
FocusedColor="@Spectre.Console.Color.Blue"
FocusOrder="1" />
<TextButton Content="[2] Logs"
OnClick="@(() => SetTab(Tab.Logs))"
BackgroundColor="@(_activeTab == Tab.Logs ? Spectre.Console.Color.DarkBlue : Spectre.Console.Color.Grey23)"
FocusedColor="@Spectre.Console.Color.Blue"
FocusOrder="2" />
<TextButton Content="[3] Settings"
OnClick="@(() => SetTab(Tab.Settings))"
BackgroundColor="@(_activeTab == Tab.Settings ? Spectre.Console.Color.DarkBlue : Spectre.Console.Color.Grey23)"
FocusedColor="@Spectre.Console.Color.Blue"
FocusOrder="3" />
</Columns>
@* Контент таба *@
@if (_activeTab == Tab.Overview)
{
<OverviewTab />
}
else if (_activeTab == Tab.Logs)
{
<LogsTab />
}
else
{
<SettingsTab />
}
</Rows>
</Panel>
</Rows>
@code {
private enum Tab { Overview, Logs, Settings }
private Tab _activeTab = Tab.Overview;
protected override void OnInitialized()
{
Registry.StateChanged += OnStateChanged;
LogSink.OnLog += OnNewLog;
}
private void SetTab(Tab tab)
{
_activeTab = tab;
StateHasChanged();
}
private void OnStateChanged()
{
InvokeAsync(StateHasChanged);
}
private void OnNewLog(LogEntry _)
{
if (_activeTab == Tab.Logs)
{
InvokeAsync(StateHasChanged);
}
}
public void Dispose()
{
Registry.StateChanged -= OnStateChanged;
LogSink.OnLog -= OnNewLog;
}
}