Files
agent-helm/helmd/tmux.go
claude f9c79ac84e
Some checks failed
build-and-push / build (push) Successful in 42s
release-client / build-release (push) Has been cancelled
helmd-release / build-release (push) Successful in 1m6s
helmd v2: Go-server som styr agent i tmux — REST+SSE, frågedetektor, fildelning, ntfy, CI-release
- 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>
2026-08-05 20:54:54 +02:00

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"
}