• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

smallnest / goclaw / 21978860214

13 Feb 2026 07:45AM UTC coverage: 5.772% (+0.008%) from 5.764%
21978860214

push

github

chaoyuepan
improve web fetch

4 of 24 new or added lines in 10 files covered. (16.67%)

221 existing lines in 4 files now uncovered.

1517 of 26284 relevant lines covered (5.77%)

0.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/skills/install_node.go
1
package skills
2

3
import (
4
        "context"
5
        "fmt"
6
        "strings"
7
)
8

9
// NodeInstaller implements Node.js package installations
10
type NodeInstaller struct {
11
        // NodeManager is the package manager to use (npm, pnpm, yarn, or bun)
12
        NodeManager string
13
}
14

15
var (
16
        // Supported node managers
17
        nodeManagers = []string{"npm", "pnpm", "yarn", "bun"}
18
)
19

20
// CanInstall checks if the configured node manager is available
21
func (n *NodeInstaller) CanInstall(spec *SkillInstallSpec) bool {
×
22
        if spec.Kind != "node" {
×
23
                return false
×
24
        }
×
25
        manager := n.resolveManager()
×
26
        return manager != "" && spec.Package != ""
×
27
}
28

29
// Install installs a package using the configured node manager
30
func (n *NodeInstaller) Install(ctx context.Context, spec *SkillInstallSpec) InstallResult {
×
31
        manager := n.resolveManager()
×
32
        if manager == "" {
×
33
                return InstallResult{
×
34
                        Success: false,
×
35
                        Message: "node manager not available (install npm, pnpm, yarn, or bun)",
×
36
                }
×
37
        }
×
38

39
        // Validate package
40
        if spec.Package == "" {
×
41
                return InstallResult{
×
42
                        Success: false,
×
43
                        Message: "missing node package",
×
44
                }
×
45
        }
×
46

47
        // Check if already installed
48
        if !spec.Extract {
×
49
                isInstalled := n.isInstalled(ctx, manager, spec.Package)
×
50
                if isInstalled {
×
51
                        return InstallResult{
×
52
                                Success: true,
×
53
                                Message: fmt.Sprintf("Already installed: %s", spec.Package),
×
54
                        }
×
55
                }
×
56
        }
57

58
        // Build command
59
        argv := n.buildCommand(spec.Package)
×
60

×
61
        // Run installation
×
62
        stdout, stderr, exitCode, _ := RunCommandWithTimeout(ctx, argv, nil)
×
63

×
64
        success := exitCode != nil && *exitCode == 0
×
65
        var message string
×
66
        if success {
×
67
                message = fmt.Sprintf("Installed: %s (%s)", spec.Package, manager)
×
68
        } else {
×
69
                message = FormatInstallFailureMessage(stdout, stderr, exitCode)
×
70
        }
×
71

72
        return InstallResult{
×
73
                Success:  success,
×
74
                Message:  message,
×
75
                Stdout:   stdout,
×
76
                Stderr:   stderr,
×
77
                ExitCode: exitCode,
×
78
        }
×
79
}
80

81
// resolveManager returns the best available node manager
82
func (n *NodeInstaller) resolveManager() string {
×
83
        // Use configured manager if available
×
84
        if n.NodeManager != "" && HasBinary(n.NodeManager) {
×
85
                return n.NodeManager
×
86
        }
×
87

88
        // Try preferred managers in order
89
        for _, manager := range nodeManagers {
×
90
                if HasBinary(manager) {
×
91
                        return manager
×
92
                }
×
93
        }
94

95
        return ""
×
96
}
97

98
// buildCommand builds the install command for a package
99
func (n *NodeInstaller) buildCommand(pkg string) []string {
×
100
        manager := n.resolveManager()
×
101

×
102
        switch manager {
×
103
        case "pnpm":
×
104
                return []string{"pnpm", "add", "-g", pkg}
×
105
        case "yarn":
×
106
                return []string{"yarn", "global", "add", pkg}
×
107
        case "bun":
×
108
                return []string{"bun", "add", "-g", pkg}
×
109
        default: // npm
×
110
                return []string{"npm", "install", "-g", pkg}
×
111
        }
112
}
113

114
// isInstalled checks if a package is installed globally
NEW
115
func (n *NodeInstaller) isInstalled(ctx context.Context, _, pkg string) bool {
×
NEW
116
        manager := n.resolveManager()
×
117

×
118
        var argv []string
×
119
        switch manager {
×
120
        case "pnpm":
×
121
                argv = []string{manager, "list", "-g", "--depth=0"}
×
122
        case "yarn":
×
123
                argv = []string{manager, "global", "list"}
×
124
        case "bun":
×
125
                argv = []string{manager, "pm", "ls", "-g"}
×
126
        default: // npm
×
127
                argv = []string{manager, "list", "-g", "--depth=0"}
×
128
        }
129

130
        stdout, _, _, _ := RunCommandWithTimeout(ctx, argv, nil)
×
131

×
132
        // Check if package appears in the list
×
133
        output := strings.ToLower(stdout)
×
134
        // For global packages, npm lists them as "package@version"
×
135
        // or just "package" depending on the npm version and flags
×
136
        search := strings.ToLower(pkg)
×
137
        return strings.Contains(output, search+"@") || strings.Contains(output, " "+search+" ") ||
×
138
                strings.Contains(output, search+"\n")
×
139
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc