diff options
Diffstat (limited to 'internal/executor/gemini.go')
| -rw-r--r-- | internal/executor/gemini.go | 61 |
1 files changed, 45 insertions, 16 deletions
diff --git a/internal/executor/gemini.go b/internal/executor/gemini.go index 7f2f54f..04382ae 100644 --- a/internal/executor/gemini.go +++ b/internal/executor/gemini.go @@ -7,9 +7,11 @@ import ( "io" "log/slog" "os" + "os/exec" "path/filepath" "strings" "sync" + "syscall" "github.com/thepeterstone/claudomator/internal/storage" "github.com/thepeterstone/claudomator/internal/task" @@ -84,8 +86,18 @@ func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi } func (r *GeminiRunner) execOnce(ctx context.Context, args []string, workingDir, projectDir string, e *storage.Execution) error { - // Temporarily bypass external command execution to debug pipe. - // We will simulate outputting to stdoutW directly. + cmd := exec.CommandContext(ctx, r.binaryPath(), args...) + cmd.Env = append(os.Environ(), + "CLAUDOMATOR_API_URL="+r.APIURL, + "CLAUDOMATOR_TASK_ID="+e.TaskID, + "CLAUDOMATOR_PROJECT_DIR="+projectDir, + "CLAUDOMATOR_QUESTION_FILE="+filepath.Join(e.ArtifactDir, "question.json"), + "CLAUDOMATOR_SUMMARY_FILE="+filepath.Join(e.ArtifactDir, "summary.txt"), + ) + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + if workingDir != "" { + cmd.Dir = workingDir + } stdoutFile, err := os.Create(e.StdoutPath) if err != nil { @@ -103,22 +115,27 @@ func (r *GeminiRunner) execOnce(ctx context.Context, args []string, workingDir, if err != nil { return fmt.Errorf("creating stdout pipe: %w", err) } + cmd.Stdout = stdoutW + cmd.Stderr = stderrFile - // Simulate writing to stdoutW + if err := cmd.Start(); err != nil { + stdoutW.Close() + stdoutR.Close() + return fmt.Errorf("starting gemini: %w", err) + } + stdoutW.Close() + + killDone := make(chan struct{}) go func() { - defer stdoutW.Close() // Close the writer when done. - fmt.Fprintf(stdoutW, "```json\n") - fmt.Fprintf(stdoutW, "{\"type\":\"content_block_start\",\"content_block\":{\"text\":\"Hello, Gemini!\",\"type\":\"text\"}}\n") - fmt.Fprintf(stdoutW, "{\"type\":\"content_block_delta\",\"content_block\":{\"text\":\" How are you?\"}}\n") - fmt.Fprintf(stdoutW, "{\"type\":\"content_block_end\"}\n") - fmt.Fprintf(stdoutW, "{\"type\":\"message_delta\",\"message\":{\"role\":\"model\"}}\n") - fmt.Fprintf(stdoutW, "{\"type\":\"message_end\"}\n") - fmt.Fprintf(stdoutW, "```\n") + select { + case <-ctx.Done(): + syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + case <-killDone: + } }() - - var streamErr error var streamCost float64 + var streamErr error var wg sync.WaitGroup wg.Add(1) go func() { @@ -127,14 +144,26 @@ func (r *GeminiRunner) execOnce(ctx context.Context, args []string, workingDir, stdoutR.Close() }() - wg.Wait() // Wait for parseGeminiStream to finish + waitErr := cmd.Wait() + close(killDone) + wg.Wait() if streamCost > 0 { e.CostUSD = streamCost } - // Set a dummy exit code for this simulated run - e.ExitCode = 0 + if waitErr != nil { + if exitErr, ok := waitErr.(*exec.ExitError); ok { + e.ExitCode = exitErr.ExitCode() + } + if streamErr != nil { + return streamErr + } + if tail := tailFile(e.StderrPath, 20); tail != "" { + return fmt.Errorf("gemini exited with error: %w\nstderr:\n%s", waitErr, tail) + } + return fmt.Errorf("gemini exited with error: %w", waitErr) + } if streamErr != nil { return streamErr |
