@@ -17,6 +17,8 @@ pub enum ParseError {
1717 context : String ,
1818 usage : & ' static str ,
1919 } ,
20+ /// Argument exists but has an invalid value
21+ InvalidValue { message : String , usage : & ' static str } ,
2022}
2123
2224impl ParseError {
@@ -41,6 +43,9 @@ impl ParseError {
4143 context, usage
4244 )
4345 }
46+ ParseError :: InvalidValue { message, usage } => {
47+ format ! ( "{}\n Usage: agent-browser {}" , message, usage)
48+ }
4449 }
4550 }
4651}
@@ -354,15 +359,48 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
354359
355360 // === Connect (CDP) ===
356361 "connect" => {
357- let port_str = rest. get ( 0 ) . ok_or_else ( || ParseError :: MissingArguments {
362+ let endpoint = rest. first ( ) . ok_or_else ( || ParseError :: MissingArguments {
358363 context : "connect" . to_string ( ) ,
359- usage : "connect <port>" ,
360- } ) ?;
361- let port: u16 = port_str. parse ( ) . map_err ( |_| ParseError :: MissingArguments {
362- context : format ! ( "connect: invalid port '{}'" , port_str) ,
363- usage : "connect <port>" ,
364+ usage : "connect <port|url>" ,
364365 } ) ?;
365- Ok ( json ! ( { "id" : id, "action" : "launch" , "cdpPort" : port } ) )
366+ // Check if it's a URL (ws://, wss://, http://, https://)
367+ if endpoint. starts_with ( "ws://" )
368+ || endpoint. starts_with ( "wss://" )
369+ || endpoint. starts_with ( "http://" )
370+ || endpoint. starts_with ( "https://" )
371+ {
372+ Ok ( json ! ( { "id" : id, "action" : "launch" , "cdpUrl" : endpoint } ) )
373+ } else {
374+ // It's a port number - validate and use cdpPort field
375+ let port: u16 = match endpoint. parse :: < u32 > ( ) {
376+ Ok ( p) if p == 0 => {
377+ return Err ( ParseError :: InvalidValue {
378+ message : "Invalid port: port must be greater than 0" . to_string ( ) ,
379+ usage : "connect <port|url>" ,
380+ } ) ;
381+ }
382+ Ok ( p) if p > 65535 => {
383+ return Err ( ParseError :: InvalidValue {
384+ message : format ! (
385+ "Invalid port: {} is out of range (valid range: 1-65535)" ,
386+ p
387+ ) ,
388+ usage : "connect <port|url>" ,
389+ } ) ;
390+ }
391+ Ok ( p) => p as u16 ,
392+ Err ( _) => {
393+ return Err ( ParseError :: InvalidValue {
394+ message : format ! (
395+ "Invalid value: '{}' is not a valid port number or URL" ,
396+ endpoint
397+ ) ,
398+ usage : "connect <port|url>" ,
399+ } ) ;
400+ }
401+ } ;
402+ Ok ( json ! ( { "id" : id, "action" : "launch" , "cdpPort" : port } ) )
403+ }
366404 }
367405
368406 // === Get ===
@@ -1713,4 +1751,99 @@ mod tests {
17131751 assert_eq ! ( cmd[ "index" ] , 2 ) ;
17141752 assert ! ( cmd. get( "value" ) . is_none( ) ) ;
17151753 }
1754+
1755+ // === Connect (CDP) tests ===
1756+
1757+ #[ test]
1758+ fn test_connect_with_port ( ) {
1759+ let cmd = parse_command ( & args ( "connect 9222" ) , & default_flags ( ) ) . unwrap ( ) ;
1760+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1761+ assert_eq ! ( cmd[ "cdpPort" ] , 9222 ) ;
1762+ assert ! ( cmd. get( "cdpUrl" ) . is_none( ) ) ;
1763+ }
1764+
1765+ #[ test]
1766+ fn test_connect_with_ws_url ( ) {
1767+ let input: Vec < String > = vec ! [
1768+ "connect" . to_string( ) ,
1769+ "ws://localhost:9222/devtools/browser/abc123" . to_string( ) ,
1770+ ] ;
1771+ let cmd = parse_command ( & input, & default_flags ( ) ) . unwrap ( ) ;
1772+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1773+ assert_eq ! ( cmd[ "cdpUrl" ] , "ws://localhost:9222/devtools/browser/abc123" ) ;
1774+ assert ! ( cmd. get( "cdpPort" ) . is_none( ) ) ;
1775+ }
1776+
1777+ #[ test]
1778+ fn test_connect_with_wss_url ( ) {
1779+ let input: Vec < String > = vec ! [
1780+ "connect" . to_string( ) ,
1781+ "wss://remote-browser.example.com/cdp?token=xyz" . to_string( ) ,
1782+ ] ;
1783+ let cmd = parse_command ( & input, & default_flags ( ) ) . unwrap ( ) ;
1784+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1785+ assert_eq ! ( cmd[ "cdpUrl" ] , "wss://remote-browser.example.com/cdp?token=xyz" ) ;
1786+ assert ! ( cmd. get( "cdpPort" ) . is_none( ) ) ;
1787+ }
1788+
1789+ #[ test]
1790+ fn test_connect_with_http_url ( ) {
1791+ let input: Vec < String > = vec ! [
1792+ "connect" . to_string( ) ,
1793+ "http://localhost:9222" . to_string( ) ,
1794+ ] ;
1795+ let cmd = parse_command ( & input, & default_flags ( ) ) . unwrap ( ) ;
1796+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1797+ assert_eq ! ( cmd[ "cdpUrl" ] , "http://localhost:9222" ) ;
1798+ assert ! ( cmd. get( "cdpPort" ) . is_none( ) ) ;
1799+ }
1800+
1801+ #[ test]
1802+ fn test_connect_missing_argument ( ) {
1803+ let result = parse_command ( & args ( "connect" ) , & default_flags ( ) ) ;
1804+ assert ! ( result. is_err( ) ) ;
1805+ assert ! ( matches!( result. unwrap_err( ) , ParseError :: MissingArguments { .. } ) ) ;
1806+ }
1807+
1808+ #[ test]
1809+ fn test_connect_invalid_port ( ) {
1810+ let result = parse_command ( & args ( "connect notanumber" ) , & default_flags ( ) ) ;
1811+ assert ! ( result. is_err( ) ) ;
1812+ let err = result. unwrap_err ( ) ;
1813+ assert ! ( matches!( err, ParseError :: InvalidValue { .. } ) ) ;
1814+ assert ! ( err. format( ) . contains( "not a valid port number or URL" ) ) ;
1815+ }
1816+
1817+ #[ test]
1818+ fn test_connect_port_zero ( ) {
1819+ let result = parse_command ( & args ( "connect 0" ) , & default_flags ( ) ) ;
1820+ assert ! ( result. is_err( ) ) ;
1821+ let err = result. unwrap_err ( ) ;
1822+ assert ! ( matches!( err, ParseError :: InvalidValue { .. } ) ) ;
1823+ assert ! ( err. format( ) . contains( "port must be greater than 0" ) ) ;
1824+ }
1825+
1826+ #[ test]
1827+ fn test_connect_port_out_of_range ( ) {
1828+ let result = parse_command ( & args ( "connect 65536" ) , & default_flags ( ) ) ;
1829+ assert ! ( result. is_err( ) ) ;
1830+ let err = result. unwrap_err ( ) ;
1831+ assert ! ( matches!( err, ParseError :: InvalidValue { .. } ) ) ;
1832+ assert ! ( err. format( ) . contains( "out of range" ) ) ;
1833+ assert ! ( err. format( ) . contains( "1-65535" ) ) ;
1834+ }
1835+
1836+ #[ test]
1837+ fn test_connect_port_max_valid ( ) {
1838+ let cmd = parse_command ( & args ( "connect 65535" ) , & default_flags ( ) ) . unwrap ( ) ;
1839+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1840+ assert_eq ! ( cmd[ "cdpPort" ] , 65535 ) ;
1841+ }
1842+
1843+ #[ test]
1844+ fn test_connect_port_min_valid ( ) {
1845+ let cmd = parse_command ( & args ( "connect 1" ) , & default_flags ( ) ) . unwrap ( ) ;
1846+ assert_eq ! ( cmd[ "action" ] , "launch" ) ;
1847+ assert_eq ! ( cmd[ "cdpPort" ] , 1 ) ;
1848+ }
17161849}
0 commit comments