runtime_paths.go
1 package utils 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 ) 10 11 const ( 12 AgentScanDirEnv = "AIG_AGENT_SCAN_DIR" 13 McpScanDirEnv = "AIG_MCP_SCAN_DIR" 14 PromptSecurityDirEnv = "AIG_PROMPT_SECURITY_DIR" 15 UvBinEnv = "AIG_UV_BIN" 16 ) 17 18 func ResolveAgentScanDir() (string, error) { 19 return resolveRuntimeDir(AgentScanDirEnv, "/app/agent-scan", "agent-scan") 20 } 21 22 func ResolveMcpScanDir() (string, error) { 23 return resolveRuntimeDir(McpScanDirEnv, "/app/mcp-scan", "mcp-scan") 24 } 25 26 func ResolvePromptSecurityDir() (string, error) { 27 return resolveRuntimeDir(PromptSecurityDirEnv, "/app/AIG-PromptSecurity", "AIG-PromptSecurity") 28 } 29 30 func ResolveUvBin() (string, error) { 31 if override := strings.TrimSpace(os.Getenv(UvBinEnv)); override != "" { 32 return override, nil 33 } 34 if uvPath, err := exec.LookPath("uv"); err == nil { 35 return uvPath, nil 36 } 37 const containerUvPath = "/usr/local/bin/uv" 38 if info, err := os.Stat(containerUvPath); err == nil && !info.IsDir() { 39 return containerUvPath, nil 40 } 41 return "", fmt.Errorf("unable to locate uv; set %s to override the path", UvBinEnv) 42 } 43 44 func resolveRuntimeDir(envName, containerPath, relativePath string) (string, error) { 45 candidates := collectRuntimeDirCandidates(envName, containerPath, relativePath, runtimeSearchBases()...) 46 for _, candidate := range candidates { 47 info, err := os.Stat(candidate) 48 if err == nil && info.IsDir() { 49 return candidate, nil 50 } 51 } 52 return "", fmt.Errorf("unable to locate %s; set %s to override the path", relativePath, envName) 53 } 54 55 func runtimeSearchBases() []string { 56 var bases []string 57 if wd, err := os.Getwd(); err == nil { 58 bases = append(bases, wd) 59 } 60 if execPath, err := os.Executable(); err == nil { 61 bases = append(bases, filepath.Dir(execPath)) 62 } 63 return bases 64 } 65 66 func collectRuntimeDirCandidates(envName, containerPath, relativePath string, bases ...string) []string { 67 var candidates []string 68 seen := make(map[string]struct{}) 69 addCandidate := func(path string) { 70 path = strings.TrimSpace(path) 71 if path == "" { 72 return 73 } 74 path = filepath.Clean(path) 75 if !filepath.IsAbs(path) { 76 if absPath, err := filepath.Abs(path); err == nil { 77 path = absPath 78 } 79 } 80 if _, ok := seen[path]; ok { 81 return 82 } 83 seen[path] = struct{}{} 84 candidates = append(candidates, path) 85 } 86 87 addCandidate(os.Getenv(envName)) 88 addCandidate(containerPath) 89 90 for _, base := range bases { 91 base = strings.TrimSpace(base) 92 if base == "" { 93 continue 94 } 95 current := filepath.Clean(base) 96 for i := 0; i < 8; i++ { 97 addCandidate(filepath.Join(current, relativePath)) 98 parent := filepath.Dir(current) 99 if parent == current { 100 break 101 } 102 current = parent 103 } 104 } 105 106 return candidates 107 }