Compare commits

..

1 Commits

Author SHA1 Message Date
cubecraft-agents[bot]
38b7d13312 CUB-53: add SignalR hub registration and configuration 2026-04-26 11:34:51 +00:00
9 changed files with 162 additions and 32 deletions

21
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
## .NET
bin/
obj/
*.user
*.suo
*.cache
*.dll
*.pdb
## IDE
.vs/
.idea/
*.swp
*~
## OS
.DS_Store
Thumbs.db
## Environment
.env

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.14" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,52 @@
using Microsoft.AspNetCore.SignalR;
namespace ControlCenter.Api.Hubs;
/// <summary>
/// SignalR hub for broadcasting agent status updates to connected clients.
///
/// <para>
/// Clients connect to this hub at the <c>/hub</c> endpoint to receive
/// real-time agent state changes. A background service subscribes to
/// OpenClaw Gateway events and pushes them through this hub.
/// </para>
///
/// <para>
/// Architecture note: This hub bridges OpenClaw Gateway events to SignalR clients.
/// The full typed client interface and extension methods will be added in a
/// subsequent task (CUB-55).
/// </para>
/// </summary>
public class AgentStatusHub : Hub
{
private readonly ILogger<AgentStatusHub> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="AgentStatusHub"/> class.
/// </summary>
/// <param name="logger">Logger for diagnostic output.</param>
public AgentStatusHub(ILogger<AgentStatusHub> logger)
{
_logger = logger;
}
/// <summary>
/// Overrides <see cref="Hub.OnConnectedAsync"/> to log new connections.
/// </summary>
public override Task OnConnectedAsync()
{
_logger.LogDebug("Client connected: {ConnectionId}", Context.ConnectionId);
return base.OnConnectedAsync();
}
/// <summary>
/// Overrides <see cref="Hub.OnDisconnectedAsync"/> to log disconnections.
/// SignalR automatically removes disconnected connections from all groups.
/// </summary>
/// <param name="exception">Exception that caused the disconnection, if any.</param>
public override Task OnDisconnectedAsync(Exception? exception)
{
_logger.LogDebug("Client disconnected: {ConnectionId}", Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
}

24
backend/Program.cs Normal file
View File

@@ -0,0 +1,24 @@
using ControlCenter.Api.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddOpenApi();
// Register SignalR for real-time agent status updates
builder.Services.AddSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
// Map SignalR hub endpoint
app.MapHub<AgentStatusHub>("/hub");
app.Run();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5178",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7041;http://localhost:5178",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

9
backend/appsettings.json Normal file
View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -1,31 +0,0 @@
// ============================================================================
// Hub Page — Responsive AgentCard Grid
// Per CUB-52: 2×2 grid on ≥ 1024px (kiosk), single-column on < 1024px (mobile)
// ============================================================================
.hub-page {
display: grid;
grid-template-columns: 1fr;
gap: var(--cc-card-gap);
padding: var(--cc-section-padding);
min-height: 400px;
width: 100%;
box-sizing: border-box;
}
.hub-page__placeholder {
color: var(--cc-on-surface-variant);
font-size: 16px;
grid-column: 1 / -1;
text-align: center;
align-self: center;
}
// ---------------------------------------------------------------------------
// Kiosk / Desktop: 2×2 AgentCard grid
// ---------------------------------------------------------------------------
@media (min-width: 1024px) {
.hub-page {
grid-template-columns: repeat(2, 1fr);
}
}

View File

@@ -9,7 +9,18 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
<p class="hub-page__placeholder">Command Hub — Fleet status grid will render here</p>
</div>
`,
styleUrl: './hub-page.component.scss',
styles: [`
.hub-page {
display: flex;
align-items: center;
justify-content: center;
min-height: 400px;
}
.hub-page__placeholder {
color: var(--cc-on-surface-variant);
font-size: 16px;
}
`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HubPageComponent {}