using Microsoft.AspNetCore.Mvc;
namespace ControlCenter.Controllers;
///
/// REST API for sending control commands to agents.
/// Provides the Command Hub's action endpoints for agent lifecycle control.
///
/// API contract for Rex (Frontend):
///
/// - POST /api/command/stop/{agentId} — Stop/abort an agent's active session
/// - POST /api/command/restart/{agentId} — Restart an agent
/// - POST /api/command/steer/{agentId} — Inject a message into an agent's session
///
///
/// Commands are forwarded to the OpenClaw Gateway via the
/// WebSocket bridge service. The Gateway handles the actual execution.
///
[ApiController]
[Route("api/[controller]")]
public class CommandController : ControllerBase
{
private readonly ILogger _logger;
public CommandController(ILogger logger)
{
_logger = logger;
}
///
/// Stops (aborts) an agent's active session.
/// Sends an abort command to the OpenClaw Gateway.
///
/// The agent identifier to stop.
/// Confirmation of the stop command.
/// Stop command sent successfully.
/// No active session found for the agent.
[HttpPost("stop/{agentId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult StopAgent(string agentId)
{
_logger.LogInformation("Stop command received for agent {AgentId}", agentId);
// TODO: Forward to Gateway via bridge service
// await _bridgeService.SendRpcAsync("sessions.abort", new { sessionKey = ... });
return Ok(new
{
agentId,
command = "stop",
status = "sent",
timestamp = DateTime.UtcNow.ToString("o")
});
}
///
/// Restarts an agent by aborting the current session and allowing
/// a new one to start on the next incoming message.
///
/// The agent identifier to restart.
/// Confirmation of the restart command.
/// Restart command sent successfully.
[HttpPost("restart/{agentId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult RestartAgent(string agentId)
{
_logger.LogInformation("Restart command received for agent {AgentId}", agentId);
// TODO: Forward to Gateway — abort current session + signal restart
return Ok(new
{
agentId,
command = "restart",
status = "sent",
timestamp = DateTime.UtcNow.ToString("o")
});
}
///
/// Steers (injects a message into) an agent's active session.
/// Used by operators to redirect an agent's task mid-execution.
///
/// The agent identifier to steer.
/// The steering message to inject.
/// Confirmation of the steer command.
/// Steer command sent successfully.
/// Missing or empty message.
[HttpPost("steer/{agentId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult SteerAgent(string agentId, [FromBody] SteerRequest request)
{
if (string.IsNullOrWhiteSpace(request.Message))
{
return BadRequest(new { error = "Message is required" });
}
_logger.LogInformation("Steer command received for agent {AgentId}: {Message}",
agentId, request.Message.Length > 100
? request.Message[..100] + "..." : request.Message);
// TODO: Forward to Gateway via bridge service
// await _bridgeService.SendRpcAsync("sessions.steer", new { sessionKey = ..., message = request.Message });
return Ok(new
{
agentId,
command = "steer",
message = request.Message,
status = "sent",
timestamp = DateTime.UtcNow.ToString("o")
});
}
}
///
/// Request body for the steer command.
///
/// The message to inject into the agent's session.
public record SteerRequest(string Message);