Module: Clacky::Utils::BrowserDetector

Defined in:
lib/clacky/utils/browser_detector.rb

Overview

Detects a running browser (Chrome/Edge) that has remote debugging enabled.

Detection strategy:

1. Scan known UserData directories for DevToolsActivePort file.
   This file contains the exact port + WS path — most reliable.
   Returns { mode: :ws_endpoint, value: "ws://127.0.0.1:PORT/PATH" }

2. Verify the port is actually reachable via TCP probe.

3. Nothing found or port unreachable → returns nil (browser not running).

Supported environments: WSL, Linux, macOS.

Class Method Summary collapse

Class Method Details

.detectHash

Detect a running debuggable browser. Scans for DevToolsActivePort file across all platforms (macOS/Linux/WSL). Returns the detected WebSocket endpoint only if the port is reachable.

Returns:

  • (Hash)

    { mode: :ws_endpoint, value: String, status: :ok|:not_found }



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/clacky/utils/browser_detector.rb', line 26

def self.detect
  os = EnvironmentDetector.os_type
  Clacky::Logger.debug("[BrowserDetector] Starting browser detection (OS: #{os})...")
  
  detected = detect_via_active_port_file
  
  unless detected
    Clacky::Logger.warn("[BrowserDetector] ✗ No reachable browser found")
    return { status: :not_found }
  end
  
  Clacky::Logger.info("[BrowserDetector] ✓ Browser detected and reachable: #{detected[:mode]}#{detected[:value]}")
  detected.merge(status: :ok)
end

.detect_via_active_port_fileHash?

Returns:

  • (Hash, nil)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/clacky/utils/browser_detector.rb', line 46

def self.detect_via_active_port_file
  Clacky::Logger.debug("[BrowserDetector] Scanning UserData directories for DevToolsActivePort...")
  
  dirs = user_data_dirs
  Clacky::Logger.debug("[BrowserDetector] Candidate directories: #{dirs.size} found")
  
  dirs.each do |dir|
    port_file = File.join(dir, "DevToolsActivePort")
    next unless File.exist?(port_file)

    Clacky::Logger.debug("[BrowserDetector] Found DevToolsActivePort: #{port_file}")
    
    ws = parse_active_port_file(port_file)
    unless ws
      Clacky::Logger.debug("[BrowserDetector] ✗ Failed to parse #{port_file}")
      next
    end
    
    Clacky::Logger.debug("[BrowserDetector] Parsed WS endpoint: #{ws}")
    
    # ⭐️ Verify port BEFORE returning — skip stale files
    candidate = { mode: :ws_endpoint, value: ws }
    if verify_port(candidate)
      Clacky::Logger.debug("[BrowserDetector] ✓ Port is reachable, using this endpoint")
      return candidate
    else
      Clacky::Logger.debug("[BrowserDetector] ✗ Port not reachable, trying next directory...")
    end
  end
  
  Clacky::Logger.debug("[BrowserDetector] No reachable browser found")
  nil
end

.user_data_dirsArray<String>

Returns ordered list of candidate UserData dirs to check.

Returns:

  • (Array<String>)


106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/clacky/utils/browser_detector.rb', line 106

def self.user_data_dirs
  os = EnvironmentDetector.os_type
  Clacky::Logger.debug("[BrowserDetector] Detected OS: #{os}")
  
  case os
  when :wsl   then wsl_user_data_dirs
  when :linux then linux_user_data_dirs
  when :macos then macos_user_data_dirs
  else
    Clacky::Logger.warn("[BrowserDetector] Unknown OS type: #{os}")
    []
  end
end

.verify_port(detected) ⇒ Boolean

Verify that the detected browser port is actually reachable. Extracts port from ws:// URL and attempts TCP connection.

Parameters:

  • detected (Hash)

    { mode: :ws_endpoint, value: String }

Returns:

  • (Boolean)

    true if port is open and reachable



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/clacky/utils/browser_detector.rb', line 84

def self.verify_port(detected)
  return false unless detected

  port = case detected[:mode]
  when :ws_endpoint
    # ws://127.0.0.1:9222/devtools/...
    detected[:value][/ws:\/\/127\.0\.0\.1:(\d+)/, 1]&.to_i
  end

  return false unless port && port > 0

  reachable = tcp_open?("127.0.0.1", port)
  Clacky::Logger.debug("[BrowserDetector] Port #{port} reachable: #{reachable}")
  reachable
end