okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
61b31d6
main
1package tui 2 3import ( 4"context" 5"errors" 6 7"albedo/cli/internal/daemon" 8 9 tea"charm.land/bubbletea/v2" 10) 11 12// Close stops the stream and invalidates batches, completions, and timers 13// already queued for this view. 14func (m * AgentsViewModel )Close () { 15m .viewGen ++ 16m .stopStream () 17} 18 19func (m * AgentsViewModel )stopStream () { 20if m .cancel != nil { 21m .cancel () 22m .cancel = nil 23 } 24m .Gen ++ 25m .seenMail = nil 26m .streamReady = false 27m .snapshotInFlight ,m .snapshotDirty = false ,false 28} 29 30func (m AgentsViewModel )snapshotCmd (gen int ) tea.Cmd { 31conn ,id := m .Conn ,m .SessionID 32return func () tea.Msg { 33if conn == nil { 34return agentsSnapshotMsg {Gen :gen ,Err :errors .New ("daemon connection unavailable" )} 35 } 36tree ,err := daemon .GetAgents (m .streamCtx ,conn ,id ) 37return agentsSnapshotMsg {Gen :gen ,Root :tree .Root ,Nodes :tree .Nodes ,Err :err } 38 } 39} 40 41func (m * AgentsViewModel )startStream (gen int ) tea.Cmd { 42if m .Conn == nil { 43return nil 44 } 45if m .cancel != nil { 46m .cancel () 47 } 48ctx ,cancel := context .WithCancel (context .Background ()) 49m .cancel = cancel 50m .streamCtx = ctx 51m .streamReady = false 52m .snapshotInFlight ,m .snapshotDirty = false ,false 53events := make (chan tea.Msg ,1 ) 54m .events = events 55conn := m .Conn 56go func () { 57defer close (events ) 58err := daemon .StreamAgents (ctx ,conn ,func (batch []daemon.AgentEvent )error { 59// Leaving the view stops its consumer. Cancellation must release a 60// producer blocked on a full queue so it can close the HTTP body. 61select { 62case events <- agentsEventsMsg {Gen :gen ,Events :batch }: 63return nil 64case <- ctx .Done (): 65return ctx .Err () 66 } 67 }) 68select { 69case events <- agentsStreamClosedMsg {Gen :gen ,Err :err }: 70case <- ctx .Done (): 71 } 72 }() 73return waitAgents (events ,gen ) 74} 75 76func (m * AgentsViewModel )restartStream () tea.Cmd { 77m .stopStream () 78m .snapshotRevision ++ 79for _ ,n := range m .nodes { 80n .preview = agentTail {} 81n .tail = agentTail {} 82n .progressByCallID = nil 83n .progressOrder = nil 84n .previewCallID = "" 85n .lineKind = tailText 86n .seeded = false 87n .revision ++ 88 } 89m .packets ,m .floats = nil ,nil 90m .ticking = false 91return m .startStream (m .Gen ) 92} 93 94func waitAgents (events <- chan tea.Msg ,gen int ) tea.Cmd { 95return func () tea.Msg { 96batch ,ok := <- events 97if ! ok { 98return agentsStreamClosedMsg {Gen :gen } 99 } 100return batch 101 } 102} 103 104// seedCmd loads the selected agent's recent history once; failures retry. 105func (m * AgentsViewModel )seedCmd () tea.Cmd { 106n := m .nodes [m .selected ] 107if ! m .streamReady || m .snapshotInFlight || m .err != nil || n == nil || n .seeded || n .id == agentsYou || m .Conn == nil { 108return nil 109 } 110n .seeded = true 111conn ,gen ,id := m .Conn ,m .Gen ,n .id 112snapshot ,ctx := m .snapshotRevision ,m .streamCtx 113if ctx == nil { 114ctx = context .Background () 115 } 116return func () tea.Msg { 117preview ,err := daemon .GetSessionPreview (ctx ,conn ,id ,10 ) 118if err != nil { 119return agentsSeedErrMsg {Gen :gen ,Snapshot :snapshot ,ID :id ,Err :err } 120 } 121return agentsSeedMsg {Gen :gen ,ID :id ,Snapshot :snapshot ,Items :preview .Items ,Session :preview .Session } 122 } 123}