diff --git a/packages/protocol/schema-registry.ts b/packages/protocol/schema-registry.ts index e2d91a3cd..7156374d9 100644 --- a/packages/protocol/schema-registry.ts +++ b/packages/protocol/schema-registry.ts @@ -7,6 +7,7 @@ import { } from "./json-rpc/schemas.ts"; import { wireSchema } from "./json-rpc/wire-casing.ts"; import { + AcknowledgementResultSchema, ActResultSchema, BrowserGetVersionResultSchema, ContextActivePageResultSchema, @@ -20,7 +21,6 @@ import { ContextClipboardReadTextParamsSchema, ContextClipboardReadTextResultSchema, ContextClipboardWriteTextParamsSchema, - ContextCloseResultSchema, ContextCookiesParamsSchema, ContextCookiesResultSchema, ContextGetDomainPolicyResultSchema, @@ -29,39 +29,30 @@ import { ContextSetActivePageParamsSchema, ContextSetDomainPolicyParamsSchema, ContextSetExtraHTTPHeadersParamsSchema, - ContextVoidResultSchema, EmptyParamsSchema, ExtractResultSchema, LocatorClickParamsSchema, - LocatorClickResultSchema, LocatorCentroidResultSchema, LocatorCountResultSchema, LocatorDescriptorSchema, LocatorFillParamsSchema, - LocatorFillResultSchema, LocatorHighlightParamsSchema, - LocatorHighlightResultSchema, - LocatorHoverResultSchema, LocatorInnerHtmlResultSchema, LocatorInnerTextResultSchema, LocatorInputValueResultSchema, LocatorIsCheckedResultSchema, LocatorIsVisibleResultSchema, LocatorScrollToParamsSchema, - LocatorScrollToResultSchema, LocatorSelectOptionParamsSchema, LocatorSelectOptionResultSchema, LocatorSendClickEventParamsSchema, - LocatorSendClickEventResultSchema, LocatorTextContentResultSchema, LocatorTypeParamsSchema, - LocatorTypeResultSchema, LLMGenerateParamsSchema, LLMGenerateResultSchema, ObserveResultSchema, PageAddInitScriptParamsSchema, PageClickParamsSchema, - PageCloseResultSchema, PageCoordinateResultSchema, PageDragAndDropParamsSchema, PageDragAndDropResultSchema, @@ -84,7 +75,6 @@ import { PageTitleResultSchema, PageTypeParamsSchema, PageUrlResultSchema, - PageVoidResultSchema, PageWaitForLoadStateParamsSchema, PageWaitForSelectorParamsSchema, PageWaitForSelectorResultSchema, @@ -93,7 +83,6 @@ import { RuntimeConfigureResultSchema, RuntimeLoopbackStatusResultSchema, StagehandActParamsSchema, - StagehandCloseResultSchema, StagehandExtractParamsSchema, StagehandInitParamsSchema, StagehandInitResultSchema, @@ -134,7 +123,7 @@ export const StagehandMethods = { stagehandClose: { name: "stagehand.close", params: EmptyParamsSchema, - result: StagehandCloseResultSchema, + result: AcknowledgementResultSchema, }, stagehandAct: { name: "stagehand.act", @@ -185,22 +174,22 @@ export const StagehandMethods = { contextSetActivePage: { name: "context.set_active_page", params: ContextSetActivePageParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClose: { name: "context.close", params: EmptyParamsSchema, - result: ContextCloseResultSchema, + result: AcknowledgementResultSchema, }, contextAddInitScript: { name: "context.add_init_script", params: ContextAddInitScriptParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextSetExtraHTTPHeaders: { name: "context.set_extra_http_headers", params: ContextSetExtraHTTPHeadersParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, paramsWire: { opaqueKeys: ["headers"] }, }, contextGetDomainPolicy: { @@ -211,7 +200,7 @@ export const StagehandMethods = { contextSetDomainPolicy: { name: "context.set_domain_policy", params: ContextSetDomainPolicyParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextCookies: { name: "context.cookies", @@ -221,12 +210,12 @@ export const StagehandMethods = { contextAddCookies: { name: "context.add_cookies", params: ContextAddCookiesParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClearCookies: { name: "context.clear_cookies", params: ContextClearCookiesParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClipboardReadText: { name: "context.clipboard_read_text", @@ -236,32 +225,36 @@ export const StagehandMethods = { contextClipboardWriteText: { name: "context.clipboard_write_text", params: ContextClipboardWriteTextParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClipboardClear: { name: "context.clipboard_clear", params: ContextClipboardClearParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClipboardPaste: { name: "context.clipboard_paste", params: ContextClipboardPasteParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClipboardCopy: { name: "context.clipboard_copy", params: ContextClipboardCopyParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, contextClipboardCut: { name: "context.clipboard_cut", params: ContextClipboardCutParamsSchema, - result: ContextVoidResultSchema, + result: AcknowledgementResultSchema, }, pageGoto: { name: "page.goto", params: PageGotoParamsSchema, result: PageRefSchema }, pageUrl: { name: "page.url", params: PageIdParamsSchema, result: PageUrlResultSchema }, pageTitle: { name: "page.title", params: PageIdParamsSchema, result: PageTitleResultSchema }, - pageClose: { name: "page.close", params: PageIdParamsSchema, result: PageCloseResultSchema }, + pageClose: { + name: "page.close", + params: PageIdParamsSchema, + result: AcknowledgementResultSchema, + }, pageReload: { name: "page.reload", params: PageReloadParamsSchema, result: PageRefSchema }, pageGoBack: { name: "page.go_back", params: PageGoBackParamsSchema, result: PageRefSchema }, pageGoForward: { @@ -289,11 +282,15 @@ export const StagehandMethods = { params: PageDragAndDropParamsSchema, result: PageDragAndDropResultSchema, }, - pageType: { name: "page.type", params: PageTypeParamsSchema, result: PageVoidResultSchema }, + pageType: { + name: "page.type", + params: PageTypeParamsSchema, + result: AcknowledgementResultSchema, + }, pageKeyPress: { name: "page.key_press", params: PageKeyPressParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, }, pageEvaluate: { name: "page.evaluate", @@ -304,12 +301,12 @@ export const StagehandMethods = { pageAddInitScript: { name: "page.add_init_script", params: PageAddInitScriptParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, }, pageSetExtraHTTPHeaders: { name: "page.set_extra_http_headers", params: PageSetExtraHTTPHeadersParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, paramsWire: { opaqueKeys: ["headers"] }, }, pageScreenshot: { @@ -326,17 +323,17 @@ export const StagehandMethods = { pageSetViewportSize: { name: "page.set_viewport_size", params: PageSetViewportSizeParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, }, pageWaitForLoadState: { name: "page.wait_for_load_state", params: PageWaitForLoadStateParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, }, pageWaitForTimeout: { name: "page.wait_for_timeout", params: PageWaitForTimeoutParamsSchema, - result: PageVoidResultSchema, + result: AcknowledgementResultSchema, }, pageWaitForSelector: { name: "page.wait_for_selector", @@ -346,17 +343,17 @@ export const StagehandMethods = { locatorClick: { name: "locator.click", params: LocatorClickParamsSchema, - result: LocatorClickResultSchema, + result: AcknowledgementResultSchema, }, locatorFill: { name: "locator.fill", params: LocatorFillParamsSchema, - result: LocatorFillResultSchema, + result: AcknowledgementResultSchema, }, locatorHover: { name: "locator.hover", params: LocatorDescriptorSchema, - result: LocatorHoverResultSchema, + result: AcknowledgementResultSchema, }, locatorCount: { name: "locator.count", @@ -396,7 +393,7 @@ export const StagehandMethods = { locatorScrollTo: { name: "locator.scroll_to", params: LocatorScrollToParamsSchema, - result: LocatorScrollToResultSchema, + result: AcknowledgementResultSchema, }, locatorCentroid: { name: "locator.centroid", @@ -406,17 +403,17 @@ export const StagehandMethods = { locatorHighlight: { name: "locator.highlight", params: LocatorHighlightParamsSchema, - result: LocatorHighlightResultSchema, + result: AcknowledgementResultSchema, }, locatorSendClickEvent: { name: "locator.send_click_event", params: LocatorSendClickEventParamsSchema, - result: LocatorSendClickEventResultSchema, + result: AcknowledgementResultSchema, }, locatorType: { name: "locator.type", params: LocatorTypeParamsSchema, - result: LocatorTypeResultSchema, + result: AcknowledgementResultSchema, }, locatorSelectOption: { name: "locator.select_option", diff --git a/packages/protocol/schemas.ts b/packages/protocol/schemas.ts index d709fda25..33418eb12 100644 --- a/packages/protocol/schemas.ts +++ b/packages/protocol/schemas.ts @@ -1277,6 +1277,10 @@ export const ObserveResultSchema = z export const EmptyParamsSchema = z.object({}).strict().meta({ id: "EmptyParams" }); +export const AcknowledgementResultSchema = z.literal(true).meta({ + id: "AcknowledgementResult", +}); + export const LoadStateSchema = z .enum(["load", "domcontentloaded", "networkidle"]) .meta({ id: "LoadState" }); @@ -1289,27 +1293,6 @@ export const PageNavigationOptionsSchema = z .strict() .meta({ id: "PageNavigationOptions" }); -export const PageVoidResultSchema = z - .object({ - ok: z.literal(true), - }) - .strict() - .meta({ id: "PageVoidResult" }); - -export const ContextVoidResultSchema = z - .object({ - ok: z.literal(true), - }) - .strict() - .meta({ id: "ContextVoidResult" }); - -export const ContextCloseResultSchema = z - .object({ - closed: z.literal(true), - }) - .strict() - .meta({ id: "ContextCloseResult" }); - export const PageCoordinateResultSchema = z .object({ xpath: z.string(), @@ -1897,13 +1880,6 @@ export const StagehandInitResultSchema = z .strict() .meta({ id: "StagehandInitResult" }); -export const StagehandCloseResultSchema = z - .object({ - closed: z.literal(true), - }) - .strict() - .meta({ id: "StagehandCloseResult" }); - export const ContextPagesResultSchema = z.array(PageRefSchema).meta({ id: "ContextPagesResult" }); export const ContextActivePageResultSchema = PageRefSchema.nullable().meta({ @@ -1945,13 +1921,6 @@ export const PageTitleResultSchema = z .strict() .meta({ id: "PageTitleResult" }); -export const PageCloseResultSchema = z - .object({ - closed: z.literal(true), - }) - .strict() - .meta({ id: "PageCloseResult" }); - export const PageDragAndDropResultSchema = z .object({ fromXpath: z.string(), @@ -1982,27 +1951,6 @@ export const PageWaitForSelectorResultSchema = z .strict() .meta({ id: "PageWaitForSelectorResult" }); -export const LocatorClickResultSchema = z - .object({ - clicked: z.literal(true), - }) - .strict() - .meta({ id: "LocatorClickResult" }); - -export const LocatorFillResultSchema = z - .object({ - filled: z.literal(true), - }) - .strict() - .meta({ id: "LocatorFillResult" }); - -export const LocatorHoverResultSchema = z - .object({ - hovered: z.literal(true), - }) - .strict() - .meta({ id: "LocatorHoverResult" }); - export const LocatorCountResultSchema = z .object({ count: z.number().int().nonnegative(), @@ -2052,13 +2000,6 @@ export const LocatorTextContentResultSchema = z .strict() .meta({ id: "LocatorTextContentResult" }); -export const LocatorScrollToResultSchema = z - .object({ - scrolled: z.literal(true), - }) - .strict() - .meta({ id: "LocatorScrollToResult" }); - export const LocatorCentroidResultSchema = z .object({ x: z.number(), @@ -2067,27 +2008,6 @@ export const LocatorCentroidResultSchema = z .strict() .meta({ id: "LocatorCentroidResult" }); -export const LocatorHighlightResultSchema = z - .object({ - highlighted: z.literal(true), - }) - .strict() - .meta({ id: "LocatorHighlightResult" }); - -export const LocatorSendClickEventResultSchema = z - .object({ - clicked: z.literal(true), - }) - .strict() - .meta({ id: "LocatorSendClickEventResult" }); - -export const LocatorTypeResultSchema = z - .object({ - typed: z.literal(true), - }) - .strict() - .meta({ id: "LocatorTypeResult" }); - export const LocatorSelectOptionResultSchema = z .object({ values: z.array(z.string()), diff --git a/packages/protocol/stagehand.v4.json b/packages/protocol/stagehand.v4.json index 32294180d..3d4750c19 100644 --- a/packages/protocol/stagehand.v4.json +++ b/packages/protocol/stagehand.v4.json @@ -78,7 +78,7 @@ "$ref": "#/$defs/EmptyParams" }, "result": { - "$ref": "#/$defs/StagehandCloseResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -195,7 +195,7 @@ "$ref": "#/$defs/ContextSetActivePageParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -208,7 +208,7 @@ "$ref": "#/$defs/EmptyParams" }, "result": { - "$ref": "#/$defs/ContextCloseResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -221,7 +221,7 @@ "$ref": "#/$defs/ContextAddInitScriptParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -234,7 +234,7 @@ "$ref": "#/$defs/ContextSetExtraHTTPHeadersParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -260,7 +260,7 @@ "$ref": "#/$defs/ContextSetDomainPolicyParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -286,7 +286,7 @@ "$ref": "#/$defs/ContextAddCookiesParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -299,7 +299,7 @@ "$ref": "#/$defs/ContextClearCookiesParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -325,7 +325,7 @@ "$ref": "#/$defs/ContextClipboardWriteTextParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -338,7 +338,7 @@ "$ref": "#/$defs/ContextClipboardTarget" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -351,7 +351,7 @@ "$ref": "#/$defs/ContextClipboardPasteParams" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -364,7 +364,7 @@ "$ref": "#/$defs/ContextClipboardTarget" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -377,7 +377,7 @@ "$ref": "#/$defs/ContextClipboardTarget" }, "result": { - "$ref": "#/$defs/ContextVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -429,7 +429,7 @@ "$ref": "#/$defs/PageIdParams" }, "result": { - "$ref": "#/$defs/PageCloseResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -533,7 +533,7 @@ "$ref": "#/$defs/PageTypeParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -546,7 +546,7 @@ "$ref": "#/$defs/PageKeyPressParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -572,7 +572,7 @@ "$ref": "#/$defs/PageAddInitScriptParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -585,7 +585,7 @@ "$ref": "#/$defs/PageSetExtraHTTPHeadersParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -624,7 +624,7 @@ "$ref": "#/$defs/PageSetViewportSizeParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -637,7 +637,7 @@ "$ref": "#/$defs/PageWaitForLoadStateParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -650,7 +650,7 @@ "$ref": "#/$defs/PageWaitForTimeoutParams" }, "result": { - "$ref": "#/$defs/PageVoidResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -676,7 +676,7 @@ "$ref": "#/$defs/LocatorClickParams" }, "result": { - "$ref": "#/$defs/LocatorClickResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -689,7 +689,7 @@ "$ref": "#/$defs/LocatorFillParams" }, "result": { - "$ref": "#/$defs/LocatorFillResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -702,7 +702,7 @@ "$ref": "#/$defs/LocatorDescriptor" }, "result": { - "$ref": "#/$defs/LocatorHoverResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -806,7 +806,7 @@ "$ref": "#/$defs/LocatorScrollToParams" }, "result": { - "$ref": "#/$defs/LocatorScrollToResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -832,7 +832,7 @@ "$ref": "#/$defs/LocatorHighlightParams" }, "result": { - "$ref": "#/$defs/LocatorHighlightResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -845,7 +845,7 @@ "$ref": "#/$defs/LocatorSendClickEventParams" }, "result": { - "$ref": "#/$defs/LocatorSendClickEventResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -858,7 +858,7 @@ "$ref": "#/$defs/LocatorTypeParams" }, "result": { - "$ref": "#/$defs/LocatorTypeResult" + "$ref": "#/$defs/AcknowledgementResult" } }, "required": ["params", "result"], @@ -1596,16 +1596,9 @@ "required": ["page_id"], "additionalProperties": false }, - "StagehandCloseResult": { - "type": "object", - "properties": { - "closed": { - "type": "boolean", - "const": true - } - }, - "required": ["closed"], - "additionalProperties": false + "AcknowledgementResult": { + "type": "boolean", + "const": true }, "StagehandActParams": { "type": "object", @@ -2825,28 +2818,6 @@ "required": ["page_id"], "additionalProperties": false }, - "ContextVoidResult": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "const": true - } - }, - "required": ["ok"], - "additionalProperties": false - }, - "ContextCloseResult": { - "type": "object", - "properties": { - "closed": { - "type": "boolean", - "const": true - } - }, - "required": ["closed"], - "additionalProperties": false - }, "ContextAddInitScriptParams": { "type": "object", "properties": { @@ -3204,17 +3175,6 @@ "required": ["title"], "additionalProperties": false }, - "PageCloseResult": { - "type": "object", - "properties": { - "closed": { - "type": "boolean", - "const": true - } - }, - "required": ["closed"], - "additionalProperties": false - }, "PageReloadParams": { "type": "object", "properties": { @@ -3471,17 +3431,6 @@ }, "additionalProperties": false }, - "PageVoidResult": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "const": true - } - }, - "required": ["ok"], - "additionalProperties": false - }, "PageKeyPressParams": { "type": "object", "properties": { @@ -3914,17 +3863,6 @@ }, "additionalProperties": false }, - "LocatorClickResult": { - "type": "object", - "properties": { - "clicked": { - "type": "boolean", - "const": true - } - }, - "required": ["clicked"], - "additionalProperties": false - }, "LocatorFillParams": { "type": "object", "properties": { @@ -3947,28 +3885,6 @@ "required": ["page_id", "selector", "value"], "additionalProperties": false }, - "LocatorFillResult": { - "type": "object", - "properties": { - "filled": { - "type": "boolean", - "const": true - } - }, - "required": ["filled"], - "additionalProperties": false - }, - "LocatorHoverResult": { - "type": "object", - "properties": { - "hovered": { - "type": "boolean", - "const": true - } - }, - "required": ["hovered"], - "additionalProperties": false - }, "LocatorCountResult": { "type": "object", "properties": { @@ -4070,17 +3986,6 @@ "required": ["page_id", "selector", "percent"], "additionalProperties": false }, - "LocatorScrollToResult": { - "type": "object", - "properties": { - "scrolled": { - "type": "boolean", - "const": true - } - }, - "required": ["scrolled"], - "additionalProperties": false - }, "LocatorCentroidResult": { "type": "object", "properties": { @@ -4152,17 +4057,6 @@ "required": ["r", "g", "b"], "additionalProperties": false }, - "LocatorHighlightResult": { - "type": "object", - "properties": { - "highlighted": { - "type": "boolean", - "const": true - } - }, - "required": ["highlighted"], - "additionalProperties": false - }, "LocatorSendClickEventParams": { "type": "object", "properties": { @@ -4203,17 +4097,6 @@ }, "additionalProperties": false }, - "LocatorSendClickEventResult": { - "type": "object", - "properties": { - "clicked": { - "type": "boolean", - "const": true - } - }, - "required": ["clicked"], - "additionalProperties": false - }, "LocatorTypeParams": { "type": "object", "properties": { @@ -4249,17 +4132,6 @@ }, "additionalProperties": false }, - "LocatorTypeResult": { - "type": "object", - "properties": { - "typed": { - "type": "boolean", - "const": true - } - }, - "required": ["typed"], - "additionalProperties": false - }, "LocatorSelectOptionParams": { "type": "object", "properties": { diff --git a/packages/protocol/tests/browser-runtime/rpc-client-smoke.test.ts b/packages/protocol/tests/browser-runtime/rpc-client-smoke.test.ts index b2871e5b3..74ff41b50 100644 --- a/packages/protocol/tests/browser-runtime/rpc-client-smoke.test.ts +++ b/packages/protocol/tests/browser-runtime/rpc-client-smoke.test.ts @@ -148,7 +148,7 @@ describe("Stagehand service worker RPC client smoke", () => { pageId: page.pageId, selector: "#blank-page-button", }), - ).resolves.toStrictEqual({ clicked: true }); + ).resolves.toStrictEqual(true); await expect( activeRpcClient.send(StagehandMethods.locatorTextContent, { pageId: page.pageId, @@ -195,7 +195,7 @@ describe("Stagehand service worker RPC client smoke", () => { pageId: page.pageId, selector: "#data-button", }), - ).resolves.toStrictEqual({ clicked: true }); + ).resolves.toStrictEqual(true); await expect( activeRpcClient.send(StagehandMethods.locatorTextContent, { pageId: page.pageId, @@ -289,9 +289,7 @@ describe("Stagehand service worker RPC client smoke", () => { await expect( activeRpcClient.send(StagehandMethods.pageClose, { pageId: page.pageId }), - ).resolves.toStrictEqual({ - closed: true, - }); + ).resolves.toStrictEqual(true); }); it("routes locator actions through real PageRefs in a browser session", async () => { @@ -329,18 +327,14 @@ describe("Stagehand service worker RPC client smoke", () => { selector: "#locator-input", value: "user@example.com", }), - ).resolves.toStrictEqual({ - filled: true, - }); + ).resolves.toStrictEqual(true); await expect( activeRpcClient.send(StagehandMethods.locatorClick, { pageId: page.pageId, selector: "#locator-button", }), - ).resolves.toStrictEqual({ - clicked: true, - }); + ).resolves.toStrictEqual(true); await expect( activeRpcClient.send(StagehandMethods.locatorTextContent, { @@ -357,7 +351,7 @@ describe("Stagehand service worker RPC client smoke", () => { selector: "#locator-date", value: "2026-07-21", }), - ).resolves.toStrictEqual({ filled: true }); + ).resolves.toStrictEqual(true); await expect( activeRpcClient.send(StagehandMethods.locatorInputValue, { pageId: page.pageId, diff --git a/packages/protocol/tests/protocol/acknowledgement-result.test.ts b/packages/protocol/tests/protocol/acknowledgement-result.test.ts new file mode 100644 index 000000000..85ef3c397 --- /dev/null +++ b/packages/protocol/tests/protocol/acknowledgement-result.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; +import { StagehandMethods } from "../../schema-registry.js"; +import { AcknowledgementResultSchema } from "../../schemas.js"; + +describe("AcknowledgementResultSchema", () => { + it("accepts only the successful acknowledgement literal", () => { + expect(AcknowledgementResultSchema.parse(true)).toBe(true); + expect(() => AcknowledgementResultSchema.parse(false)).toThrow(); + expect(() => AcknowledgementResultSchema.parse(null)).toThrow(); + expect(() => AcknowledgementResultSchema.parse({ ok: true })).toThrow(); + }); + + it("is shared directly by every side-effect-only method", () => { + const methodNames = Object.values(StagehandMethods) + .filter((method) => method.result === AcknowledgementResultSchema) + .map((method) => method.name) + .sort(); + + expect(methodNames).toStrictEqual( + [ + "context.add_cookies", + "context.add_init_script", + "context.clear_cookies", + "context.clipboard_clear", + "context.clipboard_copy", + "context.clipboard_cut", + "context.clipboard_paste", + "context.clipboard_write_text", + "context.close", + "context.set_active_page", + "context.set_domain_policy", + "context.set_extra_http_headers", + "locator.click", + "locator.fill", + "locator.highlight", + "locator.hover", + "locator.scroll_to", + "locator.send_click_event", + "locator.type", + "page.add_init_script", + "page.close", + "page.key_press", + "page.set_extra_http_headers", + "page.set_viewport_size", + "page.type", + "page.wait_for_load_state", + "page.wait_for_timeout", + "stagehand.close", + ].sort(), + ); + }); +}); diff --git a/packages/protocol/tests/protocol/context-command-schemas.test.ts b/packages/protocol/tests/protocol/context-command-schemas.test.ts index 00baa637f..2f603cd23 100644 --- a/packages/protocol/tests/protocol/context-command-schemas.test.ts +++ b/packages/protocol/tests/protocol/context-command-schemas.test.ts @@ -11,14 +11,12 @@ import { ContextClipboardReadTextParamsSchema, ContextClipboardReadTextResultSchema, ContextClipboardWriteTextParamsSchema, - ContextCloseResultSchema, ContextCookiesParamsSchema, ContextCookiesResultSchema, ContextGetDomainPolicyResultSchema, ContextSetActivePageParamsSchema, ContextSetDomainPolicyParamsSchema, ContextSetExtraHTTPHeadersParamsSchema, - ContextVoidResultSchema, EmptyParamsSchema, } from "../../schemas.js"; @@ -100,13 +98,6 @@ describe("context lifecycle and configuration command schemas", () => { expect(() => ContextSetDomainPolicyParamsSchema.parse({ policy: undefined })).toThrow(); expect(() => ContextGetDomainPolicyResultSchema.parse({ policy: null, extra: true })).toThrow(); }); - - it("keeps context mutation and close results strict", () => { - expect(ContextVoidResultSchema.parse({ ok: true })).toStrictEqual({ ok: true }); - expect(ContextCloseResultSchema.parse({ closed: true })).toStrictEqual({ closed: true }); - expect(() => ContextVoidResultSchema.parse({ ok: true, extra: true })).toThrow(); - expect(() => ContextCloseResultSchema.parse({ closed: false })).toThrow(); - }); }); describe("context cookie command schemas", () => { diff --git a/packages/protocol/tests/protocol/object-model-protocol.test.ts b/packages/protocol/tests/protocol/object-model-protocol.test.ts index 52e44be8e..9442b10a2 100644 --- a/packages/protocol/tests/protocol/object-model-protocol.test.ts +++ b/packages/protocol/tests/protocol/object-model-protocol.test.ts @@ -375,9 +375,7 @@ describe("Stagehand object-model protocol", () => { expect(StagehandMethods.locatorHover.params.parse(locatorDescriptor())).toStrictEqual( locatorDescriptor(), ); - expect(StagehandMethods.locatorHover.result.parse({ hovered: true })).toStrictEqual({ - hovered: true, - }); + expect(StagehandMethods.locatorHover.result.parse(true)).toBe(true); expect(StagehandMethods.locatorCount.result.parse({ count: 2 })).toStrictEqual({ count: 2, diff --git a/packages/protocol/tests/protocol/page-shared-schemas.test.ts b/packages/protocol/tests/protocol/page-shared-schemas.test.ts index d72b27d70..3eec2a447 100644 --- a/packages/protocol/tests/protocol/page-shared-schemas.test.ts +++ b/packages/protocol/tests/protocol/page-shared-schemas.test.ts @@ -5,7 +5,6 @@ import { PageNavigationOptionsSchema, PageScreenshotClipSchema, PageSnapshotOptionsSchema, - PageVoidResultSchema, SnapshotResultSchema, } from "../../schemas.js"; @@ -24,12 +23,13 @@ describe("shared page protocol schemas", () => { expect(() => PageNavigationOptionsSchema.parse({ timeout: 0 })).toThrow(); }); - it("keeps command result schemas strict", () => { - expect(PageVoidResultSchema.parse({ ok: true })).toStrictEqual({ ok: true }); + it("keeps coordinate results strict", () => { expect(PageCoordinateResultSchema.parse({ xpath: "/html/body/button" })).toStrictEqual({ xpath: "/html/body/button", }); - expect(() => PageVoidResultSchema.parse({ ok: true, extra: true })).toThrow(); + expect(() => + PageCoordinateResultSchema.parse({ xpath: "/html/body/button", extra: true }), + ).toThrow(); }); it("validates screenshot clips", () => { diff --git a/packages/protocol/tests/protocol/schema-registry.test-d.ts b/packages/protocol/tests/protocol/schema-registry.test-d.ts index 82c8efc86..20bc7bfff 100644 --- a/packages/protocol/tests/protocol/schema-registry.test-d.ts +++ b/packages/protocol/tests/protocol/schema-registry.test-d.ts @@ -23,6 +23,9 @@ expectTypeOf>().toEqu expectTypeOf( StagehandMethods.contextSetDomainPolicy.name, ).toEqualTypeOf<"context.set_domain_policy">(); +expectTypeOf< + z.output +>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf<{ policy: { allowedDomains?: string[]; @@ -81,6 +84,7 @@ expectTypeOf>().toEqua Record >(); expectTypeOf(StagehandMethods.locatorSelectOption.name).toEqualTypeOf<"locator.select_option">(); +expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf<{ pageId: string; selector: string; diff --git a/packages/protocol/types.ts b/packages/protocol/types.ts index ba16a9234..74bd5e897 100644 --- a/packages/protocol/types.ts +++ b/packages/protocol/types.ts @@ -6,6 +6,7 @@ import type { StagehandSendToHostBindingSchema, } from "./schema-registry.js"; import type { + AcknowledgementResultSchema, ActionSchema, ActOptionsSchema, ActResultDataSchema, @@ -46,7 +47,6 @@ import type { ContextClipboardReadTextResultSchema, ContextClipboardTargetSchema, ContextClipboardWriteTextParamsSchema, - ContextCloseResultSchema, ContextCookiesParamsSchema, ContextCookiesResultSchema, ContextGetDomainPolicyResultSchema, @@ -55,7 +55,6 @@ import type { ContextSetActivePageParamsSchema, ContextSetDomainPolicyParamsSchema, ContextSetExtraHTTPHeadersParamsSchema, - ContextVoidResultSchema, CookieFilterSchema, CookieParamSchema, CookieRegexSchema, @@ -72,15 +71,11 @@ import type { GoogleServiceAccountCredentialsSchema, ImplementationInfoSchema, LocatorClickParamsSchema, - LocatorClickResultSchema, LocatorCentroidResultSchema, LocatorCountResultSchema, LocatorDescriptorSchema, LocatorFillParamsSchema, - LocatorFillResultSchema, LocatorHighlightParamsSchema, - LocatorHighlightResultSchema, - LocatorHoverResultSchema, LocatorInnerHtmlResultSchema, LocatorInnerTextResultSchema, LocatorInputValueResultSchema, @@ -88,14 +83,11 @@ import type { LocatorIsVisibleResultSchema, LocatorSchema, LocatorScrollToParamsSchema, - LocatorScrollToResultSchema, LocatorSelectOptionParamsSchema, LocatorSelectOptionResultSchema, LocatorSendClickEventParamsSchema, - LocatorSendClickEventResultSchema, LocatorTextContentResultSchema, LocatorTypeParamsSchema, - LocatorTypeResultSchema, LoadStateSchema, LLMGenerateParamsSchema, LLMGenerateResultSchema, @@ -135,7 +127,6 @@ import type { ObserveResultSchema, PageAddInitScriptParamsSchema, PageClickParamsSchema, - PageCloseResultSchema, PageCoordinateResultSchema, PageDragAndDropParamsSchema, PageDragAndDropResultSchema, @@ -163,7 +154,6 @@ import type { PageTitleResultSchema, PageTypeParamsSchema, PageUrlResultSchema, - PageVoidResultSchema, PageWaitForLoadStateParamsSchema, PageWaitForSelectorParamsSchema, PageWaitForSelectorResultSchema, @@ -175,7 +165,6 @@ import type { RuntimeLoopbackStatusResultSchema, RgbaColorSchema, StagehandActParamsSchema, - StagehandCloseResultSchema, StagehandExtractParamsSchema, StagehandInitParamsSchema, StagehandInitResultSchema, @@ -259,11 +248,9 @@ export type ExtractResult = z.infer; export type ObserveOptions = z.infer; export type ObserveResult = z.infer; export type EmptyParams = z.infer; -export type ContextVoidResult = z.infer; -export type ContextCloseResult = z.infer; +export type AcknowledgementResult = z.infer; export type PageRef = z.infer; export type PageNavigationOptions = z.infer; -export type PageVoidResult = z.infer; export type PageCoordinateResult = z.infer; export type PageScreenshotClip = z.infer; export type PageSnapshotOptions = z.infer; @@ -328,7 +315,6 @@ export type RuntimeConfigureResult = z.infer; export type BrowserGetVersionResult = z.infer; export type StagehandInitResult = z.infer; -export type StagehandCloseResult = z.infer; export type ContextPagesResult = z.infer; export type ContextCookiesResult = z.infer; export type ContextClipboardReadTextResult = z.infer; @@ -336,14 +322,10 @@ export type ContextActivePageResult = z.infer; export type PageUrlResult = z.infer; export type PageTitleResult = z.infer; -export type PageCloseResult = z.infer; export type PageDragAndDropResult = z.infer; export type PageEvaluateResult = z.infer; export type PageScreenshotResult = z.infer; export type PageWaitForSelectorResult = z.infer; -export type LocatorClickResult = z.infer; -export type LocatorFillResult = z.infer; -export type LocatorHoverResult = z.infer; export type LocatorCountResult = z.infer; export type LocatorIsCheckedResult = z.infer; export type LocatorInputValueResult = z.infer; @@ -351,11 +333,7 @@ export type LocatorIsVisibleResult = z.infer; export type LocatorInnerHtmlResult = z.infer; export type LocatorTextContentResult = z.infer; -export type LocatorScrollToResult = z.infer; export type LocatorCentroidResult = z.infer; -export type LocatorHighlightResult = z.infer; -export type LocatorSendClickEventResult = z.infer; -export type LocatorTypeResult = z.infer; export type LocatorSelectOptionResult = z.infer; export type StagehandLogData = z.infer; export type StagehandLog = z.infer; diff --git a/packages/sdk-python/src/stagehand/_generated/models.py b/packages/sdk-python/src/stagehand/_generated/models.py index 14ff92a52..213e9b889 100644 --- a/packages/sdk-python/src/stagehand/_generated/models.py +++ b/packages/sdk-python/src/stagehand/_generated/models.py @@ -23,6 +23,10 @@ ) +class AcknowledgementResult(RootModel[Literal[True]]): + root: Literal[True] + + class ActOptions(WireModel): model_config = ConfigDict( validate_by_name=True, @@ -365,14 +369,6 @@ class ContextClipboardWriteTextParams(WireModel): text: StrictStr -class ContextCloseResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - closed: Literal[True] - - class ContextCookiesParams(WireModel): model_config = ConfigDict( extra="forbid", @@ -429,14 +425,6 @@ class ContextSetExtraHTTPHeadersParams(WireModel): headers: dict[StrictStr, StrictStr] -class ContextVoidResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - ok: Literal[True] - - class Cookie(WireModel): model_config = ConfigDict( extra="forbid", @@ -1038,14 +1026,6 @@ class LocatorClickParams(WireModel): options: Optional[LocatorClickOptions] = None -class LocatorClickResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - clicked: Literal[True] - - class LocatorCountResult(WireModel): model_config = ConfigDict( extra="forbid", @@ -1075,14 +1055,6 @@ class LocatorFillParams(WireModel): value: StrictStr -class LocatorFillResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - filled: Literal[True] - - class LocatorHighlightOptions(WireModel): model_config = ConfigDict( extra="forbid", @@ -1104,22 +1076,6 @@ class LocatorHighlightParams(WireModel): options: Optional[LocatorHighlightOptions] = None -class LocatorHighlightResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - highlighted: Literal[True] - - -class LocatorHoverResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - hovered: Literal[True] - - class LocatorInnerHtmlResult(WireModel): model_config = ConfigDict( extra="forbid", @@ -1171,14 +1127,6 @@ class LocatorScrollToParams(WireModel): percent: Union[StrictFloat, StrictStr] -class LocatorScrollToResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - scrolled: Literal[True] - - class LocatorSelectOptionParams(WireModel): model_config = ConfigDict( extra="forbid", @@ -1220,14 +1168,6 @@ class LocatorSendClickEventParams(WireModel): options: Optional[LocatorSendClickEventOptions] = None -class LocatorSendClickEventResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - clicked: Literal[True] - - class LocatorTextContentResult(WireModel): model_config = ConfigDict( extra="forbid", @@ -1256,14 +1196,6 @@ class LocatorTypeParams(WireModel): options: Optional[LocatorTypeOptions] = None -class LocatorTypeResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - typed: Literal[True] - - class LogLevel(StrEnum): off = "off" error = "error" @@ -1405,14 +1337,6 @@ class PageClickParams(WireModel): options: Optional[PageClickOptions] = None -class PageCloseResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - closed: Literal[True] - - class PageCoordinateResult(WireModel): model_config = ConfigDict( extra="forbid", @@ -1745,14 +1669,6 @@ class PageUrlResult(WireModel): url: StrictStr -class PageVoidResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - ok: Literal[True] - - class PageWaitForLoadStateParams(WireModel): model_config = ConfigDict( extra="forbid", @@ -1885,14 +1801,6 @@ class StagehandActParams(WireModel): options: Optional[ActOptions] = None -class StagehandCloseResult(WireModel): - model_config = ConfigDict( - extra="forbid", - validate_by_name=True, - ) - closed: Literal[True] - - class StagehandExtractParams(WireModel): model_config = ConfigDict( extra="forbid", diff --git a/packages/sdk-python/src/stagehand/browser_clipboard.py b/packages/sdk-python/src/stagehand/browser_clipboard.py index 90f0d8819..28a06b04b 100644 --- a/packages/sdk-python/src/stagehand/browser_clipboard.py +++ b/packages/sdk-python/src/stagehand/browser_clipboard.py @@ -3,11 +3,11 @@ from typing import TYPE_CHECKING, Literal from ._generated.models import ( + AcknowledgementResult, ContextClipboardPasteParams, ContextClipboardReadTextResult, ContextClipboardTarget, ContextClipboardWriteTextParams, - ContextVoidResult, Shortcut, ) from .rpc_client import RPCClient @@ -35,14 +35,14 @@ async def write_text(self, text: str, *, page: Page | None = None) -> None: await self._rpc_client.send( "context.clipboard_write_text", params, - ContextVoidResult, + AcknowledgementResult, ) async def clear(self, *, page: Page | None = None) -> None: await self._rpc_client.send( "context.clipboard_clear", _clipboard_target(page), - ContextVoidResult, + AcknowledgementResult, ) async def paste( @@ -62,21 +62,21 @@ async def paste( await self._rpc_client.send( "context.clipboard_paste", params, - ContextVoidResult, + AcknowledgementResult, ) async def copy(self, *, page: Page | None = None) -> None: await self._rpc_client.send( "context.clipboard_copy", _clipboard_target(page), - ContextVoidResult, + AcknowledgementResult, ) async def cut(self, *, page: Page | None = None) -> None: await self._rpc_client.send( "context.clipboard_cut", _clipboard_target(page), - ContextVoidResult, + AcknowledgementResult, ) diff --git a/packages/sdk-python/src/stagehand/browser_context.py b/packages/sdk-python/src/stagehand/browser_context.py index e6ef7f3a7..4f03b2789 100644 --- a/packages/sdk-python/src/stagehand/browser_context.py +++ b/packages/sdk-python/src/stagehand/browser_context.py @@ -5,12 +5,12 @@ from pathlib import Path from ._generated.models import ( + AcknowledgementResult, ClearCookieOptions, ContextActivePageResult, ContextAddCookiesParams, ContextAddInitScriptParams, ContextClearCookiesParams, - ContextCloseResult, ContextCookiesParams, ContextCookiesResult, ContextGetDomainPolicyResult, @@ -19,7 +19,6 @@ ContextSetActivePageParams, ContextSetDomainPolicyParams, ContextSetExtraHTTPHeadersParams, - ContextVoidResult, Cookie, CookieFilter, CookieParam, @@ -71,7 +70,7 @@ async def set_active_page(self, page: Page) -> None: await self._rpc_client.send( "context.set_active_page", ContextSetActivePageParams(page_id=page.page_id), - ContextVoidResult, + AcknowledgementResult, ) async def close(self) -> None: @@ -79,7 +78,7 @@ async def close(self) -> None: await self._rpc_client.send( "context.close", EmptyParams(), - ContextCloseResult, + AcknowledgementResult, ) async def add_init_script(self, source: str | Path) -> None: @@ -91,14 +90,14 @@ async def add_init_script(self, source: str | Path) -> None: await self._rpc_client.send( "context.add_init_script", ContextAddInitScriptParams(source=script), - ContextVoidResult, + AcknowledgementResult, ) async def set_extra_http_headers(self, headers: Mapping[str, str]) -> None: await self._rpc_client.send( "context.set_extra_http_headers", ContextSetExtraHTTPHeadersParams(headers=dict(headers)), - ContextVoidResult, + AcknowledgementResult, ) async def get_domain_policy(self) -> DomainPolicy | None: @@ -113,7 +112,7 @@ async def set_domain_policy(self, policy: DomainPolicy | None) -> None: await self._rpc_client.send( "context.set_domain_policy", ContextSetDomainPolicyParams(policy=policy), - ContextVoidResult, + AcknowledgementResult, ) async def cookies(self, urls: str | Sequence[str] | None = None) -> list[Cookie]: @@ -131,7 +130,7 @@ async def add_cookies(self, cookies: Sequence[CookieParam]) -> None: await self._rpc_client.send( "context.add_cookies", ContextAddCookiesParams(cookies=list(cookies)), - ContextVoidResult, + AcknowledgementResult, ) async def clear_cookies( @@ -152,7 +151,7 @@ async def clear_cookies( await self._rpc_client.send( "context.clear_cookies", params, - ContextVoidResult, + AcknowledgementResult, ) diff --git a/packages/sdk-python/src/stagehand/locator.py b/packages/sdk-python/src/stagehand/locator.py index 90938ca66..e0ca493e6 100644 --- a/packages/sdk-python/src/stagehand/locator.py +++ b/packages/sdk-python/src/stagehand/locator.py @@ -4,34 +4,28 @@ from typing import Literal, Self from ._generated.models import ( + AcknowledgementResult, LocatorCentroidResult, LocatorClickOptions, LocatorClickParams, - LocatorClickResult, LocatorCountResult, LocatorDescriptor, LocatorFillParams, - LocatorFillResult, LocatorHighlightOptions, LocatorHighlightParams, - LocatorHighlightResult, - LocatorHoverResult, LocatorInnerHtmlResult, LocatorInnerTextResult, LocatorInputValueResult, LocatorIsCheckedResult, LocatorIsVisibleResult, LocatorScrollToParams, - LocatorScrollToResult, LocatorSelectOptionParams, LocatorSelectOptionResult, LocatorSendClickEventOptions, LocatorSendClickEventParams, - LocatorSendClickEventResult, LocatorTextContentResult, LocatorTypeOptions, LocatorTypeParams, - LocatorTypeResult, MouseButton, RgbaColor, ) @@ -85,14 +79,14 @@ async def click( await self._rpc_client.send( "locator.click", LocatorClickParams.model_validate(values), - LocatorClickResult, + AcknowledgementResult, ) async def hover(self) -> None: await self._rpc_client.send( "locator.hover", self._descriptor, - LocatorHoverResult, + AcknowledgementResult, ) async def fill(self, value: str) -> None: @@ -102,7 +96,7 @@ async def fill(self, value: str) -> None: **self._descriptor.model_dump(exclude_unset=True), "value": value, }), - LocatorFillResult, + AcknowledgementResult, ) async def count(self) -> int: @@ -168,7 +162,7 @@ async def scroll_to(self, percent: float | str) -> None: **self._descriptor.model_dump(exclude_unset=True), "percent": percent, }), - LocatorScrollToResult, + AcknowledgementResult, ) async def centroid(self) -> LocatorCentroidResult: @@ -200,7 +194,7 @@ async def highlight( await self._rpc_client.send( "locator.highlight", LocatorHighlightParams.model_validate(values), - LocatorHighlightResult, + AcknowledgementResult, ) async def send_click_event( @@ -227,7 +221,7 @@ async def send_click_event( await self._rpc_client.send( "locator.send_click_event", LocatorSendClickEventParams.model_validate(values), - LocatorSendClickEventResult, + AcknowledgementResult, ) async def type(self, text: str, *, delay: float | None = None) -> None: @@ -237,7 +231,7 @@ async def type(self, text: str, *, delay: float | None = None) -> None: await self._rpc_client.send( "locator.type", LocatorTypeParams.model_validate(values), - LocatorTypeResult, + AcknowledgementResult, ) async def select_option(self, values: str | Sequence[str]) -> list[str]: diff --git a/packages/sdk-python/src/stagehand/page.py b/packages/sdk-python/src/stagehand/page.py index 4c820becf..cd0ff5d8b 100644 --- a/packages/sdk-python/src/stagehand/page.py +++ b/packages/sdk-python/src/stagehand/page.py @@ -9,6 +9,7 @@ from pydantic import JsonValue, TypeAdapter from ._generated.models import ( + AcknowledgementResult, Animations, Caret, LoadState, @@ -16,7 +17,6 @@ PageAddInitScriptParams, PageClickOptions, PageClickParams, - PageCloseResult, PageCoordinateResult, PageDragAndDropOptions, PageDragAndDropParams, @@ -50,7 +50,6 @@ PageTypeOptions, PageTypeParams, PageUrlResult, - PageVoidResult, PageWaitForLoadStateParams, PageWaitForSelectorOptions, PageWaitForSelectorParams, @@ -267,13 +266,13 @@ async def type( }) if options.model_fields_set: params.options = options - await self._rpc_client.send("page.type", params, PageVoidResult) + await self._rpc_client.send("page.type", params, AcknowledgementResult) async def key_press(self, key: str, *, delay: float | None = None) -> None: params = PageKeyPressParams(page_id=self.page_id, key=key) if delay is not None: params.options = PageKeyPressOptions(delay=delay) - await self._rpc_client.send("page.key_press", params, PageVoidResult) + await self._rpc_client.send("page.key_press", params, AcknowledgementResult) @overload async def evaluate(self, expression: str) -> JsonValue: ... @@ -311,14 +310,14 @@ async def add_init_script(self, source: str | Path) -> None: await self._rpc_client.send( "page.add_init_script", PageAddInitScriptParams(page_id=self.page_id, source=script), - PageVoidResult, + AcknowledgementResult, ) async def set_extra_http_headers(self, headers: Mapping[str, str]) -> None: await self._rpc_client.send( "page.set_extra_http_headers", PageSetExtraHTTPHeadersParams(page_id=self.page_id, headers=dict(headers)), - PageVoidResult, + AcknowledgementResult, ) async def set_viewport_size( @@ -333,7 +332,7 @@ async def set_viewport_size( params.options = PageSetViewportSizeOptions( device_scale_factor=device_scale_factor, ) - await self._rpc_client.send("page.set_viewport_size", params, PageVoidResult) + await self._rpc_client.send("page.set_viewport_size", params, AcknowledgementResult) async def wait_for_load_state( self, @@ -346,13 +345,13 @@ async def wait_for_load_state( }) if timeout is not None: params.timeout = timeout - await self._rpc_client.send("page.wait_for_load_state", params, PageVoidResult) + await self._rpc_client.send("page.wait_for_load_state", params, AcknowledgementResult) async def wait_for_timeout(self, ms: int) -> None: await self._rpc_client.send( "page.wait_for_timeout", PageWaitForTimeoutParams(page_id=self.page_id, ms=ms), - PageVoidResult, + AcknowledgementResult, ) async def wait_for_selector( @@ -459,7 +458,7 @@ async def close(self) -> None: await self._rpc_client.send( "page.close", PageIdParams(page_id=self.page_id), - PageCloseResult, + AcknowledgementResult, ) def locator(self, selector: str) -> Locator: diff --git a/packages/sdk-python/src/stagehand/stagehand.py b/packages/sdk-python/src/stagehand/stagehand.py index 80a23615b..c02ac9a7e 100644 --- a/packages/sdk-python/src/stagehand/stagehand.py +++ b/packages/sdk-python/src/stagehand/stagehand.py @@ -13,6 +13,7 @@ from pydantic import BaseModel from ._generated.models import ( + AcknowledgementResult, Action, ActOptions, ActResult, @@ -34,7 +35,6 @@ ProxyConfig, RuntimeLoopbackStatusResult, StagehandActParams, - StagehandCloseResult, StagehandExtractParams, StagehandInitParams, StagehandInitResult, @@ -579,7 +579,7 @@ async def close(self) -> None: await self._rpc_client.send( "stagehand.close", EmptyParams(), - StagehandCloseResult, + AcknowledgementResult, ) except CDPConnectionClosedError: pass diff --git a/packages/sdk-python/tests/test_browser_clipboard.py b/packages/sdk-python/tests/test_browser_clipboard.py index d3969a991..027e8843f 100644 --- a/packages/sdk-python/tests/test_browser_clipboard.py +++ b/packages/sdk-python/tests/test_browser_clipboard.py @@ -5,8 +5,8 @@ import pytest from stagehand._generated.models import ( + AcknowledgementResult, ContextClipboardReadTextResult, - ContextVoidResult, PageRef, ) from stagehand.browser_clipboard import BrowserClipboard @@ -20,7 +20,7 @@ async def test_browser_clipboard_uses_the_optional_page_as_its_wire_target() -> None: recording = RecordingRPCClient({ "context.clipboard_read_text": ContextClipboardReadTextResult(text="hello"), - "context.clipboard_write_text": ContextVoidResult(ok=True), + "context.clipboard_write_text": AcknowledgementResult(root=True), }) rpc_client = cast(RPCClient, recording) clipboard = BrowserClipboard(rpc_client) @@ -32,3 +32,24 @@ async def test_browser_clipboard_uses_the_optional_page_as_its_wire_target() -> assert text == "hello" assert recording.calls[0][1].model_dump(exclude_unset=True) == {"page_id": "page-1"} assert recording.calls[1][1].model_dump(exclude_unset=True) == {"text": "updated"} + + +@pytest.mark.asyncio +async def test_browser_clipboard_side_effects_use_acknowledgement_results() -> None: + methods = [ + "context.clipboard_clear", + "context.clipboard_paste", + "context.clipboard_copy", + "context.clipboard_cut", + ] + recording = RecordingRPCClient({method: AcknowledgementResult(root=True) for method in methods}) + clipboard = BrowserClipboard(cast(RPCClient, recording)) + + await clipboard.clear() + await clipboard.paste() + await clipboard.copy() + await clipboard.cut() + + assert [(method, result_model) for method, _, result_model in recording.calls] == [ + (method, AcknowledgementResult) for method in methods + ] diff --git a/packages/sdk-python/tests/test_browser_context.py b/packages/sdk-python/tests/test_browser_context.py index 4c534cf19..bab50194a 100644 --- a/packages/sdk-python/tests/test_browser_context.py +++ b/packages/sdk-python/tests/test_browser_context.py @@ -6,8 +6,8 @@ import pytest from stagehand._generated.models import ( + AcknowledgementResult, ContextClearCookiesParams, - ContextVoidResult, PageRef, ) from stagehand.browser_context import BrowserContext @@ -22,7 +22,7 @@ async def test_browser_context_wraps_generated_page_references() -> None: "context.pages": [PageRef(page_id="page-1")], "context.new_page": PageRef(page_id="page-2"), "context.active_page": PageRef(page_id="page-2"), - "context.set_active_page": ContextVoidResult(ok=True), + "context.set_active_page": AcknowledgementResult(root=True), }) context = BrowserContext(cast(RPCClient, recording)) @@ -51,7 +51,7 @@ def test_browser_context_reuses_one_clipboard_wrapper() -> None: @pytest.mark.asyncio async def test_browser_context_serializes_python_cookie_filters() -> None: - recording = RecordingRPCClient({"context.clear_cookies": ContextVoidResult(ok=True)}) + recording = RecordingRPCClient({"context.clear_cookies": AcknowledgementResult(root=True)}) context = BrowserContext(cast(RPCClient, recording)) await context.clear_cookies( diff --git a/packages/sdk-python/tests/test_generated_models.py b/packages/sdk-python/tests/test_generated_models.py index 9ac6469f7..15ef90609 100644 --- a/packages/sdk-python/tests/test_generated_models.py +++ b/packages/sdk-python/tests/test_generated_models.py @@ -63,6 +63,13 @@ def test_generated_models_validate_and_serialize_wire_values() -> None: } +def test_acknowledgement_result_accepts_only_true() -> None: + assert models.AcknowledgementResult.model_validate(True).root is True + + with pytest.raises(ValidationError): + models.AcknowledgementResult.model_validate(False) + + def test_generated_models_retain_cross_field_validation() -> None: with pytest.raises(ValidationError, match="fullPage and clip"): models.PageScreenshotOptions.model_validate({ diff --git a/packages/sdk-python/tests/test_locator.py b/packages/sdk-python/tests/test_locator.py index bada5c7ea..f4602a18c 100644 --- a/packages/sdk-python/tests/test_locator.py +++ b/packages/sdk-python/tests/test_locator.py @@ -5,8 +5,8 @@ import pytest from stagehand._generated.models import ( + AcknowledgementResult, LocatorClickParams, - LocatorClickResult, LocatorCountResult, LocatorDescriptor, LocatorSelectOptionResult, @@ -20,7 +20,7 @@ @pytest.mark.asyncio async def test_locator_methods_use_generated_models_and_keep_the_descriptor_internal() -> None: recording = RecordingRPCClient({ - "locator.click": LocatorClickResult(clicked=True), + "locator.click": AcknowledgementResult(root=True), "locator.count": LocatorCountResult(count=2), "locator.select_option": LocatorSelectOptionResult(values=["one"]), }) @@ -44,7 +44,7 @@ async def test_locator_methods_use_generated_models_and_keep_the_descriptor_inte "nth": 1, "options": {"button": "left", "click_count": 2}, }) - assert result_model is LocatorClickResult + assert result_model is AcknowledgementResult assert recording.calls[1] == ( "locator.count", LocatorDescriptor(page_id="page-1", selector="select", nth=1), diff --git a/packages/sdk-python/tests/test_page.py b/packages/sdk-python/tests/test_page.py index 64634cc7a..61234ae50 100644 --- a/packages/sdk-python/tests/test_page.py +++ b/packages/sdk-python/tests/test_page.py @@ -6,6 +6,7 @@ from pydantic import BaseModel from stagehand._generated.models import ( + AcknowledgementResult, PageEvaluateResult, PageGotoParams, PageRef, @@ -79,3 +80,32 @@ async def test_page_evaluate_returns_json_or_a_requested_typed_result() -> None: assert raw == {"answer": True} assert typed == EvaluationResult(answer=True) + + +@pytest.mark.asyncio +async def test_page_side_effects_use_acknowledgement_results() -> None: + methods = [ + "page.type", + "page.key_press", + "page.add_init_script", + "page.set_extra_http_headers", + "page.set_viewport_size", + "page.wait_for_load_state", + "page.wait_for_timeout", + "page.close", + ] + recording = RecordingRPCClient({method: AcknowledgementResult(root=True) for method in methods}) + page = Page(cast(RPCClient, recording), PageRef(page_id="page-1")) + + await page.type("hello") + await page.key_press("Enter") + await page.add_init_script("globalThis.ready = true") + await page.set_extra_http_headers({"x-test": "true"}) + await page.set_viewport_size(1280, 720) + await page.wait_for_load_state("load") + await page.wait_for_timeout(100) + await page.close() + + assert [(method, result_model) for method, _, result_model in recording.calls] == [ + (method, AcknowledgementResult) for method in methods + ] diff --git a/packages/sdk-python/tests/test_rpc_client.py b/packages/sdk-python/tests/test_rpc_client.py index 8b14c413c..5c3f936ea 100644 --- a/packages/sdk-python/tests/test_rpc_client.py +++ b/packages/sdk-python/tests/test_rpc_client.py @@ -116,7 +116,7 @@ async def test_send_revalidates_mutated_params_and_strictly_validates_results() try: with pytest.raises(ValidationError): - await client.send("page.set_extra_http_headers", params, models.PageVoidResult) + await client.send("page.set_extra_http_headers", params, models.AcknowledgementResult) assert transport.sent == [] call = asyncio.create_task( diff --git a/packages/sdk-python/tests/test_stagehand.py b/packages/sdk-python/tests/test_stagehand.py index be9649b59..585fab658 100644 --- a/packages/sdk-python/tests/test_stagehand.py +++ b/packages/sdk-python/tests/test_stagehand.py @@ -10,6 +10,7 @@ from stagehand import LLMGenerateInput, LLMGenerateOutput, Page, ProtocolLocator, Stagehand from stagehand._generated.models import ( + AcknowledgementResult, Action, ActResult, ActResultData, @@ -28,7 +29,6 @@ PageRef, RuntimeLoopbackStatusResult, StagehandActParams, - StagehandCloseResult, StagehandExtractParams, StagehandInitParams, StagehandInitResult, @@ -467,7 +467,7 @@ async def close(self, reason: BaseException | None = None) -> None: ) recording = FailingCloseRPCClient({ "stagehand.init": StagehandInitResult(initialized=True, pages=[]), - "stagehand.close": StagehandCloseResult(closed=True), + "stagehand.close": AcknowledgementResult(root=True), }) async def resolve(_: StagehandClientInitParams) -> ResolvedBrowserSource: diff --git a/packages/sdk-ts/tests/object-wrapper.test.ts b/packages/sdk-ts/tests/object-wrapper.test.ts index 912c0cc50..8f7d7d033 100644 --- a/packages/sdk-ts/tests/object-wrapper.test.ts +++ b/packages/sdk-ts/tests/object-wrapper.test.ts @@ -1,7 +1,7 @@ import { mkdtemp, readFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; -import { describe, expect, it } from "vitest"; +import { describe, expect, expectTypeOf, it } from "vitest"; import { z } from "zod/v4"; import type { RPCMethod } from "../../protocol/json-rpc/schemas.js"; import { StagehandMethods } from "../../protocol/schema-registry.js"; @@ -77,6 +77,16 @@ const stagehandInitCall = requestCall(StagehandMethods.stagehandInit, { }); describe("Stagehand TS object wrapper", () => { + it("keeps acknowledgement methods void at the public boundary", () => { + type TestStagehand = ReturnType; + + expectTypeOf>().toEqualTypeOf>(); + expectTypeOf>().toEqualTypeOf>(); + expectTypeOf>().toEqualTypeOf>(); + expectTypeOf>().toEqualTypeOf>(); + expectTypeOf>().toEqualTypeOf>(); + }); + it("initializes the remote Stagehand configuration", async () => { const client = new FakeProtocolClient(); const stagehand = createStagehandWithClientForTest(client); @@ -91,7 +101,7 @@ describe("Stagehand TS object wrapper", () => { it("closes the remote runtime", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.stagehandClose, { closed: true }); + client.queueResponse(StagehandMethods.stagehandClose, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); @@ -181,8 +191,8 @@ describe("Stagehand TS object wrapper", () => { it("routes context.setActivePage and context.close", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.contextSetActivePage, { ok: true }); - client.queueResponse(StagehandMethods.contextClose, { closed: true }); + client.queueResponse(StagehandMethods.contextSetActivePage, true); + client.queueResponse(StagehandMethods.contextClose, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); const page = new Page(client, { pageId: "page-1" }); @@ -199,8 +209,8 @@ describe("Stagehand TS object wrapper", () => { it("normalizes context init script content and functions", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.contextAddInitScript, { ok: true }); - client.queueResponse(StagehandMethods.contextAddInitScript, { ok: true }); + client.queueResponse(StagehandMethods.contextAddInitScript, true); + client.queueResponse(StagehandMethods.contextAddInitScript, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); const script = (arg: { ready: boolean }) => { @@ -223,7 +233,7 @@ describe("Stagehand TS object wrapper", () => { it("routes context headers and adapts domain policy results", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.contextSetExtraHTTPHeaders, { ok: true }); + client.queueResponse(StagehandMethods.contextSetExtraHTTPHeaders, true); client.queueResponse(StagehandMethods.contextGetDomainPolicy, { policy: { allowedDomains: ["example.com"], @@ -231,8 +241,8 @@ describe("Stagehand TS object wrapper", () => { }, }); client.queueResponse(StagehandMethods.contextGetDomainPolicy, { policy: null }); - client.queueResponse(StagehandMethods.contextSetDomainPolicy, { ok: true }); - client.queueResponse(StagehandMethods.contextSetDomainPolicy, { ok: true }); + client.queueResponse(StagehandMethods.contextSetDomainPolicy, true); + client.queueResponse(StagehandMethods.contextSetDomainPolicy, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); @@ -276,9 +286,9 @@ describe("Stagehand TS object wrapper", () => { }; client.queueResponse(StagehandMethods.contextCookies, { cookies: [cookie] }); client.queueResponse(StagehandMethods.contextCookies, { cookies: [] }); - client.queueResponse(StagehandMethods.contextAddCookies, { ok: true }); - client.queueResponse(StagehandMethods.contextClearCookies, { ok: true }); - client.queueResponse(StagehandMethods.contextClearCookies, { ok: true }); + client.queueResponse(StagehandMethods.contextAddCookies, true); + client.queueResponse(StagehandMethods.contextClearCookies, true); + client.queueResponse(StagehandMethods.contextClearCookies, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); const cookieParam = { @@ -322,11 +332,11 @@ describe("Stagehand TS object wrapper", () => { it("lazily exposes a clipboard facade and routes all clipboard operations", async () => { const client = new FakeProtocolClient(); client.queueResponse(StagehandMethods.contextClipboardReadText, { text: "clipboard text" }); - client.queueResponse(StagehandMethods.contextClipboardWriteText, { ok: true }); - client.queueResponse(StagehandMethods.contextClipboardClear, { ok: true }); - client.queueResponse(StagehandMethods.contextClipboardPaste, { ok: true }); - client.queueResponse(StagehandMethods.contextClipboardCopy, { ok: true }); - client.queueResponse(StagehandMethods.contextClipboardCut, { ok: true }); + client.queueResponse(StagehandMethods.contextClipboardWriteText, true); + client.queueResponse(StagehandMethods.contextClipboardClear, true); + client.queueResponse(StagehandMethods.contextClipboardPaste, true); + client.queueResponse(StagehandMethods.contextClipboardCopy, true); + client.queueResponse(StagehandMethods.contextClipboardCut, true); const stagehand = createStagehandWithClientForTest(client); await stagehand.init(); const page = new Page(client, { pageId: "page-1" }); @@ -490,8 +500,8 @@ describe("Stagehand TS object wrapper", () => { it("routes page keyboard interactions", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.pageType, { ok: true }); - client.queueResponse(StagehandMethods.pageKeyPress, { ok: true }); + client.queueResponse(StagehandMethods.pageType, true); + client.queueResponse(StagehandMethods.pageKeyPress, true); const page = new Page(client, { pageId: "page-1" }); await page.type("hello", { delay: 25, withMistakes: true }); @@ -533,8 +543,8 @@ describe("Stagehand TS object wrapper", () => { it("normalizes page init script content and functions", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.pageAddInitScript, { ok: true }); - client.queueResponse(StagehandMethods.pageAddInitScript, { ok: true }); + client.queueResponse(StagehandMethods.pageAddInitScript, true); + client.queueResponse(StagehandMethods.pageAddInitScript, true); const page = new Page(client, { pageId: "page-1" }); const script = (arg: { ready: boolean }) => { globalThis.document.title = String(arg.ready); @@ -557,8 +567,8 @@ describe("Stagehand TS object wrapper", () => { it("routes page headers and viewport configuration", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.pageSetExtraHTTPHeaders, { ok: true }); - client.queueResponse(StagehandMethods.pageSetViewportSize, { ok: true }); + client.queueResponse(StagehandMethods.pageSetExtraHTTPHeaders, true); + client.queueResponse(StagehandMethods.pageSetViewportSize, true); const page = new Page(client, { pageId: "page-1" }); await page.setExtraHTTPHeaders({ "X-Request-ID": "request-1", doNotRenameMe: "value" }); @@ -580,8 +590,8 @@ describe("Stagehand TS object wrapper", () => { it("routes page wait methods and unwraps selector results", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.pageWaitForLoadState, { ok: true }); - client.queueResponse(StagehandMethods.pageWaitForTimeout, { ok: true }); + client.queueResponse(StagehandMethods.pageWaitForLoadState, true); + client.queueResponse(StagehandMethods.pageWaitForTimeout, true); client.queueResponse(StagehandMethods.pageWaitForSelector, { matched: false }); const page = new Page(client, { pageId: "page-1" }); @@ -687,7 +697,7 @@ describe("Stagehand TS object wrapper", () => { it("routes page.close", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.pageClose, { closed: true }); + client.queueResponse(StagehandMethods.pageClose, true); const page = new Page(client, { pageId: "page-1" }); await page.close(); @@ -894,7 +904,7 @@ describe("Stagehand TS object wrapper", () => { it("routes locator.click with the page descriptor", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.locatorClick, { clicked: true }); + client.queueResponse(StagehandMethods.locatorClick, true); const page = new Page(client, { pageId: "page-1" }); await page.locator("button").click({ @@ -916,7 +926,7 @@ describe("Stagehand TS object wrapper", () => { it("routes locator.fill with the page descriptor", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.locatorFill, { filled: true }); + client.queueResponse(StagehandMethods.locatorFill, true); const page = new Page(client, { pageId: "page-1" }); await page.locator("#email").fill("user@example.com"); @@ -988,11 +998,11 @@ describe("Stagehand TS object wrapper", () => { it("routes write locator methods with their options", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.locatorHover, { hovered: true }); - client.queueResponse(StagehandMethods.locatorScrollTo, { scrolled: true }); - client.queueResponse(StagehandMethods.locatorHighlight, { highlighted: true }); - client.queueResponse(StagehandMethods.locatorSendClickEvent, { clicked: true }); - client.queueResponse(StagehandMethods.locatorType, { typed: true }); + client.queueResponse(StagehandMethods.locatorHover, true); + client.queueResponse(StagehandMethods.locatorScrollTo, true); + client.queueResponse(StagehandMethods.locatorHighlight, true); + client.queueResponse(StagehandMethods.locatorSendClickEvent, true); + client.queueResponse(StagehandMethods.locatorType, true); client.queueResponse(StagehandMethods.locatorSelectOption, { values: ["pro"] }); const page = new Page(client, { pageId: "page-1" }); const locator = page.locator("#field"); @@ -1037,7 +1047,7 @@ describe("Stagehand TS object wrapper", () => { it("creates descriptor-backed nth locators without sending protocol calls", async () => { const client = new FakeProtocolClient(); - client.queueResponse(StagehandMethods.locatorClick, { clicked: true }); + client.queueResponse(StagehandMethods.locatorClick, true); const page = new Page(client, { pageId: "page-1" }); const locator = page.locator("button").first().nth(2); diff --git a/packages/sdk-ts/tests/stagehand.test.ts b/packages/sdk-ts/tests/stagehand.test.ts index c5c5ef538..be9a073aa 100644 --- a/packages/sdk-ts/tests/stagehand.test.ts +++ b/packages/sdk-ts/tests/stagehand.test.ts @@ -429,7 +429,7 @@ describe("Stagehand", () => { it("prints info and higher logs while hiding debug by default", async () => { const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true); const rpcClient = new FakeRPCClient(); - rpcClient.queueResponse(StagehandMethods.stagehandClose, { closed: true }); + rpcClient.queueResponse(StagehandMethods.stagehandClose, true); const stagehand = createStagehandWithDependenciesForTest( { browser: { @@ -543,7 +543,7 @@ describe("Stagehand", () => { it("does not close a keepAlive browser source", async () => { const closeBrowser = vi.fn(); const rpcClient = new FakeRPCClient(); - rpcClient.queueResponse(StagehandMethods.stagehandClose, { closed: true }); + rpcClient.queueResponse(StagehandMethods.stagehandClose, true); const stagehand = createStagehandWithDependenciesForTest( { browser: { diff --git a/packages/server/controllers/stagehandController.ts b/packages/server/controllers/stagehandController.ts index 543ee41a5..22859356b 100644 --- a/packages/server/controllers/stagehandController.ts +++ b/packages/server/controllers/stagehandController.ts @@ -21,7 +21,7 @@ export function createStagehandController(runtime: StagehandRuntime) { async function close(_params: EmptyParams, { logger }: HandlerContext) { logger.info("stagehand.close", {}); await runtime.close(); - return { closed: true as const }; + return true as const; } async function act(params: StagehandActParams, { logger }: HandlerContext) { diff --git a/packages/server/runtime.ts b/packages/server/runtime.ts index ac8a6e0cd..d84992371 100644 --- a/packages/server/runtime.ts +++ b/packages/server/runtime.ts @@ -1,4 +1,5 @@ import type { + AcknowledgementResult, BrowserGetVersionResult, ClearCookieOptions, ContextActivePageResult, @@ -12,7 +13,6 @@ import type { ContextClipboardReadTextParams, ContextClipboardReadTextResult, ContextClipboardWriteTextParams, - ContextCloseResult, ContextCookiesParams, ContextCookiesResult, ContextGetDomainPolicyResult, @@ -21,7 +21,6 @@ import type { ContextSetActivePageParams, ContextSetDomainPolicyParams, ContextSetExtraHTTPHeadersParams, - ContextVoidResult, Cookie, CookieFilter, CookieParam, @@ -30,31 +29,23 @@ import type { LLMGenerateResult, LoadState, LocatorClickParams, - LocatorClickResult, LocatorCentroidResult, LocatorCountResult, LocatorDescriptor, LocatorFillParams, - LocatorFillResult, LocatorHighlightParams, - LocatorHighlightResult, - LocatorHoverResult, LocatorInnerHtmlResult, LocatorInnerTextResult, LocatorInputValueResult, LocatorIsCheckedResult, LocatorIsVisibleResult, LocatorScrollToParams, - LocatorScrollToResult, LocatorSelectOptionParams, LocatorSelectOptionResult, LocatorSendClickEventParams, - LocatorSendClickEventResult, LocatorTextContentResult, LocatorTypeParams, - LocatorTypeResult, PageClickParams, - PageCloseResult, PageCoordinateResult, PageAddInitScriptParams, PageDragAndDropParams, @@ -81,7 +72,6 @@ import type { PageTitleResult, PageTypeParams, PageUrlResult, - PageVoidResult, PageWaitForLoadStateParams, PageWaitForSelectorParams, PageWaitForSelectorResult, @@ -344,50 +334,52 @@ export class StagehandRuntime { return pageRefFromUnderstudyPage(page); } - async contextSetActivePage(params: ContextSetActivePageParams): Promise { + async contextSetActivePage(params: ContextSetActivePageParams): Promise { const page = this.resolvePage(params.pageId); await this.requireBrowserSession().setActivePage(page); - return { ok: true }; + return true as const; } - async contextClose(): Promise { + async contextClose(): Promise { await this.close(); - return { closed: true }; + return true as const; } - async contextAddInitScript(params: ContextAddInitScriptParams): Promise { + async contextAddInitScript(params: ContextAddInitScriptParams): Promise { await this.requireBrowserSession().addInitScript(params.source); - return { ok: true }; + return true as const; } async contextSetExtraHTTPHeaders( params: ContextSetExtraHTTPHeadersParams, - ): Promise { + ): Promise { await this.requireBrowserSession().setExtraHTTPHeaders(params.headers); - return { ok: true }; + return true as const; } contextGetDomainPolicy(): ContextGetDomainPolicyResult { return { policy: this.requireBrowserSession().getDomainPolicy() }; } - async contextSetDomainPolicy(params: ContextSetDomainPolicyParams): Promise { + async contextSetDomainPolicy( + params: ContextSetDomainPolicyParams, + ): Promise { await this.requireBrowserSession().setDomainPolicy(params.policy); - return { ok: true }; + return true as const; } async contextCookies(params: ContextCookiesParams): Promise { return { cookies: await this.requireBrowserSession().cookies(params.urls) }; } - async contextAddCookies(params: ContextAddCookiesParams): Promise { + async contextAddCookies(params: ContextAddCookiesParams): Promise { await this.requireBrowserSession().addCookies(params.cookies); - return { ok: true }; + return true as const; } - async contextClearCookies(params: ContextClearCookiesParams): Promise { + async contextClearCookies(params: ContextClearCookiesParams): Promise { await this.requireBrowserSession().clearCookies(hydrateClearCookieOptions(params.options)); - return { ok: true }; + return true as const; } async contextClipboardReadText( @@ -399,19 +391,19 @@ export class StagehandRuntime { async contextClipboardWriteText( params: ContextClipboardWriteTextParams, - ): Promise { + ): Promise { const clipboard = this.requireBrowserSession().clipboard; await clipboard.writeText(params.text, this.clipboardOptions(params.pageId)); - return { ok: true }; + return true as const; } - async contextClipboardClear(params: ContextClipboardClearParams): Promise { + async contextClipboardClear(params: ContextClipboardClearParams): Promise { const clipboard = this.requireBrowserSession().clipboard; await clipboard.clear(this.clipboardOptions(params.pageId)); - return { ok: true }; + return true as const; } - async contextClipboardPaste(params: ContextClipboardPasteParams): Promise { + async contextClipboardPaste(params: ContextClipboardPasteParams): Promise { const clipboard = this.requireBrowserSession().clipboard; const pageOptions = this.clipboardOptions(params.pageId); const options = @@ -422,19 +414,19 @@ export class StagehandRuntime { } : undefined; await clipboard.paste(options); - return { ok: true }; + return true as const; } - async contextClipboardCopy(params: ContextClipboardCopyParams): Promise { + async contextClipboardCopy(params: ContextClipboardCopyParams): Promise { const clipboard = this.requireBrowserSession().clipboard; await clipboard.copy(this.clipboardOptions(params.pageId)); - return { ok: true }; + return true as const; } - async contextClipboardCut(params: ContextClipboardCutParams): Promise { + async contextClipboardCut(params: ContextClipboardCutParams): Promise { const clipboard = this.requireBrowserSession().clipboard; await clipboard.cut(this.clipboardOptions(params.pageId)); - return { ok: true }; + return true as const; } async pageGoto(params: PageGotoParams): Promise { @@ -490,14 +482,14 @@ export class StagehandRuntime { return { fromXpath, toXpath }; } - async pageType(params: PageTypeParams): Promise { + async pageType(params: PageTypeParams): Promise { await this.resolvePage(params.pageId).type(params.text, params.options); - return { ok: true }; + return true as const; } - async pageKeyPress(params: PageKeyPressParams): Promise { + async pageKeyPress(params: PageKeyPressParams): Promise { await this.resolvePage(params.pageId).keyPress(params.key, params.options); - return { ok: true }; + return true as const; } async pageEvaluate(params: PageEvaluateParams): Promise { @@ -507,33 +499,35 @@ export class StagehandRuntime { }; } - async pageAddInitScript(params: PageAddInitScriptParams): Promise { + async pageAddInitScript(params: PageAddInitScriptParams): Promise { await this.resolvePage(params.pageId).addInitScript(params.source); - return { ok: true }; + return true as const; } - async pageSetExtraHTTPHeaders(params: PageSetExtraHTTPHeadersParams): Promise { + async pageSetExtraHTTPHeaders( + params: PageSetExtraHTTPHeadersParams, + ): Promise { await this.resolvePage(params.pageId).setExtraHTTPHeaders(params.headers); - return { ok: true }; + return true as const; } - async pageSetViewportSize(params: PageSetViewportSizeParams): Promise { + async pageSetViewportSize(params: PageSetViewportSizeParams): Promise { await this.resolvePage(params.pageId).setViewportSize( params.width, params.height, params.options, ); - return { ok: true }; + return true as const; } - async pageWaitForLoadState(params: PageWaitForLoadStateParams): Promise { + async pageWaitForLoadState(params: PageWaitForLoadStateParams): Promise { await this.resolvePage(params.pageId).waitForLoadState(params.state, params.timeout); - return { ok: true }; + return true as const; } - async pageWaitForTimeout(params: PageWaitForTimeoutParams): Promise { + async pageWaitForTimeout(params: PageWaitForTimeoutParams): Promise { await this.resolvePage(params.pageId).waitForTimeout(params.ms); - return { ok: true }; + return true as const; } async pageWaitForSelector(params: PageWaitForSelectorParams): Promise { @@ -585,26 +579,26 @@ export class StagehandRuntime { }; } - async pageClose(params: PageIdParams): Promise { + async pageClose(params: PageIdParams): Promise { const page = this.resolvePage(params.pageId); await page.close(); this.pagesById.delete(params.pageId); - return { closed: true }; + return true as const; } - async locatorClick(params: LocatorClickParams): Promise { + async locatorClick(params: LocatorClickParams): Promise { await this.resolveLocator(params).click(params.options); - return { clicked: true }; + return true as const; } - async locatorHover(params: LocatorDescriptor): Promise { + async locatorHover(params: LocatorDescriptor): Promise { await this.resolveLocator(params).hover(); - return { hovered: true }; + return true as const; } - async locatorFill(params: LocatorFillParams): Promise { + async locatorFill(params: LocatorFillParams): Promise { await this.resolveLocator(params).fill(params.value); - return { filled: true }; + return true as const; } async locatorCount(params: LocatorDescriptor): Promise { @@ -649,30 +643,28 @@ export class StagehandRuntime { }; } - async locatorScrollTo(params: LocatorScrollToParams): Promise { + async locatorScrollTo(params: LocatorScrollToParams): Promise { await this.resolveLocator(params).scrollTo(params.percent); - return { scrolled: true }; + return true as const; } async locatorCentroid(params: LocatorDescriptor): Promise { return await this.resolveLocator(params).centroid(); } - async locatorHighlight(params: LocatorHighlightParams): Promise { + async locatorHighlight(params: LocatorHighlightParams): Promise { await this.resolveLocator(params).highlight(params.options); - return { highlighted: true }; + return true as const; } - async locatorSendClickEvent( - params: LocatorSendClickEventParams, - ): Promise { + async locatorSendClickEvent(params: LocatorSendClickEventParams): Promise { await this.resolveLocator(params).sendClickEvent(params.options); - return { clicked: true }; + return true as const; } - async locatorType(params: LocatorTypeParams): Promise { + async locatorType(params: LocatorTypeParams): Promise { await this.resolveLocator(params).type(params.text, params.options); - return { typed: true }; + return true as const; } async locatorSelectOption(params: LocatorSelectOptionParams): Promise { diff --git a/packages/server/tests/rpc-router.test.ts b/packages/server/tests/rpc-router.test.ts index 028aa8113..65ac54d95 100644 --- a/packages/server/tests/rpc-router.test.ts +++ b/packages/server/tests/rpc-router.test.ts @@ -109,7 +109,7 @@ describe("Stagehand RPC router", () => { await expect( router.handle(request({ id: 13, method: "stagehand.close", params: {} })), - ).resolves.toStrictEqual({ closed: true }); + ).resolves.toStrictEqual(true); expect(lifecycle.slice(-2)).toStrictEqual(["ended:stagehand.close", "shutdown"]); }); diff --git a/packages/server/tests/stagehand-clients.test.ts b/packages/server/tests/stagehand-clients.test.ts index 5783dc7d3..9b30e0f3e 100644 --- a/packages/server/tests/stagehand-clients.test.ts +++ b/packages/server/tests/stagehand-clients.test.ts @@ -835,9 +835,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 5, - result: { - closed: true, - }, + result: true, }); expect(session.closed).toBe(true); @@ -1032,7 +1030,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 10, - result: { ok: true }, + result: true, }); expect(context.setActivePageCalls).toStrictEqual([pageA]); @@ -1146,7 +1144,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 9, - result: { closed: true }, + result: true, }); expect(context.closed).toBe(true); }); @@ -1162,7 +1160,7 @@ describe("Stagehand worker clients", () => { method: "context.add_init_script", params: { source: "globalThis.ready = true" }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1172,7 +1170,7 @@ describe("Stagehand worker clients", () => { headers: { "X-Request-ID": "request-1", doNotRenameMe: "value" }, }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1185,7 +1183,7 @@ describe("Stagehand worker clients", () => { }, }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1227,7 +1225,7 @@ describe("Stagehand worker clients", () => { method: "context.set_domain_policy", params: { policy: null }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1302,7 +1300,7 @@ describe("Stagehand worker clients", () => { ], }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1315,7 +1313,7 @@ describe("Stagehand worker clients", () => { }, }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1323,7 +1321,7 @@ describe("Stagehand worker clients", () => { method: "context.clear_cookies", params: {}, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); expect(context.cookiesCalls).toStrictEqual([["https://example.test/account"]]); expect(context.addCookiesCalls).toStrictEqual([ @@ -1369,7 +1367,7 @@ describe("Stagehand worker clients", () => { method: "context.clipboard_write_text", params: { text: "new clipboard text" }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1377,7 +1375,7 @@ describe("Stagehand worker clients", () => { method: "context.clipboard_clear", params: { page_id: "page-a" }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1385,7 +1383,7 @@ describe("Stagehand worker clients", () => { method: "context.clipboard_paste", params: { page_id: "page-a", shortcut: "Meta+V" }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1393,7 +1391,7 @@ describe("Stagehand worker clients", () => { method: "context.clipboard_copy", params: {}, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); await expect( handle({ jsonrpc: "2.0", @@ -1401,7 +1399,7 @@ describe("Stagehand worker clients", () => { method: "context.clipboard_cut", params: { page_id: "page-a" }, }), - ).resolves.toMatchObject({ result: { ok: true } }); + ).resolves.toMatchObject({ result: true }); expect(context.clipboard.readTextCalls).toStrictEqual([{ page }]); expect(context.clipboard.writeTextCalls).toStrictEqual([ @@ -1640,7 +1638,7 @@ describe("Stagehand worker clients", () => { options: { delay: 25, with_mistakes: true }, }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 20, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 20, result: true }); await expect( handle({ @@ -1649,7 +1647,7 @@ describe("Stagehand worker clients", () => { method: "page.key_press", params: { page_id: "page-a", key: "Control+A", options: { delay: 10 } }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 21, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 21, result: true }); expect(page.pageTypeCalls).toStrictEqual([ { text: "hello", options: { delay: 25, withMistakes: true } }, @@ -1696,7 +1694,7 @@ describe("Stagehand worker clients", () => { method: "page.add_init_script", params: { page_id: "page-a", source: "globalThis.ready = true" }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 24, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 24, result: true }); expect(page.evaluateCalls).toStrictEqual(["({ camelCase: true })", "undefined"]); expect(page.addInitScriptCalls).toStrictEqual(["globalThis.ready = true"]); @@ -1716,7 +1714,7 @@ describe("Stagehand worker clients", () => { headers: { "X-Request-ID": "request-1", doNotRenameMe: "value" }, }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 25, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 25, result: true }); await expect( handle({ @@ -1730,7 +1728,7 @@ describe("Stagehand worker clients", () => { options: { device_scale_factor: 2 }, }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 26, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 26, result: true }); expect(page.setExtraHTTPHeadersCalls).toStrictEqual([ { "X-Request-ID": "request-1", doNotRenameMe: "value" }, @@ -1752,7 +1750,7 @@ describe("Stagehand worker clients", () => { method: "page.wait_for_load_state", params: { page_id: "page-a", state: "networkidle", timeout: 0 }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 27, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 27, result: true }); await expect( handle({ @@ -1761,7 +1759,7 @@ describe("Stagehand worker clients", () => { method: "page.wait_for_timeout", params: { page_id: "page-a", ms: 250 }, }), - ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 28, result: { ok: true } }); + ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 28, result: true }); await expect( handle({ @@ -1917,9 +1915,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 12, - result: { - closed: true, - }, + result: true, }); expect(page.closed).toBe(true); @@ -1984,9 +1980,7 @@ describe("Stagehand worker clients", () => { clickCount: 2, }, }), - ).resolves.toStrictEqual({ - clicked: true, - }); + ).resolves.toStrictEqual(true); expect(page.locatorRefs).toHaveLength(1); expect(page.locatorRefs[0]?.selector).toBe("button.submit"); @@ -2008,9 +2002,7 @@ describe("Stagehand worker clients", () => { selector: "input[name=email]", value: "user@example.com", }), - ).resolves.toStrictEqual({ - filled: true, - }); + ).resolves.toStrictEqual(true); expect(page.locatorRefs).toHaveLength(1); expect(page.locatorRefs[0]?.selector).toBe("input[name=email]"); @@ -2123,22 +2115,22 @@ describe("Stagehand worker clients", () => { selector: "input.email", }; - await expect(runtime.locatorHover(descriptor)).resolves.toStrictEqual({ hovered: true }); - await expect(runtime.locatorScrollTo({ ...descriptor, percent: 50 })).resolves.toStrictEqual({ - scrolled: true, - }); + await expect(runtime.locatorHover(descriptor)).resolves.toStrictEqual(true); + await expect(runtime.locatorScrollTo({ ...descriptor, percent: 50 })).resolves.toStrictEqual( + true, + ); await expect( runtime.locatorHighlight({ ...descriptor, options: { durationMs: 0, borderColor: { r: 1, g: 2, b: 3 } }, }), - ).resolves.toStrictEqual({ highlighted: true }); + ).resolves.toStrictEqual(true); await expect( runtime.locatorSendClickEvent({ ...descriptor, options: { detail: 2 } }), - ).resolves.toStrictEqual({ clicked: true }); + ).resolves.toStrictEqual(true); await expect( runtime.locatorType({ ...descriptor, text: "hello", options: { delay: 1 } }), - ).resolves.toStrictEqual({ typed: true }); + ).resolves.toStrictEqual(true); await expect( runtime.locatorSelectOption({ ...descriptor, values: ["a", "b"] }), ).resolves.toStrictEqual({ values: ["b"] }); @@ -2195,9 +2187,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 13, - result: { - clicked: true, - }, + result: true, }); expect(page.locatorRefs).toHaveLength(1); @@ -2228,9 +2218,7 @@ describe("Stagehand worker clients", () => { ).resolves.toStrictEqual({ jsonrpc: "2.0", id: 14, - result: { - filled: true, - }, + result: true, }); expect(page.locatorRefs).toHaveLength(1);