20f5f43fb3
- Use acp-go-sdk for all ACP communication in agentctl - Mark agent as completed when Prompt() returns successfully - Update task state to COMPLETED when agent completes - Update task state to FAILED when agent fails and retries exhausted - Enhanced E2E test to verify agent actually creates files - E2E test now validates full flow: task creation -> agent work -> file creation -> task completion
103 lines
2.3 KiB
Go
103 lines
2.3 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/kandev/kandev/internal/agentctl/process"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// OutputResponse is the response for getting output
|
|
type OutputResponse struct {
|
|
Lines []process.OutputLine `json:"lines"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
func (s *Server) handleGetOutput(c *gin.Context) {
|
|
// Parse optional 'last' query param
|
|
lastStr := c.DefaultQuery("last", "100")
|
|
last, err := strconv.Atoi(lastStr)
|
|
if err != nil || last < 1 {
|
|
last = 100
|
|
}
|
|
|
|
lines := s.procMgr.GetOutputBuffer().GetLast(last)
|
|
c.JSON(http.StatusOK, OutputResponse{
|
|
Lines: lines,
|
|
Total: s.procMgr.GetOutputBuffer().Count(),
|
|
})
|
|
}
|
|
|
|
// handleOutputStreamWS streams output via WebSocket
|
|
func (s *Server) handleOutputStreamWS(c *gin.Context) {
|
|
conn, err := s.upgrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
s.logger.Error("WebSocket upgrade failed", zap.Error(err))
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
s.logger.Info("Output stream WebSocket connected")
|
|
|
|
// Check if client wants history first
|
|
sendHistory := c.DefaultQuery("history", "false") == "true"
|
|
historyCount, _ := strconv.Atoi(c.DefaultQuery("history_count", "100"))
|
|
|
|
if sendHistory {
|
|
lines := s.procMgr.GetOutputBuffer().GetLast(historyCount)
|
|
for _, line := range lines {
|
|
data, err := json.Marshal(line)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
|
|
s.logger.Debug("WebSocket write error (history)", zap.Error(err))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Subscribe to real-time output
|
|
sub := s.procMgr.GetOutputBuffer().Subscribe()
|
|
defer s.procMgr.GetOutputBuffer().Unsubscribe(sub)
|
|
|
|
// Handle WebSocket close
|
|
closeCh := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
_, _, err := conn.ReadMessage()
|
|
if err != nil {
|
|
close(closeCh)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Stream output to client
|
|
for {
|
|
select {
|
|
case line, ok := <-sub:
|
|
if !ok {
|
|
return
|
|
}
|
|
data, err := json.Marshal(line)
|
|
if err != nil {
|
|
s.logger.Error("failed to marshal output line", zap.Error(err))
|
|
continue
|
|
}
|
|
if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
|
|
s.logger.Debug("WebSocket write error", zap.Error(err))
|
|
return
|
|
}
|
|
case <-closeCh:
|
|
s.logger.Info("Output stream WebSocket closed by client")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|