This commit is contained in:
Ian Gulliver
2026-10-20 22:49:11 -07:00
parent 4f7d05021a
commit 99401670ee
7 changed files with 53 additions and 40 deletions
+2 -6
View File
@@ -8,10 +8,10 @@ import (
"os"
"os/exec"
"strings"
"syscall"
"time"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/procgroup"
)
const bashKeep = 64 << 10
@@ -68,13 +68,9 @@ func (m *Manager) bash(ctx context.Context, l *live, raw json.RawMessage) (strin
cctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(cctx, shell, "-l", "-c", args.Command)
cmd := procgroup.CommandContext(cctx, shell, "-l", "-c", args.Command)
cmd.Dir = l.dir
cmd.Env = append(os.Environ(), portEnv(task)...)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
cmd.WaitDelay = 5 * time.Second
out := &tailWriter{}
cmd.Stdout = out
+2 -7
View File
@@ -8,13 +8,12 @@ import (
"log/slog"
"maps"
"os"
"os/exec"
"slices"
"strings"
"syscall"
"time"
"g.fc.run/ai/byre/internal/api"
"g.fc.run/ai/byre/internal/procgroup"
)
const usageTimeout = 10 * time.Second
@@ -127,11 +126,7 @@ func runUsageCommand(cmdline string) (api.Usage, error) {
}
ctx, cancel := context.WithTimeout(context.Background(), usageTimeout)
defer cancel()
cmd := exec.CommandContext(ctx, shell, "-l", "-c", cmdline)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
cmd := procgroup.CommandContext(ctx, shell, "-l", "-c", cmdline)
cmd.WaitDelay = time.Second
out, err := cmd.Output()
if err != nil {
+5 -6
View File
@@ -13,7 +13,6 @@ import (
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
@@ -22,6 +21,7 @@ import (
"time"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/procgroup"
)
const maxRuleBytes = 24000
@@ -115,7 +115,7 @@ func (e Event) Result() (Result, error) {
}
type proc struct {
cmd *exec.Cmd
cmd *procgroup.Cmd
stdin io.WriteCloser
done chan struct{}
expected bool
@@ -324,7 +324,7 @@ func (s *Session) spawnLocked() error {
}
args = append(args, "--print", "")
cmd := exec.Command("agy", args...)
cmd := procgroup.Command("agy", args...)
cmd.Dir = s.opts.Cwd
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(),
@@ -334,7 +334,6 @@ func (s *Session) spawnLocked() error {
"GIT_COMMITTER_EMAIL="+s.opts.GitEmail,
)
cmd.Env = append(cmd.Env, s.opts.Env...)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
stdin, err := cmd.StdinPipe()
if err != nil {
@@ -429,7 +428,7 @@ func (s *Session) restartLocked() error {
select {
case <-p.done:
case <-time.After(10 * time.Second):
syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL)
p.cmd.Kill()
<-p.done
}
return s.spawnLocked()
@@ -568,7 +567,7 @@ func (s *Session) Kill() {
s.mu.Unlock()
if p != nil {
syscall.Kill(-p.cmd.Process.Pid, syscall.SIGKILL)
p.cmd.Kill()
<-p.done
}
s.die()
+5 -7
View File
@@ -13,13 +13,12 @@ import (
"io"
"log/slog"
"os"
"os/exec"
"sync"
"sync/atomic"
"syscall"
"time"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/procgroup"
)
type HookFunc func(ctx context.Context, input json.RawMessage) (map[string]any, error)
@@ -194,7 +193,7 @@ func (e Event) Result() (Result, error) {
}
type Session struct {
cmd *exec.Cmd
cmd *procgroup.Cmd
stdin io.WriteCloser
events chan Event
done chan struct{}
@@ -255,11 +254,10 @@ func Start(ctx context.Context, opts Options) (*Session, error) {
return nil, err
}
s.cmd = exec.Command("claude", args...)
s.cmd = procgroup.Command("claude", args...)
s.cmd.Dir = opts.Cwd
s.cmd.Stderr = os.Stderr
s.cmd.Env = buildEnv(opts)
s.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
s.stdin, err = s.cmd.StdinPipe()
if err != nil {
@@ -514,7 +512,7 @@ func (s *Session) Close() error {
select {
case <-s.done:
case <-time.After(5 * time.Second):
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
s.cmd.Kill()
<-s.done
}
@@ -522,7 +520,7 @@ func (s *Session) Close() error {
}
func (s *Session) Kill() {
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
s.cmd.Kill()
<-s.done
s.cmd.Wait()
}
+6 -8
View File
@@ -11,15 +11,14 @@ import (
"io"
"log/slog"
"os"
"os/exec"
"regexp"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/procgroup"
)
type Options struct {
@@ -90,7 +89,7 @@ type rpcResult struct {
}
type Session struct {
cmd *exec.Cmd
cmd *procgroup.Cmd
stdin *os.File
events chan Event
done chan struct{}
@@ -156,11 +155,10 @@ func ReadRateLimits(ctx context.Context, cwd string, env []string) (RateLimitSna
}
func spawn(opts Options) (*Session, error) {
cmd := exec.Command("codex", "app-server", "-c", "check_for_update_on_startup=false")
cmd := procgroup.Command("codex", "app-server", "-c", "check_for_update_on_startup=false")
cmd.Dir = opts.Cwd
cmd.Stderr = os.Stderr
cmd.Env = buildEnv(opts)
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
stdinR, stdin, err := os.Pipe()
if err != nil {
@@ -538,7 +536,7 @@ func (s *Session) Close() error {
select {
case <-s.done:
case <-time.After(5 * time.Second):
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
s.cmd.Kill()
<-s.done
}
@@ -546,7 +544,7 @@ func (s *Session) Close() error {
}
func (s *Session) Kill() {
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
s.cmd.Kill()
<-s.done
s.cmd.Wait()
}
@@ -579,7 +577,7 @@ func (s *Session) write(m message, deadline time.Time) error {
n, err := s.stdin.Write(append(b, '\n'))
if n > 0 && errors.Is(err, os.ErrDeadlineExceeded) {
slog.Error("codex stopped reading its stdin mid-line; killing it", "pid", s.cmd.Process.Pid, "written", n, "len", len(b)+1)
syscall.Kill(-s.cmd.Process.Pid, syscall.SIGKILL)
s.cmd.Kill()
}
return err
}
+3 -6
View File
@@ -11,8 +11,9 @@ import (
"slices"
"strconv"
"strings"
"syscall"
"time"
"g.fc.run/ai/byre/internal/procgroup"
)
type RepoInfo struct {
@@ -435,14 +436,10 @@ func runNetwork(ctx context.Context, dir string, args ...string) (string, string
}
func runCodeInput(ctx context.Context, dir string, env []string, stdin io.Reader, args ...string) (string, string, int, error) {
cmd := exec.CommandContext(ctx, "git", args...)
cmd := procgroup.CommandContext(ctx, "git", args...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), env...)
cmd.Stdin = stdin
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Cancel = func() error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
cmd.WaitDelay = 5 * time.Second
stderr := &strings.Builder{}
cmd.Stderr = stderr
+30
View File
@@ -0,0 +1,30 @@
package procgroup
import (
"context"
"os/exec"
"syscall"
)
type Cmd struct {
*exec.Cmd
}
func Command(name string, args ...string) *Cmd {
return group(exec.Command(name, args...))
}
func CommandContext(ctx context.Context, name string, args ...string) *Cmd {
cmd := group(exec.CommandContext(ctx, name, args...))
cmd.Cancel = cmd.Kill
return cmd
}
func group(cmd *exec.Cmd) *Cmd {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
return &Cmd{Cmd: cmd}
}
func (cmd *Cmd) Kill() error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}