//go:build darwin package discovery import ( "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") && !strings.Contains(line, "opencode-monitor") { 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 { lsofOut, err := exec.Command("lsof", "-p", strconv.Itoa(pid), "-i", "TCP", "-s", "TCP:LISTEN").Output() if err != nil { logger.Debug("执行 lsof 失败: %v", err) continue } for _, line := range strings.Split(string(lsofOut), "\n") { if strings.Contains(line, "LISTEN") { // lsof 输出格式: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME // NAME 部分格式: *:port 或 host:port fields := strings.Fields(line) if len(fields) >= 9 { nameField := fields[len(fields)-1] if strings.Contains(nameField, ":") { parts := strings.Split(nameField, ":") if len(parts) >= 2 { if port, err := strconv.Atoi(parts[len(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 }