package browser import ( "encoding/json" "fmt" "net/http" "strings" ) // CdpConnectTarget se conecta a un target CDP DETERMINISTA identificado por match. // // Si host es "" se usa "localhost". // match puede ser: // - "" → primer target con Type "page" y WebSocketDebuggerURL no vacío (misma // semántica que CdpConnectHost, útil como fallback compatible). // - ID exacto del target (campo "id" en /json). // - Substring case-insensitive de la URL del target. // // Retorna error si ningún target type=page satisface el match. func CdpConnectTarget(host string, port int, match string) (*CDPConn, error) { if host == "" { host = "localhost" } resp, err := http.Get(fmt.Sprintf("http://%s:%d/json", host, port)) if err != nil { return nil, fmt.Errorf("cdp connect target: listar targets: %w", err) } defer resp.Body.Close() var targets []cdpTarget if err := json.NewDecoder(resp.Body).Decode(&targets); err != nil { return nil, fmt.Errorf("cdp connect target: decode targets: %w", err) } matchLower := strings.ToLower(match) for _, t := range targets { if t.Type != "page" || t.WebSocketDebuggerURL == "" { continue } if match == "" { // Sin filtro: primera tab page disponible. return cdpConnectWS(t.WebSocketDebuggerURL, port) } // Coincidencia por ID exacto o substring de URL (case-insensitive). if t.ID == match || strings.Contains(strings.ToLower(t.URL), matchLower) { return cdpConnectWS(t.WebSocketDebuggerURL, port) } } if match == "" { return nil, fmt.Errorf("cdp connect target: no hay ninguna tab 'page' disponible en %s:%d", host, port) } return nil, fmt.Errorf("cdp connect target: no hay tab 'page' que matchee %q en %s:%d", match, host, port) }