feat(CUB-19): implement AgentStatus SignalR hub for real-time updates
- Add AgentStatusHub with typed IAgentStatusClient interface
- Hub at /hubs/agent-status (matches design spec)
- Fleet group + per-agent group subscription
- AgentStatusChanged and AgentTaskProgress push events
- Extension methods for server-side push via IHubContext
- Add GatewayEventBridgeService background service
- Connects to OpenClaw Gateway WebSocket (v3 protocol)
- Handles challenge → connect → hello-ok handshake
- Bridges sessions.changed, session.message, session.tool events
- Translates Gateway session status to AgentStatus enum
- Maintains in-memory fleet state for snapshot queries
- Add REST API controllers
- GET /api/agents — fleet status snapshot
- GET /api/agents/{agentId} — single agent status
- GET /api/logs/{agentId} — agent session logs (stub)
- POST /api/command/stop/{agentId} — stop agent
- POST /api/command/restart/{agentId} — restart agent
- POST /api/command/steer/{agentId} — inject message
- Add models matching TypeScript spec interfaces
- AgentStatusUpdate, TaskProgressUpdate, AgentCardData
- AgentStatus enum (active/idle/thinking/error)
- Configure CORS with credentials for SignalR WebSocket
- Configure Swagger/OpenAPI with XML doc comments
- Agent role map matching frontend AGENT_ROLES constant
This commit is contained in:
87
backend/ControlCenter/Controllers/LogsController.cs
Normal file
87
backend/ControlCenter/Controllers/LogsController.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ControlCenter.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// REST API for querying agent session logs.
|
||||
/// Provides historical message and tool call logs for a specific agent.
|
||||
///
|
||||
/// <para>API contract for Rex (Frontend):</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><c>GET /api/logs/{agentId}</c> — Returns recent logs for an agent</item>
|
||||
/// <item><c>GET /api/logs/{agentId}/tools</c> — Returns recent tool calls for an agent</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>Log data is sourced from the OpenClaw Gateway's transcript files.
|
||||
/// The Gateway's <c>logs.tail</c> RPC provides the raw data, and this
|
||||
/// controller formats it for the frontend.</para>
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class LogsController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<LogsController> _logger;
|
||||
|
||||
public LogsController(ILogger<LogsController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets recent session logs for a specific agent.
|
||||
/// Returns the last N messages from the agent's active session transcript.
|
||||
/// </summary>
|
||||
/// <param name="agentId">The agent identifier, e.g. "otto", "dex".</param>
|
||||
/// <param name="limit">Maximum number of log entries to return (default: 50, max: 200).</param>
|
||||
/// <returns>An array of log entries for the agent.</returns>
|
||||
/// <response code="200">Returns the agent's recent logs.</response>
|
||||
/// <response code="404">No active session found for the agent.</response>
|
||||
[HttpGet("{agentId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult GetLogs(string agentId, [FromQuery] int limit = 50)
|
||||
{
|
||||
limit = Math.Clamp(limit, 1, 200);
|
||||
|
||||
_logger.LogDebug("Logs requested for agent {AgentId}, limit {Limit}", agentId, limit);
|
||||
|
||||
// TODO: Implement log retrieval by calling the Gateway's logs.tail RPC
|
||||
// or reading transcript files. For now, return an empty array as the
|
||||
// bridge service will provide this data when fully integrated.
|
||||
return Ok(new
|
||||
{
|
||||
agentId,
|
||||
logs = Array.Empty<object>(),
|
||||
count = 0,
|
||||
hasMore = false
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets recent tool call logs for a specific agent.
|
||||
/// Returns the last N tool invocations from the agent's session.
|
||||
/// </summary>
|
||||
/// <param name="agentId">The agent identifier.</param>
|
||||
/// <param name="limit">Maximum number of tool entries to return (default: 20, max: 100).</param>
|
||||
/// <returns>An array of tool call entries for the agent.</returns>
|
||||
/// <response code="200">Returns the agent's recent tool calls.</response>
|
||||
/// <response code="404">No active session found for the agent.</response>
|
||||
[HttpGet("{agentId}/tools")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult GetToolLogs(string agentId, [FromQuery] int limit = 20)
|
||||
{
|
||||
limit = Math.Clamp(limit, 1, 100);
|
||||
|
||||
_logger.LogDebug("Tool logs requested for agent {AgentId}, limit {Limit}", agentId, limit);
|
||||
|
||||
// TODO: Implement tool log retrieval. Return empty for now.
|
||||
return Ok(new
|
||||
{
|
||||
agentId,
|
||||
tools = Array.Empty<object>(),
|
||||
count = 0,
|
||||
hasMore = false
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user