package main import ( "encoding/json" "os" "os/exec" "path/filepath" "strings" "sync" ) const dshPackage = "@deepseek-ai/dsh" var ( npmRootOnce sync.Once npmRootPath string ) // npmGlobalRoot 执行 npm root -g(隐藏窗口),结果缓存(仅首次调用起子进程)。 func npmGlobalRoot() string { npmRootOnce.Do(func() { exe, args := npmCommandFor("root", "-g") cmd := exec.Command(exe, args...) hideWindow(cmd) out, err := cmd.Output() if err != nil { return } npmRootPath = strings.TrimSpace(string(out)) }) return npmRootPath } // npmCommandFor 组装 npm 调用:优先 node + npm-cli.js(无 shell),回退 cmd /c npm。 func npmCommandFor(args ...string) (string, []string) { if node, err := exec.LookPath("node"); err == nil { if cli := detectNpmCLI(node); cli != "" { return node, append([]string{cli}, args...) } } return "cmd.exe", append([]string{"/c", "npm"}, args...) } func detectNpmCLI(node string) string { base := filepath.Dir(node) candidates := []string{ filepath.Join(base, "node_modules", "npm", "bin", "npm-cli.js"), filepath.Join(base, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"), } for _, c := range candidates { if fileExists(c) { return c } } return "" } // readPackageVersion 读取 package.json 的 version 字段。 func readPackageVersion(path string) string { data, err := os.ReadFile(path) if err != nil { return "" } var v struct { Version string `json:"version"` } if err := json.Unmarshal(data, &v); err != nil { return "" } return v.Version }