2025-03-21 10:58:58 -07:00
/ * *
* Copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
import { z } from 'zod' ;
2025-04-30 23:06:56 +02:00
import { defineTool } from './tool.js' ;
import * as javascript from '../javascript.js' ;
2025-05-12 16:42:47 -07:00
import { generateLocator } from './utils.js' ;
2025-04-16 19:36:48 -07:00
2025-04-22 13:24:38 +02:00
const snapshot = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-21 10:58:58 -07:00
schema : {
name : 'browser_snapshot' ,
2025-05-05 17:38:22 -07:00
title : 'Page snapshot' ,
2025-03-21 10:58:58 -07:00
description : 'Capture accessibility snapshot of the current page, this is better than screenshot' ,
2025-04-22 13:24:38 +02:00
inputSchema : z.object ( { } ) ,
2025-05-05 17:38:22 -07:00
type : 'readOnly' ,
2025-03-21 10:58:58 -07:00
} ,
handle : async context = > {
2025-04-16 19:36:48 -07:00
await context . ensureTab ( ) ;
return {
code : [ ` // <internal code to capture accessibility snapshot> ` ] ,
captureSnapshot : true ,
waitForNetwork : false ,
} ;
2025-03-21 10:58:58 -07:00
} ,
2025-04-22 13:24:38 +02:00
} ) ;
2025-03-21 10:58:58 -07:00
const elementSchema = z . object ( {
element : z.string ( ) . describe ( 'Human-readable element description used to obtain permission to interact with the element' ) ,
ref : z.string ( ) . describe ( 'Exact target element reference from the page snapshot' ) ,
} ) ;
2025-04-22 13:24:38 +02:00
const click = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-21 10:58:58 -07:00
schema : {
name : 'browser_click' ,
2025-05-05 17:38:22 -07:00
title : 'Click' ,
2025-03-21 10:58:58 -07:00
description : 'Perform click on a web page' ,
2025-04-22 13:24:38 +02:00
inputSchema : elementSchema ,
2025-05-05 17:38:22 -07:00
type : 'destructive' ,
2025-03-21 10:58:58 -07:00
} ,
handle : async ( context , params ) = > {
2025-04-16 19:36:48 -07:00
const tab = context . currentTabOrDie ( ) ;
2025-05-24 11:44:57 -07:00
const locator = tab . snapshotOrDie ( ) . refLocator ( params ) ;
2025-04-16 19:36:48 -07:00
const code = [
2025-04-22 13:24:38 +02:00
` // Click ${ params . element } ` ,
2025-04-16 19:36:48 -07:00
` await page. ${ await generateLocator ( locator ) } .click(); `
] ;
return {
code ,
2025-04-17 00:58:02 -07:00
action : ( ) = > locator . click ( ) ,
2025-04-16 19:36:48 -07:00
captureSnapshot : true ,
waitForNetwork : true ,
} ;
2025-03-21 10:58:58 -07:00
} ,
} ) ;
2025-04-22 13:24:38 +02:00
const drag = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-21 10:58:58 -07:00
schema : {
name : 'browser_drag' ,
2025-05-05 17:38:22 -07:00
title : 'Drag mouse' ,
2025-03-21 10:58:58 -07:00
description : 'Perform drag and drop between two elements' ,
2025-04-22 13:24:38 +02:00
inputSchema : z.object ( {
startElement : z.string ( ) . describe ( 'Human-readable source element description used to obtain the permission to interact with the element' ) ,
startRef : z.string ( ) . describe ( 'Exact source element reference from the page snapshot' ) ,
endElement : z.string ( ) . describe ( 'Human-readable target element description used to obtain the permission to interact with the element' ) ,
endRef : z.string ( ) . describe ( 'Exact target element reference from the page snapshot' ) ,
} ) ,
2025-05-05 17:38:22 -07:00
type : 'destructive' ,
2025-03-21 10:58:58 -07:00
} ,
handle : async ( context , params ) = > {
2025-04-16 19:36:48 -07:00
const snapshot = context . currentTabOrDie ( ) . snapshotOrDie ( ) ;
2025-05-24 11:44:57 -07:00
const startLocator = snapshot . refLocator ( { ref : params.startRef , element : params.startElement } ) ;
const endLocator = snapshot . refLocator ( { ref : params.endRef , element : params.endElement } ) ;
2025-04-16 19:36:48 -07:00
const code = [
2025-04-22 13:24:38 +02:00
` // Drag ${ params . startElement } to ${ params . endElement } ` ,
2025-04-16 19:36:48 -07:00
` await page. ${ await generateLocator ( startLocator ) } .dragTo(page. ${ await generateLocator ( endLocator ) } ); `
] ;
return {
code ,
2025-04-17 00:58:02 -07:00
action : ( ) = > startLocator . dragTo ( endLocator ) ,
2025-04-16 19:36:48 -07:00
captureSnapshot : true ,
waitForNetwork : true ,
} ;
2025-03-21 10:58:58 -07:00
} ,
2025-04-22 13:24:38 +02:00
} ) ;
2025-03-21 10:58:58 -07:00
2025-04-22 13:24:38 +02:00
const hover = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-21 10:58:58 -07:00
schema : {
name : 'browser_hover' ,
2025-05-05 17:38:22 -07:00
title : 'Hover mouse' ,
2025-03-21 10:58:58 -07:00
description : 'Hover over element on page' ,
2025-04-22 13:24:38 +02:00
inputSchema : elementSchema ,
2025-05-05 17:38:22 -07:00
type : 'readOnly' ,
2025-03-21 10:58:58 -07:00
} ,
handle : async ( context , params ) = > {
2025-04-16 19:36:48 -07:00
const snapshot = context . currentTabOrDie ( ) . snapshotOrDie ( ) ;
2025-05-24 11:44:57 -07:00
const locator = snapshot . refLocator ( params ) ;
2025-04-16 19:36:48 -07:00
const code = [
2025-04-22 13:24:38 +02:00
` // Hover over ${ params . element } ` ,
2025-04-16 19:36:48 -07:00
` await page. ${ await generateLocator ( locator ) } .hover(); `
] ;
return {
code ,
2025-04-17 00:58:02 -07:00
action : ( ) = > locator . hover ( ) ,
2025-04-16 19:36:48 -07:00
captureSnapshot : true ,
waitForNetwork : true ,
} ;
2025-03-21 10:58:58 -07:00
} ,
2025-04-22 13:24:38 +02:00
} ) ;
2025-03-21 10:58:58 -07:00
const typeSchema = elementSchema . extend ( {
text : z.string ( ) . describe ( 'Text to type into the element' ) ,
2025-04-02 14:36:30 -07:00
submit : z.boolean ( ) . optional ( ) . describe ( 'Whether to submit entered text (press Enter after)' ) ,
2025-04-02 17:26:45 -07:00
slowly : z.boolean ( ) . optional ( ) . describe ( 'Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.' ) ,
2025-03-21 10:58:58 -07:00
} ) ;
2025-04-22 13:24:38 +02:00
const type = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-21 10:58:58 -07:00
schema : {
name : 'browser_type' ,
2025-05-05 17:38:22 -07:00
title : 'Type text' ,
2025-03-21 10:58:58 -07:00
description : 'Type text into editable element' ,
2025-04-22 13:24:38 +02:00
inputSchema : typeSchema ,
2025-05-05 17:38:22 -07:00
type : 'destructive' ,
2025-03-21 10:58:58 -07:00
} ,
handle : async ( context , params ) = > {
2025-04-16 19:36:48 -07:00
const snapshot = context . currentTabOrDie ( ) . snapshotOrDie ( ) ;
2025-05-24 11:44:57 -07:00
const locator = snapshot . refLocator ( params ) ;
2025-04-16 19:36:48 -07:00
const code : string [ ] = [ ] ;
const steps : ( ( ) = > Promise < void > ) [ ] = [ ] ;
2025-04-22 13:24:38 +02:00
if ( params . slowly ) {
code . push ( ` // Press " ${ params . text } " sequentially into " ${ params . element } " ` ) ;
code . push ( ` await page. ${ await generateLocator ( locator ) } .pressSequentially( ${ javascript . quote ( params . text ) } ); ` ) ;
steps . push ( ( ) = > locator . pressSequentially ( params . text ) ) ;
2025-04-16 19:36:48 -07:00
} else {
2025-04-22 13:24:38 +02:00
code . push ( ` // Fill " ${ params . text } " into " ${ params . element } " ` ) ;
code . push ( ` await page. ${ await generateLocator ( locator ) } .fill( ${ javascript . quote ( params . text ) } ); ` ) ;
steps . push ( ( ) = > locator . fill ( params . text ) ) ;
2025-04-16 19:36:48 -07:00
}
2025-04-22 13:24:38 +02:00
if ( params . submit ) {
2025-04-16 19:36:48 -07:00
code . push ( ` // Submit text ` ) ;
code . push ( ` await page. ${ await generateLocator ( locator ) } .press('Enter'); ` ) ;
steps . push ( ( ) = > locator . press ( 'Enter' ) ) ;
}
return {
code ,
2025-04-17 00:58:02 -07:00
action : ( ) = > steps . reduce ( ( acc , step ) = > acc . then ( step ) , Promise . resolve ( ) ) ,
2025-04-16 19:36:48 -07:00
captureSnapshot : true ,
waitForNetwork : true ,
} ;
2025-03-21 10:58:58 -07:00
} ,
2025-04-22 13:24:38 +02:00
} ) ;
2025-03-21 10:58:58 -07:00
2025-03-26 13:53:56 +09:00
const selectOptionSchema = elementSchema . extend ( {
values : z.array ( z . string ( ) ) . describe ( 'Array of values to select in the dropdown. This can be a single value or multiple values.' ) ,
} ) ;
2025-04-22 13:24:38 +02:00
const selectOption = defineTool ( {
2025-04-04 17:14:30 -07:00
capability : 'core' ,
2025-03-26 13:53:56 +09:00
schema : {
name : 'browser_select_option' ,
2025-05-05 17:38:22 -07:00
title : 'Select option' ,
2025-03-26 13:53:56 +09:00
description : 'Select an option in a dropdown' ,
2025-04-22 13:24:38 +02:00
inputSchema : selectOptionSchema ,
2025-05-05 17:38:22 -07:00
type : 'destructive' ,
2025-03-26 13:53:56 +09:00
} ,
handle : async ( context , params ) = > {
2025-04-16 19:36:48 -07:00
const snapshot = context . currentTabOrDie ( ) . snapshotOrDie ( ) ;
2025-05-24 11:44:57 -07:00
const locator = snapshot . refLocator ( params ) ;
2025-04-16 19:36:48 -07:00
const code = [
2025-04-22 13:24:38 +02:00
` // Select options [ ${ params . values . join ( ', ' ) } ] in ${ params . element } ` ,
` await page. ${ await generateLocator ( locator ) } .selectOption( ${ javascript . formatObject ( params . values ) } ); `
2025-04-16 19:36:48 -07:00
] ;
return {
code ,
2025-04-22 13:24:38 +02:00
action : ( ) = > locator . selectOption ( params . values ) . then ( ( ) = > { } ) ,
2025-04-16 19:36:48 -07:00
captureSnapshot : true ,
waitForNetwork : true ,
} ;
2025-03-26 13:53:56 +09:00
} ,
2025-04-22 13:24:38 +02:00
} ) ;
2025-03-26 13:53:56 +09:00
2025-04-04 15:22:00 -07:00
export default [
snapshot ,
click ,
drag ,
hover ,
type ,
selectOption ,
] ;