feat: update dashboard and process manager for unified launcher

Actualiza el dashboard TUI y el process manager para el modelo de launcher
unificado donde todos los agentes corren en un solo proceso.

Dashboard (pkg/tui):
- model.go: campos de estado del launcher (PID, uptime, memory, CPU, log size)
- model.go: ServerMenuOptions(running) contextual, AgentActionOptions(enabled)
- messages.go: MsgAgentsLoaded incluye estado del launcher, MsgServerActionDone/MsgRebuildDone simplificados
- update.go: intents nuevos (Enable/Disable agent, Start/Stop/Restart/Kill launcher)
- view.go: vista de servidor muestra stats del launcher, agentes muestran enabled/disabled

Shell adapter (shell/tui):
- adapter.go: reescrito para usar métodos unificados (StartUnified, StopUnified, ToggleEnabled, StatusAllUnified, UnifiedStats, UnifiedLogTail)

Process manager (shell/process):
- manager.go: métodos StartUnified, StopUnified, KillUnified, IsUnifiedRunning, UnifiedPID, UnifiedStats, UnifiedLogTail, StatusAllUnified, ToggleEnabled

Los agentes ya no se inician/detienen individualmente desde el dashboard.
Se habilitan/deshabilitan en config y se reinicia el launcher para aplicar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 09:05:57 +00:00
parent 2667af52cc
commit 1af0457c1f
6 changed files with 432 additions and 370 deletions
+42 -41
View File
@@ -102,39 +102,26 @@ func viewAgentActions(m Model) string {
}
a := m.Selected
icon := "○ stopped"
if a.Running {
if a.Instances > 1 {
icon = fmt.Sprintf("● running %d instances", a.Instances)
} else {
icon = fmt.Sprintf("● running PID %d", a.PID)
}
var icon string
switch {
case !a.Enabled:
icon = " disabled"
case a.Running:
icon = "● enabled (running)"
default:
icon = "○ enabled (stopped)"
}
b.WriteString(fmt.Sprintf("\n %s %s\n", a.ID, icon))
b.WriteString(" " + strings.Repeat("─", 44) + "\n")
// Stats line if running
if a.Running && (a.Memory != "" || a.CPU != "") {
parts := []string{}
if a.Uptime != "" {
parts = append(parts, "uptime: "+a.Uptime)
}
if a.Memory != "" {
parts = append(parts, "mem: "+a.Memory)
}
if a.CPU != "" {
parts = append(parts, "cpu: "+a.CPU)
}
if a.LogSize != "" {
parts = append(parts, "log: "+a.LogSize)
}
b.WriteString(" " + strings.Join(parts, " ") + "\n")
if a.Desc != "" {
b.WriteString(" " + a.Desc + "\n")
}
b.WriteString("\n")
opts := AgentActionOptions(a.Running)
opts := AgentActionOptions(a.Enabled)
for i, opt := range opts {
cursor := " "
if i == m.Cursor {
@@ -154,7 +141,7 @@ func viewAgentActions(m Model) string {
func viewLogs(m Model) string {
var b strings.Builder
agentID := "?"
agentID := "Launcher"
if m.Selected != nil {
agentID = m.Selected.ID
}
@@ -190,27 +177,41 @@ func viewLogs(m Model) string {
func viewServer(m Model) string {
var b strings.Builder
b.WriteString("\n Server Management\n")
b.WriteString("\n Launcher Management\n")
b.WriteString(" " + strings.Repeat("─", 44) + "\n")
// Summary
running, stopped, disabled := countStatuses(m.Agents)
total := len(m.Agents)
if total > 0 {
b.WriteString(fmt.Sprintf(" %d agents: %d running, %d stopped, %d disabled\n", total, running, stopped, disabled))
// Launcher status
if m.LauncherRunning {
b.WriteString(fmt.Sprintf(" ● Launcher running PID %d\n", m.LauncherPID))
parts := []string{}
if m.LauncherUptime != "" {
parts = append(parts, "uptime: "+m.LauncherUptime)
}
if m.LauncherMemory != "" {
parts = append(parts, "mem: "+m.LauncherMemory)
}
if m.LauncherCPU != "" {
parts = append(parts, "cpu: "+m.LauncherCPU)
}
if m.LauncherLogSize != "" {
parts = append(parts, "log: "+m.LauncherLogSize)
}
if len(parts) > 0 {
b.WriteString(" " + strings.Join(parts, " ") + "\n")
}
} else {
b.WriteString(" Loading...\n")
b.WriteString(" ○ Launcher stopped\n")
}
// Agent status list (compact)
if total > 0 {
b.WriteString("\n")
// Agent summary
_, _, disabled := countStatuses(m.Agents)
enabled := len(m.Agents) - disabled
if len(m.Agents) > 0 {
b.WriteString(fmt.Sprintf("\n %d agents (%d enabled, %d disabled)\n", len(m.Agents), enabled, disabled))
for _, a := range m.Agents {
icon := ""
icon := ""
if !a.Enabled {
icon = " "
} else if a.Running {
icon = "●"
icon = ""
}
b.WriteString(fmt.Sprintf(" %s %s\n", icon, a.ID))
}
@@ -219,12 +220,12 @@ func viewServer(m Model) string {
b.WriteString("\n")
// Action menu
for i, opt := range ServerMenuOptions() {
for i, opt := range ServerMenuOptions(m.LauncherRunning) {
cursor := " "
if i == m.Cursor {
cursor = "> "
}
b.WriteString(fmt.Sprintf(" %s%-16s %s\n", cursor, opt.Label, opt.Desc))
b.WriteString(fmt.Sprintf(" %s%-20s %s\n", cursor, opt.Label, opt.Desc))
}
if m.StatusMsg != "" {