All checks were successful
CI / build (push) Successful in 2m41s
Split monolithic app.go into focused modules (dashboard, chat, workflow, config, agents, terminal, commands, handlers). Add proper error handling for installer commands, proxy pipes, and MCP config parsing. Fix daemon channel buffer, cap orchestrator history, compile think regex once, and set HTTP timeouts on preview server. Improve CI with Go module caching, dependency download step, and test stage with race detection. 😘 Generated with Crush Assisted-by: GLM-5-Turbo via Crush <crush@charm.land>
174 lines
3.1 KiB
Go
174 lines
3.1 KiB
Go
package daemon
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/muyue/muyue/internal/config"
|
|
"github.com/muyue/muyue/internal/scanner"
|
|
"github.com/muyue/muyue/internal/updater"
|
|
)
|
|
|
|
type Daemon struct {
|
|
config *config.MuyueConfig
|
|
interval time.Duration
|
|
stopCh chan struct{}
|
|
mu sync.RWMutex
|
|
running bool
|
|
lastCheck time.Time
|
|
lastStatus []updater.UpdateStatus
|
|
logs []string
|
|
onUpdate func([]updater.UpdateStatus)
|
|
}
|
|
|
|
func NewDaemon(cfg *config.MuyueConfig, interval time.Duration) *Daemon {
|
|
if interval == 0 {
|
|
interval = 1 * time.Hour
|
|
}
|
|
return &Daemon{
|
|
config: cfg,
|
|
interval: interval,
|
|
stopCh: make(chan struct{}, 1),
|
|
logs: []string{},
|
|
}
|
|
}
|
|
|
|
func (d *Daemon) OnUpdate(fn func([]updater.UpdateStatus)) {
|
|
d.onUpdate = fn
|
|
}
|
|
|
|
func (d *Daemon) Start() error {
|
|
d.mu.Lock()
|
|
if d.running {
|
|
d.mu.Unlock()
|
|
return fmt.Errorf("daemon already running")
|
|
}
|
|
d.running = true
|
|
d.mu.Unlock()
|
|
|
|
d.log("daemon started (interval: %s)", d.interval)
|
|
|
|
go d.run()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) Stop() {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
if !d.running {
|
|
return
|
|
}
|
|
d.running = false
|
|
d.stopCh <- struct{}{}
|
|
d.log("daemon stopped")
|
|
}
|
|
|
|
func (d *Daemon) IsRunning() bool {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
return d.running
|
|
}
|
|
|
|
func (d *Daemon) LastCheck() time.Time {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
return d.lastCheck
|
|
}
|
|
|
|
func (d *Daemon) LastStatus() []updater.UpdateStatus {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
return d.lastStatus
|
|
}
|
|
|
|
func (d *Daemon) Logs() []string {
|
|
d.mu.RLock()
|
|
defer d.mu.RUnlock()
|
|
return d.logs
|
|
}
|
|
|
|
func (d *Daemon) TriggerCheck() []updater.UpdateStatus {
|
|
return d.checkUpdates()
|
|
}
|
|
|
|
func (d *Daemon) run() {
|
|
d.checkUpdates()
|
|
|
|
ticker := time.NewTicker(d.interval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
d.checkUpdates()
|
|
case <-d.stopCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *Daemon) checkUpdates() []updater.UpdateStatus {
|
|
d.log("checking for updates...")
|
|
result := scanner.ScanSystem()
|
|
statuses := updater.CheckUpdates(result)
|
|
|
|
needsUpdate := false
|
|
for _, s := range statuses {
|
|
if s.NeedsUpdate {
|
|
needsUpdate = true
|
|
d.log("update available: %s %s -> %s", s.Tool, s.Current, s.Latest)
|
|
}
|
|
}
|
|
|
|
if !needsUpdate {
|
|
d.log("all tools up to date")
|
|
}
|
|
|
|
d.mu.Lock()
|
|
d.lastCheck = time.Now()
|
|
d.lastStatus = statuses
|
|
d.mu.Unlock()
|
|
|
|
if d.config.Profile.Preferences.AutoUpdate && needsUpdate {
|
|
d.log("auto-updating...")
|
|
results := updater.RunAutoUpdate(statuses)
|
|
for _, r := range results {
|
|
if r.Message != "" {
|
|
d.log(" %s: %s", r.Tool, r.Message)
|
|
}
|
|
}
|
|
}
|
|
|
|
if d.onUpdate != nil {
|
|
d.onUpdate(statuses)
|
|
}
|
|
|
|
return statuses
|
|
}
|
|
|
|
func (d *Daemon) log(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), fmt.Sprintf(format, args...))
|
|
d.mu.Lock()
|
|
d.logs = append(d.logs, msg)
|
|
if len(d.logs) > 500 {
|
|
d.logs = d.logs[250:]
|
|
}
|
|
d.mu.Unlock()
|
|
}
|
|
|
|
func RunStandalone(cfg *config.MuyueConfig) {
|
|
d := NewDaemon(cfg, 1*time.Hour)
|
|
d.Start()
|
|
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
<-sigCh
|
|
d.Stop()
|
|
}
|