package main import ( "os" "os/exec" "path/filepath" ) // probe.go —— 运行环境探测(node 路径、dsh 入口、已装版本)。 // 结果缓存于 Supervisor,probeMu 保证并发只探测一次;更新后调用 invalidateProbe 失效。 func (s *Supervisor) nodePath() string { s.mu.Lock() if s.nodeCache != "" { v := s.nodeCache s.mu.Unlock() return v } s.mu.Unlock() p, err := exec.LookPath("node") if err != nil { return "" } s.mu.Lock() s.nodeCache = p s.mu.Unlock() return p } // dshBinJS 探测 dsh 的 bin.js(APPDATA\npm 与 npm root -g)。 func (s *Supervisor) dshBinJS() string { s.probeMu.Lock() defer s.probeMu.Unlock() s.mu.Lock() if s.binCache != "" { v := s.binCache s.mu.Unlock() return v } s.mu.Unlock() var candidates []string if appdata := os.Getenv("APPDATA"); appdata != "" { candidates = append(candidates, filepath.Join(appdata, "npm", "node_modules", "@deepseek-ai", "dsh", "lib", "bin.js")) } if root := npmGlobalRoot(); root != "" { candidates = append(candidates, filepath.Join(root, "@deepseek-ai", "dsh", "lib", "bin.js")) } for _, c := range candidates { if fileExists(c) { s.mu.Lock() s.binCache = c s.mu.Unlock() s.log.Printf("dsh 入口: %s", c) return c } } return "" } // invalidateProbe 使入口缓存失效(更新 dsh 后调用)。 func (s *Supervisor) invalidateProbe() { s.mu.Lock() s.binCache = "" s.mu.Unlock() } // installedVersion 读取已安装 dsh 版本(空串表示未找到)。 func (s *Supervisor) installedVersion() string { binJS := s.cfg.DshBinJS if binJS == "" { binJS = s.dshBinJS() } if binJS == "" { return "" } return readPackageVersion(filepath.Join(filepath.Dir(filepath.Dir(binJS)), "package.json")) } func fileExists(p string) bool { st, err := os.Stat(p) return err == nil && !st.IsDir() }