From b41e0dfbf9a11f16e28783aa127bb848acaa863f Mon Sep 17 00:00:00 2001 From: jordan Date: Sat, 7 Feb 2026 16:41:21 -0700 Subject: [PATCH] fix: use raw JSON responses in claudebox server The claudebox sidecar was using api.WriteJSON which wraps responses in {data: ..., meta: ...} format. The claudebox HTTP client expects raw JSON responses without wrapping. This caused git clone to appear to fail - the HTTP request succeeded and returned {data: {success: true, cloned: true}, meta: {...}}, but the client decoded success=false because it couldn't find the fields at the top level. Added writeRawJSON helper and replaced all api.WriteJSON calls with it for actual responses. Error responses still use api.WriteBadRequest which returns proper error format. Co-Authored-By: Claude Opus 4.5 --- internal/claudebox/server.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/internal/claudebox/server.go b/internal/claudebox/server.go index 2a6d56e..fda8440 100644 --- a/internal/claudebox/server.go +++ b/internal/claudebox/server.go @@ -14,6 +14,14 @@ import ( "github.com/orchard9/rdev/pkg/api" ) +// writeRawJSON writes a raw JSON response without the rdev-api wrapper. +// The claudebox sidecar uses direct JSON responses, not the {data, meta} format. +func writeRawJSON(w http.ResponseWriter, status int, data any) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(data) +} + // Server handles HTTP requests for claudebox operations. type Server struct { executor *Executor @@ -65,7 +73,7 @@ func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { Timestamp: time.Now().UTC().Format(time.RFC3339), WorkDir: s.executor.workDir, } - api.WriteJSON(w, r, http.StatusOK, resp) + writeRawJSON(w, http.StatusOK, resp) } // ExecuteRequest is the request to execute Claude Code. @@ -120,7 +128,7 @@ func (s *Server) handleExecute(w http.ResponseWriter, r *http.Request) { resp.Error = result.Error.Error() } - api.WriteJSON(w, r, http.StatusOK, resp) + writeRawJSON(w, http.StatusOK, resp) } // StreamEvent is an SSE event for streaming execution. @@ -234,7 +242,7 @@ func (s *Server) handleGitClone(w http.ResponseWriter, r *http.Request) { resp.Error = result.Error.Error() } - api.WriteJSON(w, r, http.StatusOK, resp) + writeRawJSON(w, http.StatusOK, resp) } // GitCommitAndPushRequest is the request to commit and push changes. @@ -286,7 +294,7 @@ func (s *Server) handleGitCommitAndPush(w http.ResponseWriter, r *http.Request) resp.Error = result.Error.Error() } - api.WriteJSON(w, r, http.StatusOK, resp) + writeRawJSON(w, http.StatusOK, resp) } // GitStatusResponse is the response from git status. @@ -309,14 +317,14 @@ func (s *Server) handleGitStatus(w http.ResponseWriter, r *http.Request) { status, err := s.gitOps.Status(ctx, workDir) if err != nil { - api.WriteJSON(w, r, http.StatusOK, GitStatusResponse{ + writeRawJSON(w, http.StatusOK, GitStatusResponse{ IsRepo: false, Error: err.Error(), }) return } - api.WriteJSON(w, r, http.StatusOK, status) + writeRawJSON(w, http.StatusOK, status) } // SDLCRequest is the request to run an SDLC command. @@ -364,5 +372,5 @@ func (s *Server) handleSDLC(w http.ResponseWriter, r *http.Request) { resp.Error = result.Error.Error() } - api.WriteJSON(w, r, http.StatusOK, resp) + writeRawJSON(w, http.StatusOK, resp) }