Files
goclaw/internal/skills/package_runtime_paths.go

144 lines
3.0 KiB
Go

package skills
import (
"os"
"path/filepath"
"runtime"
"strings"
)
func packageRuntimeDir() string {
if v := strings.TrimSpace(os.Getenv("RUNTIME_DIR")); v != "" {
return v
}
if v := strings.TrimSpace(os.Getenv("GOCLAW_DATA_DIR")); v != "" {
return filepath.Join(v, ".runtime")
}
if runtime.GOOS != "windows" && !IsAlpineRuntime() {
return "/var/lib/goclaw/data/.runtime"
}
return filepath.Join("/app/data", ".runtime")
}
func npmGlobalPrefix() string {
if v := strings.TrimSpace(os.Getenv("NPM_CONFIG_PREFIX")); v != "" {
return v
}
return filepath.Join(packageRuntimeDir(), "npm-global")
}
func npmGlobalBinDir() string {
if runtime.GOOS == "windows" {
return npmGlobalPrefix()
}
return filepath.Join(npmGlobalPrefix(), "bin")
}
func RuntimeExecutableDirs() []string {
return uniqueNonEmptyPaths(
filepath.Join(packageRuntimeDir(), "bin"),
npmGlobalBinDir(),
filepath.Join(packageRuntimeDir(), "pip", "bin"),
)
}
func FindRuntimeExecutable(name string) (string, bool) {
if strings.ContainsAny(name, `/\`) {
return "", false
}
for _, dir := range RuntimeExecutableDirs() {
path := filepath.Join(dir, name)
if IsExecutableFile(path) {
return path, true
}
}
if path, ok := findNpmPackageExecutableAlias(name); ok {
return path, true
}
return "", false
}
func IsExecutableFile(path string) bool {
info, err := os.Stat(path)
if err != nil || info.IsDir() {
return false
}
if runtime.GOOS == "windows" {
return true
}
return info.Mode().Perm()&0111 != 0
}
func npmGlobalNodePath() string {
return filepath.Join(npmGlobalPrefix(), "lib", "node_modules")
}
func ensureNpmGlobalEnv() {
prependProcessPath(npmGlobalBinDir())
}
func npmCommandEnv() []string {
prefix := npmGlobalPrefix()
binDir := npmGlobalBinDir()
nodePath := npmGlobalNodePath()
env := make([]string, 0, len(os.Environ())+3)
for _, e := range os.Environ() {
switch {
case strings.HasPrefix(e, "NPM_CONFIG_PREFIX="):
continue
case strings.HasPrefix(e, "PATH="):
continue
case strings.HasPrefix(e, "NODE_PATH="):
continue
}
env = append(env, e)
}
pathValue := prependPathValue(os.Getenv("PATH"), binDir)
nodePathValue := prependPathValue(os.Getenv("NODE_PATH"), nodePath)
env = append(env,
"NPM_CONFIG_PREFIX="+prefix,
"PATH="+pathValue,
"NODE_PATH="+nodePathValue,
)
return env
}
func prependProcessPath(dir string) {
if strings.TrimSpace(dir) == "" {
return
}
_ = os.Setenv("PATH", prependPathValue(os.Getenv("PATH"), dir))
}
func prependPathValue(current, dir string) string {
if strings.TrimSpace(dir) == "" {
return current
}
parts := filepath.SplitList(current)
for _, p := range parts {
if p == dir {
return current
}
}
if current == "" {
return dir
}
return dir + string(os.PathListSeparator) + current
}
func uniqueNonEmptyPaths(paths ...string) []string {
seen := make(map[string]bool, len(paths))
out := make([]string, 0, len(paths))
for _, path := range paths {
path = strings.TrimSpace(path)
if path == "" || seen[path] {
continue
}
seen[path] = true
out = append(out, path)
}
return out
}