@@ -89,6 +89,7 @@ impl Toolset {
8989 browser_navigate_def( ) ,
9090 browser_click_def( ) ,
9191 browser_type_def( ) ,
92+ browser_press_key_def( ) ,
9293 browser_read_page_def( ) ,
9394 browser_screenshot_def( ) ,
9495 browser_close_def( ) ,
@@ -263,6 +264,12 @@ impl Toolset {
263264 serde_json:: from_str ( arguments_json) . context ( "parse browser_type arguments" ) ?;
264265 self . browser_type ( & args. selector , & args. text ) . await
265266 }
267+ "browser_press_key" => {
268+ let args: BrowserPressKeyArgs = serde_json:: from_str ( arguments_json)
269+ . context ( "parse browser_press_key arguments" ) ?;
270+ self . browser_press_key ( args. selector . as_deref ( ) , & args. key )
271+ . await
272+ }
266273 "browser_read_page" => {
267274 let _args: serde_json:: Value = serde_json:: from_str ( arguments_json)
268275 . context ( "parse browser_read_page arguments" ) ?;
@@ -1289,6 +1296,24 @@ impl Toolset {
12891296 Ok ( resp. into_tool_output ( ) ?)
12901297 }
12911298
1299+ async fn browser_press_key ( & self , selector : Option < & str > , key : & str ) -> anyhow:: Result < String > {
1300+ let mut guard = self . browser . lock ( ) . await ;
1301+ let session = guard
1302+ . as_mut ( )
1303+ . context ( "browser session not started; call browser_navigate first" ) ?;
1304+
1305+ let mut cmd = serde_json:: json!( {
1306+ "action" : "PressKey" ,
1307+ "key" : key,
1308+ } ) ;
1309+ if let Some ( sel) = selector {
1310+ cmd[ "selector" ] = serde_json:: Value :: String ( sel. to_string ( ) ) ;
1311+ }
1312+
1313+ let resp = session. send ( cmd) . await ?;
1314+ Ok ( resp. into_tool_output ( ) ?)
1315+ }
1316+
12921317 async fn browser_read_page ( & self ) -> anyhow:: Result < String > {
12931318 let mut guard = self . browser . lock ( ) . await ;
12941319 let session = guard
@@ -1737,6 +1762,13 @@ struct BrowserTypeArgs {
17371762 text : String ,
17381763}
17391764
1765+ #[ derive( Debug , serde:: Deserialize ) ]
1766+ struct BrowserPressKeyArgs {
1767+ key : String ,
1768+ #[ serde( default ) ]
1769+ selector : Option < String > ,
1770+ }
1771+
17401772#[ derive( Debug , serde:: Deserialize ) ]
17411773struct BrowserScreenshotArgs {
17421774 #[ serde( default ) ]
@@ -3184,6 +3216,26 @@ fn browser_type_def() -> ToolDefinition {
31843216 }
31853217}
31863218
3219+ fn browser_press_key_def ( ) -> ToolDefinition {
3220+ ToolDefinition {
3221+ kind : "function" . to_string ( ) ,
3222+ function : ToolFunctionDefinition {
3223+ name : "browser_press_key" . to_string ( ) ,
3224+ description : "Press a key in the browser (optionally on a target element)."
3225+ . to_string ( ) ,
3226+ parameters : serde_json:: json!( {
3227+ "type" : "object" ,
3228+ "properties" : {
3229+ "key" : { "type" : "string" , "description" : "Key to press (example: Enter, Escape, ArrowDown, Control+A)." } ,
3230+ "selector" : { "type" : "string" , "description" : "Optional CSS selector to target before pressing the key." }
3231+ } ,
3232+ "required" : [ "key" ] ,
3233+ "additionalProperties" : false
3234+ } ) ,
3235+ } ,
3236+ }
3237+ }
3238+
31873239fn browser_read_page_def ( ) -> ToolDefinition {
31883240 ToolDefinition {
31893241 kind : "function" . to_string ( ) ,
@@ -3332,6 +3384,7 @@ mod tests {
33323384 "browser_navigate" ,
33333385 "browser_click" ,
33343386 "browser_type" ,
3387+ "browser_press_key" ,
33353388 "browser_read_page" ,
33363389 "browser_screenshot" ,
33373390 "browser_close" ,
@@ -4005,6 +4058,22 @@ mod tests {
40054058 ) ;
40064059 }
40074060
4061+ #[ tokio:: test]
4062+ async fn browser_press_key_requires_session ( ) {
4063+ let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
4064+ let tools = Toolset :: new ( tmp. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
4065+
4066+ let err = tools
4067+ . call ( "browser_press_key" , r#"{ "key": "Enter" }"# )
4068+ . await
4069+ . unwrap_err ( ) ;
4070+ let msg = err. to_string ( ) ;
4071+ assert ! (
4072+ msg. contains( "browser_navigate" ) || msg. contains( "session" ) ,
4073+ "{msg}"
4074+ ) ;
4075+ }
4076+
40084077 #[ tokio:: test]
40094078 async fn browser_read_page_requires_session ( ) {
40104079 let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
@@ -4059,6 +4128,13 @@ mod tests {
40594128 . await
40604129 . unwrap ( ) ;
40614130
4131+ let out = tools
4132+ . call ( "browser_press_key" , r#"{ "key": "Enter" }"# )
4133+ . await
4134+ . unwrap ( ) ;
4135+ let v: serde_json:: Value = serde_json:: from_str ( & out) . unwrap ( ) ;
4136+ assert_eq ! ( v[ "key" ] , "Enter" ) ;
4137+
40624138 let page = tools. call ( "browser_read_page" , r#"{}"# ) . await . unwrap ( ) ;
40634139 let v: serde_json:: Value = serde_json:: from_str ( & page) . unwrap ( ) ;
40644140 assert_eq ! ( v[ "title" ] , "Stub" ) ;
@@ -4137,6 +4213,8 @@ for line in sys.stdin:
41374213 resp = {"success": True, "data": {"clicked": cmd.get("selector", "")}}
41384214 elif action == "Type":
41394215 resp = {"success": True, "data": {"typed": cmd.get("text", ""), "selector": cmd.get("selector", "")}}
4216+ elif action == "PressKey":
4217+ resp = {"success": True, "data": {"key": cmd.get("key", ""), "selector": cmd.get("selector", "")}}
41404218 elif action == "Close":
41414219 resp = {"success": True, "data": {"status": "closed"}}
41424220 sys.stdout.write(json.dumps(resp) + "\n")
0 commit comments