diff --git a/.changeset/brave-browsers-migrate.md b/.changeset/brave-browsers-migrate.md new file mode 100644 index 0000000000..608dbe48d8 --- /dev/null +++ b/.changeset/brave-browsers-migrate.md @@ -0,0 +1,5 @@ +--- +"browse": minor +--- + +migrate the Browse CLI runtime to Stagehand V4 and remove the `--return-xpath` option from coordinate actions diff --git a/packages/cli/README.md b/packages/cli/README.md index 416dc1b3a9..58e8b5b142 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -111,7 +111,7 @@ browse highlight @0-12 # Highlight an element (--duratio ### Mouse (raw coordinates) -Use these when you need pixel coordinates instead of a ref. Add `--return-xpath` to get the XPath under the cursor. +Use these when you need pixel coordinates instead of a ref. ```bash browse mouse click 240 320 # Click coordinates (--button, --click-count) diff --git a/packages/cli/src/commands/mouse/click.ts b/packages/cli/src/commands/mouse/click.ts index ad0f21d35b..32a981c6bf 100644 --- a/packages/cli/src/commands/mouse/click.ts +++ b/packages/cli/src/commands/mouse/click.ts @@ -15,7 +15,7 @@ export default class MouseClick extends BrowseCommand { static override examples = [ "browse mouse click 240 320", "browse mouse click 240 320 --button right", - "browse mouse click 240 320 --click-count 2 --return-xpath", + "browse mouse click 240 320 --click-count 2", ]; static override args = { @@ -31,10 +31,6 @@ export default class MouseClick extends BrowseCommand { description: "Number of clicks to send.", helpValue: "", }), - "return-xpath": Flags.boolean({ - description: - "Include the XPath under the coordinate when the driver can return it.", - }), }; async run(): Promise { @@ -44,7 +40,6 @@ export default class MouseClick extends BrowseCommand { { button: flags.button, clickCount: flags["click-count"], - returnXPath: flags["return-xpath"], x: parseNumber(args.x, "x"), y: parseNumber(args.y, "y"), }, diff --git a/packages/cli/src/commands/mouse/drag.ts b/packages/cli/src/commands/mouse/drag.ts index d2a57cff0d..a056abf05f 100644 --- a/packages/cli/src/commands/mouse/drag.ts +++ b/packages/cli/src/commands/mouse/drag.ts @@ -15,7 +15,6 @@ export default class MouseDrag extends BrowseCommand { static override examples = [ "browse mouse drag 100 100 400 400", "browse mouse drag 100 100 400 400 --steps 20 --delay 10", - "browse mouse drag 100 100 400 400 --return-xpath", ]; static override args = { @@ -45,10 +44,6 @@ export default class MouseDrag extends BrowseCommand { description: "Delay between drag steps in milliseconds.", helpValue: "", }), - "return-xpath": Flags.boolean({ - description: - "Include the XPath under the start/end coordinates when the driver can return it.", - }), steps: Flags.integer({ default: 10, description: "Number of intermediate drag steps.", @@ -65,7 +60,6 @@ export default class MouseDrag extends BrowseCommand { delay: flags.delay, fromX: parseNumber(args.fromX, "fromX"), fromY: parseNumber(args.fromY, "fromY"), - returnXPath: flags["return-xpath"], steps: flags.steps, toX: parseNumber(args.toX, "toX"), toY: parseNumber(args.toY, "toY"), diff --git a/packages/cli/src/commands/mouse/hover.ts b/packages/cli/src/commands/mouse/hover.ts index 16c7d537bb..ed7fd2d03a 100644 --- a/packages/cli/src/commands/mouse/hover.ts +++ b/packages/cli/src/commands/mouse/hover.ts @@ -1,4 +1,4 @@ -import { Args, Flags } from "@oclif/core"; +import { Args } from "@oclif/core"; import { BrowseCommand } from "../../base.js"; import { @@ -13,7 +13,6 @@ export default class MouseHover extends BrowseCommand { static override examples = [ "browse mouse hover 240 320", - "browse mouse hover 240 320 --return-xpath", "browse mouse hover 240 320 --session research", ]; @@ -24,10 +23,6 @@ export default class MouseHover extends BrowseCommand { static override flags = { ...driverCommandFlags, - "return-xpath": Flags.boolean({ - description: - "Include the XPath under the coordinate when the driver can return it.", - }), }; async run(): Promise { @@ -35,7 +30,6 @@ export default class MouseHover extends BrowseCommand { await runDriverCommandFromFlags( "mouse.hover", { - returnXPath: flags["return-xpath"], x: parseNumber(args.x, "x"), y: parseNumber(args.y, "y"), }, diff --git a/packages/cli/src/commands/mouse/scroll.ts b/packages/cli/src/commands/mouse/scroll.ts index 882d963826..9a135c7833 100644 --- a/packages/cli/src/commands/mouse/scroll.ts +++ b/packages/cli/src/commands/mouse/scroll.ts @@ -1,4 +1,4 @@ -import { Args, Flags } from "@oclif/core"; +import { Args } from "@oclif/core"; import { BrowseCommand } from "../../base.js"; import { @@ -14,7 +14,6 @@ export default class MouseScroll extends BrowseCommand { static override examples = [ "browse mouse scroll 400 500 0 600", "browse mouse scroll 400 500 0 -600", - "browse mouse scroll 400 500 0 600 --return-xpath", ]; static override args = { @@ -32,10 +31,6 @@ export default class MouseScroll extends BrowseCommand { static override flags = { ...driverCommandFlags, - "return-xpath": Flags.boolean({ - description: - "Include the XPath under the coordinate when the driver can return it.", - }), }; async run(): Promise { @@ -45,7 +40,6 @@ export default class MouseScroll extends BrowseCommand { { deltaX: parseNumber(args.deltaX, "deltaX"), deltaY: parseNumber(args.deltaY, "deltaY"), - returnXPath: flags["return-xpath"], x: parseNumber(args.x, "x"), y: parseNumber(args.y, "y"), }, diff --git a/packages/cli/src/lib/driver/commands/mouse.ts b/packages/cli/src/lib/driver/commands/mouse.ts index ee03fa8548..9097ac8eaf 100644 --- a/packages/cli/src/lib/driver/commands/mouse.ts +++ b/packages/cli/src/lib/driver/commands/mouse.ts @@ -8,16 +8,15 @@ const ButtonSchema = z.enum(["left", "right", "middle"]).optional(); export const mouseHandlers: DriverCommandHandlers = { async "mouse.click"(manager, params) { - const { button, clickCount, returnXPath, x, y } = z + const { button, clickCount, x, y } = z .object({ button: ButtonSchema, clickCount: z.number().int().positive().optional(), - returnXPath: z.boolean().optional(), x: z.number(), y: z.number(), }) + .strict() .parse(params); - assertXPathUnavailable(returnXPath); const page = await manager.activePage(); await positionCursorOverlay(manager, page, x, y); await page.click(x, y, { @@ -28,14 +27,13 @@ export const mouseHandlers: DriverCommandHandlers = { }, async "mouse.hover"(manager, params) { - const { returnXPath, x, y } = z + const { x, y } = z .object({ - returnXPath: z.boolean().optional(), x: z.number(), y: z.number(), }) + .strict() .parse(params); - assertXPathUnavailable(returnXPath); const page = await manager.activePage(); await positionCursorOverlay(manager, page, x, y); await page.hover(x, y); @@ -43,16 +41,15 @@ export const mouseHandlers: DriverCommandHandlers = { }, async "mouse.scroll"(manager, params) { - const { deltaX, deltaY, returnXPath, x, y } = z + const { deltaX, deltaY, x, y } = z .object({ deltaX: z.number(), deltaY: z.number(), - returnXPath: z.boolean().optional(), x: z.number(), y: z.number(), }) + .strict() .parse(params); - assertXPathUnavailable(returnXPath); const page = await manager.activePage(); await positionCursorOverlay(manager, page, x, y); await page.scroll(x, y, deltaX, deltaY); @@ -60,19 +57,18 @@ export const mouseHandlers: DriverCommandHandlers = { }, async "mouse.drag"(manager, params) { - const { button, delay, fromX, fromY, returnXPath, steps, toX, toY } = z + const { button, delay, fromX, fromY, steps, toX, toY } = z .object({ button: ButtonSchema, delay: z.number().int().nonnegative().optional(), fromX: z.number(), fromY: z.number(), - returnXPath: z.boolean().optional(), steps: z.number().int().positive().optional(), toX: z.number(), toY: z.number(), }) + .strict() .parse(params); - assertXPathUnavailable(returnXPath); const page = await manager.activePage(); await positionCursorOverlay(manager, page, fromX, fromY); await page.dragAndDrop(fromX, fromY, toX, toY, { @@ -100,9 +96,3 @@ async function positionCursorOverlay( // Best-effort parity with V3's cursor updates. } } - -function assertXPathUnavailable(returnXPath: boolean | undefined): void { - if (returnXPath) { - throw new Error("Coordinate XPath lookup is not exposed by Stagehand V4"); - } -} diff --git a/packages/cli/tests/driver-commands.test.ts b/packages/cli/tests/driver-commands.test.ts index 31fa2051a2..1991b3c844 100644 --- a/packages/cli/tests/driver-commands.test.ts +++ b/packages/cli/tests/driver-commands.test.ts @@ -503,23 +503,6 @@ describe("driver commands", () => { ).rejects.toBe(actionError); }); - it("fails explicitly for the V4 coordinate XPath capability", async () => { - const manager = {} as Parameters< - NonNullable<(typeof mouseHandlers)["mouse.click"]> - >[0]; - - for (const [command, params] of [ - ["mouse.click", { returnXPath: true, x: 1, y: 2 }], - ["mouse.hover", { returnXPath: true, x: 1, y: 2 }], - ["mouse.scroll", { deltaX: 0, deltaY: 1, returnXPath: true, x: 1, y: 2 }], - ["mouse.drag", { fromX: 1, fromY: 2, returnXPath: true, toX: 3, toY: 4 }], - ] as const) { - await expect(mouseHandlers[command]!(manager, params)).rejects.toThrow( - "Coordinate XPath lookup is not exposed by Stagehand V4", - ); - } - }); - it("enables sidecar network capture", async () => { const page = {}; const network = {