package main import ( "context" "errors" "fmt" "os/exec" "strings" "time" ) const allowScripts = "@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs" // Update 执行全局更新:npm install -g @deepseek-ai/dsh@latest --allow-scripts=... func (s *Supervisor) Update() (string, error) { s.mu.Lock() if s.updating { s.mu.Unlock() return "", errors.New("更新已在进行中") } s.updating = true s.mu.Unlock() defer func() { s.mu.Lock() s.updating = false s.mu.Unlock() }() exe, args := npmCommandFor("install", "-g", dshPackage+"@latest", "--allow-scripts="+allowScripts) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) defer cancel() cmd := exec.CommandContext(ctx, exe, args...) hideWindow(cmd) s.log.Printf("执行更新: %s %s", exe, strings.Join(args, " ")) out, err := cmd.CombinedOutput() text := strings.TrimSpace(string(out)) if text != "" { s.log.Printf("更新输出: %s", text) } if err != nil { return text, fmt.Errorf("更新失败: %w", err) } s.invalidateProbe() return text, nil } // LatestVersion 查询 npm 上最新版本。 func (s *Supervisor) LatestVersion() (string, error) { exe, args := npmCommandFor("view", dshPackage, "version") ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() cmd := exec.CommandContext(ctx, exe, args...) hideWindow(cmd) out, err := cmd.Output() if err != nil { return "", err } return strings.TrimSpace(string(out)), nil } // startAutoUpdate 定期后台更新(单例):每分钟检查开关,到达间隔才查询/更新。 // 重复调用安全:已有循环时直接返回,避免多个循环并存。 func (s *Supervisor) startAutoUpdate() { s.autoMu.Lock() if s.autoLoop { s.autoMu.Unlock() return } s.autoLoop = true s.autoMu.Unlock() go func() { defer func() { s.autoMu.Lock() s.autoLoop = false s.autoMu.Unlock() }() var elapsed time.Duration ticker := time.NewTicker(time.Minute) defer ticker.Stop() for range ticker.C { s.mu.Lock() enabled := s.cfg.AutoUpdate interval := time.Duration(s.cfg.UpdateIntervalH) * time.Hour s.mu.Unlock() if !enabled { s.log.Printf("定期更新:已关闭") return } if interval <= 0 { interval = 24 * time.Hour } elapsed += time.Minute if elapsed < interval { continue } elapsed = 0 s.runScheduledUpdate() } }() } // runScheduledUpdate 查询最新版本,有更新才安装并重启。 func (s *Supervisor) runScheduledUpdate() { latest, err := s.LatestVersion() if err != nil { s.log.Printf("定期更新:查询最新版本失败: %v", err) return } current := s.installedVersion() if latest == "" || current == latest { s.log.Printf("定期更新:已是最新(%s)", current) return } s.log.Printf("定期更新:发现新版本 %s(当前 %s),开始更新", latest, current) if _, err := s.Update(); err != nil { s.log.Printf("定期更新失败: %v", err) return } s.Restart() }