npm.go 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package main
  2. import (
  3. "encoding/json"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. )
  10. const dshPackage = "@deepseek-ai/dsh"
  11. var (
  12. npmRootOnce sync.Once
  13. npmRootPath string
  14. )
  15. // npmGlobalRoot 执行 npm root -g(隐藏窗口),结果缓存(仅首次调用起子进程)。
  16. func npmGlobalRoot() string {
  17. npmRootOnce.Do(func() {
  18. exe, args := npmCommandFor("root", "-g")
  19. cmd := exec.Command(exe, args...)
  20. hideWindow(cmd)
  21. out, err := cmd.Output()
  22. if err != nil {
  23. return
  24. }
  25. npmRootPath = strings.TrimSpace(string(out))
  26. })
  27. return npmRootPath
  28. }
  29. // npmCommandFor 组装 npm 调用:优先 node + npm-cli.js(无 shell),回退 cmd /c npm。
  30. func npmCommandFor(args ...string) (string, []string) {
  31. if node, err := exec.LookPath("node"); err == nil {
  32. if cli := detectNpmCLI(node); cli != "" {
  33. return node, append([]string{cli}, args...)
  34. }
  35. }
  36. return "cmd.exe", append([]string{"/c", "npm"}, args...)
  37. }
  38. func detectNpmCLI(node string) string {
  39. base := filepath.Dir(node)
  40. candidates := []string{
  41. filepath.Join(base, "node_modules", "npm", "bin", "npm-cli.js"),
  42. filepath.Join(base, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
  43. }
  44. for _, c := range candidates {
  45. if fileExists(c) {
  46. return c
  47. }
  48. }
  49. return ""
  50. }
  51. // readPackageVersion 读取 package.json 的 version 字段。
  52. func readPackageVersion(path string) string {
  53. data, err := os.ReadFile(path)
  54. if err != nil {
  55. return ""
  56. }
  57. var v struct {
  58. Version string `json:"version"`
  59. }
  60. if err := json.Unmarshal(data, &v); err != nil {
  61. return ""
  62. }
  63. return v.Version
  64. }