@@ -1936,41 +1936,273 @@ def doctor_db():
19361936 raise typer .Exit (1 )
19371937
19381938
1939+ @doctor_app .command ("bridge" )
1940+ def doctor_bridge (
1941+ host : str = typer .Option ("localhost" , "--host" , "-H" , help = "Server host" ),
1942+ port : int = typer .Option (8765 , "--port" , "-p" , help = "Server port" ),
1943+ ):
1944+ """Test bridge WebSocket connectivity.
1945+
1946+ Performs:
1947+ 1. HTTP health check
1948+ 2. WebSocket handshake test
1949+ 3. Welcome message validation
1950+
1951+ Examples:
1952+ praisonai browser doctor bridge
1953+ praisonai browser doctor bridge --port 8766
1954+ """
1955+ import asyncio
1956+ from .diagnostics import check_bridge_server , check_bridge_websocket
1957+
1958+ console .print ("[bold]Bridge Server Test[/bold]\n " )
1959+
1960+ async def run_checks ():
1961+ results = []
1962+ results .append (await check_bridge_server (host , port ))
1963+ results .append (await check_bridge_websocket (host , port ))
1964+ return results
1965+
1966+ results = asyncio .run (run_checks ())
1967+
1968+ all_pass = True
1969+ for r in results :
1970+ if r .status .value == "pass" :
1971+ console .print (f"[green]✅ { r .name } :[/green] { r .message } " )
1972+ elif r .status .value == "fail" :
1973+ console .print (f"[red]❌ { r .name } :[/red] { r .message } " )
1974+ all_pass = False
1975+ else :
1976+ console .print (f"[yellow]⚠️ { r .name } :[/yellow] { r .message } " )
1977+
1978+ if r .details :
1979+ for k , v in r .details .items ():
1980+ console .print (f" { k } : { v } " )
1981+
1982+ if not all_pass :
1983+ raise typer .Exit (1 )
1984+
1985+
1986+ @doctor_app .command ("api-keys" )
1987+ def doctor_api_keys (
1988+ model : Optional [str ] = typer .Option (None , "--model" , "-m" , help = "Test specific model API" ),
1989+ validate : bool = typer .Option (False , "--validate" , "-V" , help = "Actually call API to validate key" ),
1990+ ):
1991+ """Check API key configuration.
1992+
1993+ Verifies:
1994+ - OPENAI_API_KEY
1995+ - GEMINI_API_KEY
1996+ - ANTHROPIC_API_KEY
1997+
1998+ Examples:
1999+ praisonai browser doctor api-keys
2000+ praisonai browser doctor api-keys --validate
2001+ praisonai browser doctor api-keys --model gemini/gemini-2.0-flash --validate
2002+ """
2003+ from .diagnostics import check_api_keys , check_api_key_valid
2004+
2005+ console .print ("[bold]API Key Check[/bold]\n " )
2006+
2007+ results = check_api_keys ()
2008+
2009+ all_pass = True
2010+ for r in results :
2011+ provider = r .details .get ('provider' , r .name )
2012+ if r .status .value == "pass" :
2013+ console .print (f"[green]✅ { provider } :[/green] { r .message } " )
2014+ elif r .status .value == "fail" :
2015+ console .print (f"[red]❌ { provider } :[/red] { r .message } " )
2016+ all_pass = False
2017+ else :
2018+ console .print (f"[yellow]⚠️ { provider } :[/yellow] { r .message } " )
2019+
2020+
2021+ # Validate with actual API call
2022+ if validate :
2023+ console .print ("\n [dim]Validating with API call...[/dim]" )
2024+ test_model = model or "gpt-4o-mini"
2025+ result = check_api_key_valid (test_model )
2026+
2027+ if result .status .value == "pass" :
2028+ console .print (f"[green]✅ API validation:[/green] { result .message } " )
2029+ else :
2030+ console .print (f"[red]❌ API validation:[/red] { result .message } " )
2031+ if result .details .get ("error" ):
2032+ console .print (f" Error: { result .details ['error' ][:100 ]} " )
2033+ all_pass = False
2034+
2035+ if not all_pass :
2036+ raise typer .Exit (1 )
2037+
2038+
2039+ @doctor_app .command ("env" )
2040+ def doctor_env ():
2041+ """Show environment configuration.
2042+
2043+ Displays:
2044+ - Python version
2045+ - Platform
2046+ - API key status
2047+ - Working directory
2048+
2049+ Examples:
2050+ praisonai browser doctor env
2051+ """
2052+ from .diagnostics import get_environment_info
2053+
2054+ console .print ("[bold]Environment Configuration[/bold]\n " )
2055+
2056+ result = get_environment_info ()
2057+ details = result .details
2058+
2059+ console .print (f"Python: { details .get ('python_version' , 'unknown' )} " )
2060+ console .print (f"Platform: { details .get ('platform' , 'unknown' )} " )
2061+ console .print (f"Home: { details .get ('home' , 'unknown' )} " )
2062+ console .print (f"CWD: { details .get ('cwd' , 'unknown' )} " )
2063+
2064+ console .print ("\n [bold]API Keys:[/bold]" )
2065+ api_keys = details .get ("api_keys_set" , {})
2066+ for key , is_set in api_keys .items ():
2067+ if key == "OPENAI_BASE_URL" :
2068+ console .print (f" { key } : { is_set } " )
2069+ elif is_set :
2070+ console .print (f" [green]✅ { key } [/green]: Set" )
2071+ else :
2072+ console .print (f" [yellow]⚠️ { key } [/yellow]: Not set" )
2073+
2074+
2075+ @doctor_app .command ("agent" )
2076+ def doctor_agent (
2077+ model : str = typer .Option ("gpt-4o-mini" , "--model" , "-m" , help = "LLM model to test" ),
2078+ ):
2079+ """Test agent LLM capability.
2080+
2081+ Sends a mock observation and verifies agent returns valid action.
2082+ This tests the full LLM call path.
2083+
2084+ Examples:
2085+ praisonai browser doctor agent
2086+ praisonai browser doctor agent --model gemini/gemini-2.0-flash
2087+ """
2088+ import asyncio
2089+ from .diagnostics import check_agent_llm , check_vision_capability
2090+
2091+ console .print (f"[bold]Agent LLM Test ({ model } )[/bold]\n " )
2092+
2093+ # Check vision capability
2094+ vision_result = check_vision_capability (model )
2095+ if vision_result .status .value == "pass" :
2096+ console .print (f"[green]✅ Vision:[/green] { vision_result .message } " )
2097+ else :
2098+ console .print (f"[yellow]⚠️ Vision:[/yellow] { vision_result .message } " )
2099+
2100+ # Test LLM call
2101+ console .print ("\n [dim]Testing LLM call...[/dim]" )
2102+ result = asyncio .run (check_agent_llm (model ))
2103+
2104+ if result .status .value == "pass" :
2105+ console .print (f"[green]✅ Agent:[/green] { result .message } " )
2106+ if result .details .get ("thought" ):
2107+ console .print (f" Thought: { result .details ['thought' ][:80 ]} ..." )
2108+ elif result .status .value == "fail" :
2109+ console .print (f"[red]❌ Agent:[/red] { result .message } " )
2110+ if result .details .get ("error" ):
2111+ console .print (f" Error: { result .details ['error' ][:150 ]} " )
2112+ raise typer .Exit (1 )
2113+ else :
2114+ console .print (f"[yellow]⚠️ Agent:[/yellow] { result .message } " )
2115+
2116+
2117+ @doctor_app .command ("flow" )
2118+ def doctor_flow (
2119+ bridge_port : int = typer .Option (8765 , "--bridge-port" , help = "Bridge server port" ),
2120+ chrome_port : int = typer .Option (9222 , "--chrome-port" , help = "Chrome debug port" ),
2121+ model : str = typer .Option ("gpt-4o-mini" , "--model" , "-m" , help = "LLM model" ),
2122+ skip_llm : bool = typer .Option (False , "--skip-llm" , help = "Skip LLM API test" ),
2123+ json_output : bool = typer .Option (False , "--json" , help = "Output as JSON" ),
2124+ ):
2125+ """Run full automation flow diagnostics.
2126+
2127+ Tests all components in sequence:
2128+ 1. Environment & API keys
2129+ 2. Bridge server connectivity
2130+ 3. Chrome CDP & extension
2131+ 4. Agent LLM capability
2132+
2133+ Examples:
2134+ praisonai browser doctor flow
2135+ praisonai browser doctor flow --json
2136+ praisonai browser doctor flow --skip-llm
2137+ """
2138+ import asyncio
2139+ import json as json_lib
2140+ from .diagnostics import run_all_diagnostics
2141+
2142+ if not json_output :
2143+ console .print ("[bold]Full Automation Flow Diagnostics[/bold]\n " )
2144+
2145+ results = asyncio .run (run_all_diagnostics (
2146+ bridge_port = bridge_port ,
2147+ chrome_port = chrome_port ,
2148+ model = model ,
2149+ skip_llm = skip_llm ,
2150+ ))
2151+
2152+ if json_output :
2153+ console .print (json_lib .dumps (results , indent = 2 ))
2154+ return
2155+
2156+ # Display results
2157+ for r in results ["results" ]:
2158+ status = r ["status" ]
2159+ name = r ["name" ]
2160+ message = r ["message" ]
2161+
2162+ if status == "pass" :
2163+ console .print (f"[green]✅ { name } :[/green] { message } " )
2164+ elif status == "fail" :
2165+ console .print (f"[red]❌ { name } :[/red] { message } " )
2166+ elif status == "warn" :
2167+ console .print (f"[yellow]⚠️ { name } :[/yellow] { message } " )
2168+ else :
2169+ console .print (f"[dim]⏭️ { name } :[/dim] { message } " )
2170+
2171+ # Summary
2172+ summary = results ["summary" ]
2173+ console .print (f"\n [bold]Summary:[/bold] { summary ['passed' ]} /{ summary ['total' ]} passed" )
2174+
2175+ if summary ["failed" ] > 0 :
2176+ console .print (f"[red] ❌ { summary ['failed' ]} failed[/red]" )
2177+ if summary ["warned" ] > 0 :
2178+ console .print (f"[yellow] ⚠️ { summary ['warned' ]} warnings[/yellow]" )
2179+
2180+ if not summary ["all_pass" ]:
2181+ raise typer .Exit (1 )
2182+
2183+
19392184@doctor_app .callback (invoke_without_command = True )
19402185def doctor_all (
19412186 ctx : typer .Context ,
19422187 json_output : bool = typer .Option (False , "--json" , help = "Output as JSON" ),
19432188):
1944- """Run all browser health checks."""
2189+ """Run all browser health checks.
2190+
2191+ Runs comprehensive diagnostics on all components.
2192+
2193+ Examples:
2194+ praisonai browser doctor
2195+ praisonai browser doctor --json
2196+ """
19452197 if ctx .invoked_subcommand is None :
1946- console .print ("[bold]Browser Health Check[/bold]\n " )
1947-
1948- # Run all checks with explicit defaults
1949- try :
1950- doctor_server (host = "localhost" , port = 8765 )
1951- except typer .Exit :
1952- pass
1953-
1954- console .print ()
1955-
1956- try :
1957- doctor_chrome (port = 9222 )
1958- except typer .Exit :
1959- pass
1960-
1961- console .print ()
1962-
1963- try :
1964- doctor_extension (port = 9222 )
1965- except typer .Exit :
1966- pass
1967-
1968- console .print ()
1969-
1970- try :
1971- doctor_db ()
1972- except typer .Exit :
1973- pass
2198+ # Use full flow diagnostics
2199+ doctor_flow (
2200+ bridge_port = 8765 ,
2201+ chrome_port = 9222 ,
2202+ model = "gpt-4o-mini" ,
2203+ skip_llm = True , # Skip by default for speed
2204+ json_output = json_output ,
2205+ )
19742206
19752207
19762208# ============================================================
0 commit comments