//go:build linux package discovery import ( "fmt" "os/exec" "regexp" "sort" "strconv" "strings" "ai-status-light/internal/logger" ) func findByPID() []int { var ports []int pidRegex := regexp.MustCompile(`(?:^|/)opencode(?:\s|$)`) out, err := exec.Command("ps", "aux").Output() if err != nil { logger.Debug("执行 ps aux 失败: %v", err) return ports } var pids []int for _, line := range strings.Split(string(out), "\n") { if pidRegex.MatchString(line) && !strings.Contains(line, "grep") { fields := strings.Fields(line) if len(fields) > 1 { if pid, err := strconv.Atoi(fields[1]); err == nil { pids = append(pids, pid) } } } } if len(pids) == 0 { logger.Debug("未找到 opencode 进程") return ports } logger.Debug("发现 opencode 进程 PID: %v", pids) for _, pid := range pids { ssOut, err := exec.Command("ss", "-tlnp").Output() if err != nil { logger.Debug("执行 ss -tlnp 失败: %v", err) continue } for _, line := range strings.Split(string(ssOut), "\n") { if strings.Contains(line, fmt.Sprintf("pid=%d", pid)) { fields := strings.Fields(line) for _, f := range fields { if strings.Contains(f, ":") { parts := strings.Split(f, ":") if len(parts) == 2 { if port, err := strconv.Atoi(parts[1]); err == nil { if port > 0 && port < 65535 && !contains(ports, port) { ports = append(ports, port) } } } } } } } } sort.Ints(ports) return ports } func findByCmdline() []int { var ports []int out, err := exec.Command("ps", "aux").Output() if err != nil { logger.Debug("执行 ps aux 失败: %v", err) return ports } for _, line := range strings.Split(string(out), "\n") { if strings.Contains(line, "opencode") && !strings.Contains(line, "opencode-monitor") && !strings.Contains(line, "grep") { if strings.Contains(line, "--port") { fields := strings.Fields(line) for i, f := range fields { if f == "--port" && i+1 < len(fields) { if port, err := strconv.Atoi(fields[i+1]); err == nil { if !contains(ports, port) { ports = append(ports, port) } } } } } } } sort.Ints(ports) return ports }