Simplify session lifecycle and MCP cleanup

This commit is contained in:
2026-05-14 20:51:37 +01:00
parent 27361f79c4
commit cc4bf9e904
16 changed files with 439 additions and 255 deletions

View File

@@ -8,6 +8,7 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/hjbdev/patterm/internal/projectkey"
"github.com/hjbdev/patterm/internal/trust"
@@ -179,9 +180,18 @@ func defaultPattermBin() (string, error) {
if p := os.Getenv("PATTERM_BIN"); p != "" {
return p, nil
}
return buildPattermBinary()
defaultBinOnce.Do(func() {
defaultBinPath, defaultBinErr = buildPattermBinary()
})
return defaultBinPath, defaultBinErr
}
var (
defaultBinOnce sync.Once
defaultBinPath string
defaultBinErr error
)
func buildPattermBinary() (string, error) {
root, err := repoRoot()
if err != nil {

View File

@@ -21,9 +21,16 @@ func (s *Session) DumpArtifacts(sc *Scenario, failingStep int, cause error) (*Ar
if name == "" {
name = "scenario"
}
dir := filepath.Join("internal", "harness", ".artifacts", fmt.Sprintf("%s-%d", name, time.Now().Unix()))
abs, _ := filepath.Abs(dir)
if err := os.MkdirAll(abs, 0o700); err != nil {
root, err := repoRoot()
if err != nil {
return nil, err
}
base := filepath.Join(root, "internal", "harness", ".artifacts")
if err := os.MkdirAll(base, 0o700); err != nil {
return nil, err
}
abs, err := os.MkdirTemp(base, fmt.Sprintf("%s-%d-*", name, time.Now().UnixNano()))
if err != nil {
return nil, err
}
screen, _ := s.em.ScreenText()

View File

@@ -210,19 +210,19 @@ func (s *Session) WaitForStable(timeout time.Duration) error {
}
func (s *Session) WaitForText(text string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
return pollUntil(timeout, 25*time.Millisecond, func() (bool, error) {
screen, err := s.Screen()
if err != nil {
return err
return false, err
}
if strings.Contains(screen, text) {
return nil
return true, nil
}
time.Sleep(25 * time.Millisecond)
}
screen, _ := s.Screen()
return fmt.Errorf("text %q not found before timeout; screen:\n%s", text, screen)
return false, nil
}, func() error {
screen, _ := s.Screen()
return fmt.Errorf("text %q not found before timeout; screen:\n%s", text, screen)
})
}
func (s *Session) WaitForRegex(pattern string, timeout time.Duration) error {
@@ -230,19 +230,31 @@ func (s *Session) WaitForRegex(pattern string, timeout time.Duration) error {
if err != nil {
return err
}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
return pollUntil(timeout, 25*time.Millisecond, func() (bool, error) {
screen, err := s.Screen()
if err != nil {
return err
return false, err
}
if re.MatchString(screen) {
return nil
return true, nil
}
time.Sleep(25 * time.Millisecond)
return false, nil
}, func() error {
screen, _ := s.Screen()
return fmt.Errorf("regex %q not found before timeout; screen:\n%s", pattern, screen)
})
}
func pollUntil(timeout, interval time.Duration, check func() (bool, error), timeoutErr func() error) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
ok, err := check()
if err != nil || ok {
return err
}
time.Sleep(interval)
}
screen, _ := s.Screen()
return fmt.Errorf("regex %q not found before timeout; screen:\n%s", pattern, screen)
return timeoutErr()
}
func (s *Session) MCPCall(method string, params json.RawMessage) (json.RawMessage, error) {