feat: add bubbletea TUI dashboard for bot server management
Implementa un dashboard interactivo con bubbletea siguiendo el patrón pure core / impure shell del proyecto: - pkg/tui/ (PURE): Model, Update, View — solo fmt y strings, cero I/O. Update produce Intent[] (datos puros) en vez de side effects. - shell/tui/ (IMPURE): Adapter convierte Intent[] en tea.Cmd[] con I/O real (process management, /proc stats, log tail). - cmd/dashboard/ (composición): Bridge conecta pure Update con shell Adapter usando la Elm Architecture de bubbletea. Pantallas: Main Menu → Agent List → Agent Actions (start/stop/restart/kill) → Logs. Navegación: flechas ↑↓, Enter seleccionar, 0 volver, q salir. Dependencias añadidas: bubbletea, lipgloss. Actualiza .gitignore para anclar binarios a raíz (/agentctl, /dashboard). Documenta nuevos scripts en CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// Command dashboard provides an interactive TUI for managing bot agents.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// dashboard # launch the interactive TUI
|
||||
// go run ./cmd/dashboard
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
||||
puretui "github.com/enmanuel/agents/pkg/tui"
|
||||
"github.com/enmanuel/agents/shell/process"
|
||||
shelltui "github.com/enmanuel/agents/shell/tui"
|
||||
)
|
||||
|
||||
const (
|
||||
runDir = "run"
|
||||
agentsGlob = "agents/*/config.yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_ = os.MkdirAll(runDir, 0o755)
|
||||
|
||||
mgr := process.NewManager(runDir, agentsGlob, "")
|
||||
adapter := shelltui.NewAdapter(mgr)
|
||||
|
||||
p := tea.NewProgram(newBridge(adapter), tea.WithAltScreen())
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// bridge implements tea.Model and connects the pure Update with the impure Adapter.
|
||||
type bridge struct {
|
||||
model puretui.Model
|
||||
adapter *shelltui.Adapter
|
||||
}
|
||||
|
||||
func newBridge(adapter *shelltui.Adapter) bridge {
|
||||
return bridge{
|
||||
model: puretui.InitialModel(),
|
||||
adapter: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
func (b bridge) Init() tea.Cmd {
|
||||
return b.adapter.RunIntent(puretui.Intent{Kind: puretui.IntentLoadAgents})
|
||||
}
|
||||
|
||||
func (b bridge) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Convert tea messages to pure messages.
|
||||
var pureMsg interface{}
|
||||
switch m := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
pureMsg = puretui.KeyMsg{Str: m.String()}
|
||||
case tea.WindowSizeMsg:
|
||||
pureMsg = puretui.WindowSizeMsg{Width: m.Width, Height: m.Height}
|
||||
default:
|
||||
// MsgAgentsLoaded, MsgActionDone, MsgLogsLoaded, MsgTick pass through.
|
||||
pureMsg = msg
|
||||
}
|
||||
|
||||
// Pure update: no side effects.
|
||||
newModel, intents := puretui.Update(b.model, pureMsg)
|
||||
b.model = newModel
|
||||
|
||||
// Convert pure intents to impure tea.Cmds.
|
||||
cmds := make([]tea.Cmd, 0, len(intents))
|
||||
for _, intent := range intents {
|
||||
if cmd := b.adapter.RunIntent(intent); cmd != nil {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return b, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (b bridge) View() string {
|
||||
return puretui.View(b.model)
|
||||
}
|
||||
Reference in New Issue
Block a user