From cf83444a9d341ae362e65a9f995100c69176887c Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Thu, 5 Mar 2026 18:51:50 +0000 Subject: Rescue work from claudomator-work: question/answer, ratelimit, start-next-task Merges features developed in /site/doot.terst.org/claudomator-work (a stale clone) into the canonical repo: - executor: QuestionRegistry for human-in-the-loop answers, rate limit detection and exponential backoff retry (ratelimit.go, question.go) - executor/claude.go: process group isolation (SIGKILL orphans on cancel), os.Pipe for reliable stdout drain, backoff retry on rate limits - api/scripts.go: POST /api/scripts/start-next-task handler - api/server.go: startNextTaskScript field, answer-question route, BroadcastQuestion for WebSocket question events - web: Cancel/Restart buttons, question banner UI, log viewer, validate section, WebSocket auto-connect All tests pass. Co-Authored-By: Claude Sonnet 4.6 --- internal/api/scripts.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 internal/api/scripts.go (limited to 'internal/api/scripts.go') diff --git a/internal/api/scripts.go b/internal/api/scripts.go new file mode 100644 index 0000000..492570b --- /dev/null +++ b/internal/api/scripts.go @@ -0,0 +1,50 @@ +package api + +import ( + "bytes" + "context" + "net/http" + "os/exec" + "path/filepath" + "time" +) + +const scriptTimeout = 30 * time.Second + +func (s *Server) startNextTaskScriptPath() string { + if s.startNextTaskScript != "" { + return s.startNextTaskScript + } + return filepath.Join(s.workDir, "scripts", "start-next-task") +} + +func (s *Server) handleStartNextTask(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), scriptTimeout) + defer cancel() + + scriptPath := s.startNextTaskScriptPath() + cmd := exec.CommandContext(ctx, scriptPath) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + exitCode := 0 + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitCode = exitErr.ExitCode() + } else { + s.logger.Error("start-next-task: script execution failed", "error", err, "path", scriptPath) + writeJSON(w, http.StatusInternalServerError, map[string]string{ + "error": "script execution failed: " + err.Error(), + }) + return + } + } + + writeJSON(w, http.StatusOK, map[string]interface{}{ + "output": stdout.String(), + "exit_code": exitCode, + }) +} -- cgit v1.2.3