- tmux som sanningskälla (agentoberoende, överlever omstart via adoption) - API: sessions, prompt, question/answer, keys, mode, config, shares, notify, events - frågedetektor testad mot riktiga agy 1.1.9-dumpar + mock - integrationstest: 26 tester i isolerad debian-container (scripts/run-integration.sh) - e2e-verifierad mot riktig agy (trust-fråga -> svar -> prompt) - .gitea/workflows/helmd-release.yaml -> rullande helmd-latest (x64+arm64) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
// tmuxCmd runs a tmux command and returns its stdout.
|
|
func tmuxCmd(args ...string) (string, error) {
|
|
out, err := exec.Command("tmux", args...).CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("tmux %s: %v: %s", strings.Join(args, " "), err, strings.TrimSpace(string(out)))
|
|
}
|
|
return string(out), nil
|
|
}
|
|
|
|
func tmuxHasSession(name string) bool {
|
|
err := exec.Command("tmux", "has-session", "-t", "="+name).Run()
|
|
return err == nil
|
|
}
|
|
|
|
func tmuxListSessions() []string {
|
|
out, err := tmuxCmd("list-sessions", "-F", "#{session_name}")
|
|
if err != nil {
|
|
return nil // no tmux server running = no sessions
|
|
}
|
|
var names []string
|
|
for _, l := range strings.Split(strings.TrimSpace(out), "\n") {
|
|
if l != "" {
|
|
names = append(names, l)
|
|
}
|
|
}
|
|
return names
|
|
}
|
|
|
|
func tmuxNewSession(name, cwd string, width, height int, cmdline string) error {
|
|
args := []string{"new-session", "-d", "-s", name,
|
|
"-x", fmt.Sprint(width), "-y", fmt.Sprint(height)}
|
|
if cwd != "" {
|
|
args = append(args, "-c", cwd)
|
|
}
|
|
if cmdline != "" {
|
|
args = append(args, cmdline)
|
|
}
|
|
_, err := tmuxCmd(args...)
|
|
return err
|
|
}
|
|
|
|
func tmuxKillSession(name string) error {
|
|
_, err := tmuxCmd("kill-session", "-t", "="+name)
|
|
return err
|
|
}
|
|
|
|
// tmuxCapture returns the visible pane content. ansi keeps escape
|
|
// sequences (for terminal-faithful rendering in clients).
|
|
func tmuxCapture(name string, ansi bool, history int) (string, error) {
|
|
args := []string{"capture-pane", "-p", "-t", "=" + name + ":"}
|
|
if ansi {
|
|
args = append(args, "-e")
|
|
}
|
|
if history > 0 {
|
|
args = append(args, "-S", fmt.Sprintf("-%d", history))
|
|
}
|
|
return tmuxCmd(args...)
|
|
}
|
|
|
|
// tmuxSendKeys sends key names (Enter, Up, Down, BTab, C-c, ...).
|
|
func tmuxSendKeys(name string, keys ...string) error {
|
|
args := append([]string{"send-keys", "-t", "=" + name + ":"}, keys...)
|
|
_, err := tmuxCmd(args...)
|
|
return err
|
|
}
|
|
|
|
// tmuxPasteText inserts literal text (multi-line safe) via a paste
|
|
// buffer, without submitting it.
|
|
func tmuxPasteText(name, text string) error {
|
|
cmd := exec.Command("tmux", "load-buffer", "-b", "helmd", "-")
|
|
cmd.Stdin = strings.NewReader(text)
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("tmux load-buffer: %v: %s", err, strings.TrimSpace(string(out)))
|
|
}
|
|
_, err := tmuxCmd("paste-buffer", "-d", "-b", "helmd", "-t", "="+name+":")
|
|
return err
|
|
}
|
|
|
|
func tmuxSessionAlive(name string) bool {
|
|
out, err := tmuxCmd("list-panes", "-t", "="+name+":", "-F", "#{pane_dead}")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return strings.TrimSpace(out) == "0"
|
|
}
|