@stablyai/playwright-base 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isPage = isPage;
4
4
  exports.isLocator = isLocator;
5
5
  function isPage(candidate) {
6
- return (typeof candidate === 'object' &&
6
+ return (typeof candidate === "object" &&
7
7
  candidate !== null &&
8
- typeof candidate.screenshot === 'function' &&
9
- typeof candidate.goto === 'function');
8
+ typeof candidate.screenshot === "function" &&
9
+ typeof candidate.goto === "function");
10
10
  }
11
11
  function isLocator(candidate) {
12
- return (typeof candidate === 'object' &&
12
+ return (typeof candidate === "object" &&
13
13
  candidate !== null &&
14
- typeof candidate.screenshot === 'function' &&
15
- typeof candidate.nth === 'function');
14
+ typeof candidate.screenshot === "function" &&
15
+ typeof candidate.nth === "function");
16
16
  }
package/dist/runtime.js CHANGED
@@ -13,7 +13,7 @@ function getApiKey() {
13
13
  function requireApiKey() {
14
14
  const apiKey = getApiKey();
15
15
  if (!apiKey) {
16
- throw new Error('Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.');
16
+ throw new Error("Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.");
17
17
  }
18
18
  return apiKey;
19
19
  }
@@ -0,0 +1,3 @@
1
+ import type { Locator, Page } from "@stablyai/internal-playwright-test";
2
+ import type { ScreenshotPromptOptions } from "./index";
3
+ export declare function takeStableScreenshot(target: Page | Locator, options?: ScreenshotPromptOptions): Promise<Buffer>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.takeStableScreenshot = takeStableScreenshot;
4
+ const playwright_type_predicates_1 = require("./playwright-type-predicates");
5
+ const image_compare_1 = require("./image-compare");
6
+ async function takeStableScreenshot(target, options) {
7
+ const page = (0, playwright_type_predicates_1.isPage)(target) ? target : target.page();
8
+ // Use a small budget for stabilization within the overall assertion timeout.
9
+ // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).
10
+ const totalTimeout = options?.timeout ?? 5000;
11
+ // Budget is 25% of the total timeout
12
+ const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);
13
+ const stabilityBudgetMs = Math.min(2000, Math.max(300, stabilizationBudgetMs));
14
+ const deadline = Date.now() + stabilityBudgetMs;
15
+ let actual;
16
+ let previous;
17
+ const pollIntervals = [0, 100, 250, 500];
18
+ let isFirstIteration = true;
19
+ while (true) {
20
+ if (Date.now() >= deadline)
21
+ break;
22
+ const delay = pollIntervals.length ? pollIntervals.shift() : 1000;
23
+ if (delay) {
24
+ await page.waitForTimeout(delay);
25
+ }
26
+ previous = actual;
27
+ actual = await target.screenshot(options);
28
+ if (!isFirstIteration &&
29
+ actual &&
30
+ previous &&
31
+ (0, image_compare_1.imagesAreSimilar)({
32
+ image1: previous,
33
+ image2: actual,
34
+ threshold: options?.threshold ?? 0.02,
35
+ })) {
36
+ return actual;
37
+ }
38
+ isFirstIteration = false;
39
+ }
40
+ return actual ?? (await target.screenshot(options));
41
+ }
package/package.json CHANGED
@@ -1,20 +1,29 @@
1
1
  {
2
2
  "name": "@stablyai/playwright-base",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Shared augmentation runtime for Stably Playwright wrappers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "engines": {
8
11
  "node": ">=18"
9
12
  },
10
- "dependencies": {},
13
+ "dependencies": {
14
+ "@stablyai/internal-playwright-test": "^0.1.1",
15
+ "jpeg-js": "^0.4.4",
16
+ "pixelmatch": "^5.3.0",
17
+ "pngjs": "^7.0.0"
18
+ },
11
19
  "peerDependencies": {
12
- "@playwright/test": "^1.44.0",
13
- "playwright": "^1.44.0",
14
- "zod": "^4.0.0"
20
+ "zod": "^3.25.0 || ^4.0.0"
15
21
  },
16
22
  "devDependencies": {
17
- "zod": "^4.0.0"
23
+ "@types/jpeg-js": "^0.3.7",
24
+ "@types/pngjs": "^6.0.5",
25
+ "@types/pixelmatch": "^5.2.6",
26
+ "zod": "^3.25.0 || ^4.0.0"
18
27
  },
19
28
  "scripts": {
20
29
  "build": "tsc -p tsconfig.build.json"
package/src/ai/extract.ts DELETED
@@ -1,97 +0,0 @@
1
- import type { Locator, Page } from 'playwright';
2
- import * as z4 from 'zod/v4/core';
3
- import { z } from 'zod';
4
- import { requireApiKey } from '../runtime';
5
-
6
- export type ExtractSchema = z4.$ZodType;
7
-
8
- export type SchemaOutput<T extends ExtractSchema> = z4.output<T>;
9
-
10
- type ExtractIssue = z4.$ZodIssue;
11
-
12
- const EXTRACT_ENDPOINT = 'https://api.stably.ai/internal/v1/extract';
13
-
14
- const zSuccess = z.object({ value: z.unknown() });
15
- const zError = z.object({ error: z.string() });
16
-
17
- class ExtractValidationError extends Error {
18
- constructor(
19
- message: string,
20
- readonly issues: ReadonlyArray<ExtractIssue>,
21
- ) {
22
- super(message);
23
- this.name = 'ExtractValidationError';
24
- }
25
- }
26
-
27
- async function validateWithSchema<T extends ExtractSchema>(
28
- schema: T,
29
- value: unknown,
30
- ): Promise<SchemaOutput<T>> {
31
- const result = await z4.safeParseAsync(schema, value);
32
- if (!result.success) {
33
- throw new ExtractValidationError('Validation failed', result.error.issues);
34
- }
35
-
36
- return result.data;
37
- }
38
-
39
- type ExtractSubject = Page | Locator;
40
-
41
- type BaseExtractArgs = {
42
- prompt: string;
43
- pageOrLocator: ExtractSubject;
44
- };
45
-
46
- type ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {
47
- schema: T;
48
- };
49
-
50
- export async function extract(args: BaseExtractArgs): Promise<string>;
51
- export async function extract<T extends ExtractSchema>(
52
- args: ExtractArgsWithSchema<T>,
53
- ): Promise<SchemaOutput<T>>;
54
- export async function extract<T extends ExtractSchema>({
55
- prompt,
56
- pageOrLocator,
57
- schema,
58
- }: BaseExtractArgs & { schema?: T }): Promise<string | SchemaOutput<T>> {
59
- const jsonSchema = schema ? z4.toJSONSchema(schema) : undefined;
60
-
61
- const apiKey = requireApiKey();
62
-
63
- const form = new FormData();
64
- form.append('prompt', prompt);
65
- if (jsonSchema) {
66
- form.append('jsonSchema', JSON.stringify(jsonSchema));
67
- }
68
-
69
- const pngBuffer = await pageOrLocator.screenshot({ type: 'png' }); // Buffer
70
- const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array
71
- const blob = new Blob([u8], { type: 'image/png' });
72
- form.append('image', blob, 'screenshot.png');
73
-
74
- const response = await fetch(EXTRACT_ENDPOINT, {
75
- method: 'POST',
76
- headers: {
77
- Authorization: `Bearer ${apiKey}`,
78
- },
79
- body: form,
80
- });
81
-
82
- const parsed = await response.json().catch(() => undefined as unknown);
83
-
84
- if (response.ok) {
85
- const { value } = zSuccess.parse(parsed);
86
- return (
87
- schema ? await validateWithSchema(schema, value)
88
- : typeof value === 'string' ? value
89
- : JSON.stringify(value)
90
- );
91
- }
92
-
93
- const err = zError.safeParse(parsed);
94
- throw new Error(
95
- `Extract failed (${response.status})${err.success ? `: ${err.data.error}` : ''}`,
96
- );
97
- }
@@ -1,57 +0,0 @@
1
- const PROMPT_ASSERTION_ENDPOINT = 'https://api.stably.ai/internal/v1/assert';
2
-
3
- import { z } from 'zod';
4
- import { requireApiKey } from '../runtime';
5
-
6
- const zSuccess = z.object({
7
- success: z.boolean(),
8
- reason: z.string().optional(),
9
- });
10
-
11
- const zError = z.object({
12
- error: z.string(),
13
- });
14
-
15
- export async function verifyPrompt({
16
- prompt,
17
- screenshot,
18
- }: {
19
- prompt: string;
20
- screenshot: Uint8Array;
21
- }): Promise<{
22
- pass: boolean;
23
- reason?: string;
24
- }> {
25
- const apiKey = requireApiKey();
26
-
27
- const form = new FormData();
28
- form.append('prompt', prompt);
29
- const u8 = Uint8Array.from(screenshot);
30
- const blob = new Blob([u8], { type: 'image/png' });
31
- form.append('image', blob, 'screenshot.png');
32
-
33
- const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {
34
- method: 'POST',
35
- headers: {
36
- Authorization: `Bearer ${apiKey}`,
37
- },
38
- body: form,
39
- });
40
-
41
- const parsed = await response.json().catch(() => undefined as unknown);
42
-
43
- if (response.ok) {
44
- const { success, reason } = zSuccess.parse(parsed);
45
- return {
46
- pass: success,
47
- reason,
48
- };
49
- }
50
-
51
- const err = zError.safeParse(parsed);
52
- throw new Error(
53
- `Verify prompt failed (${response.status})${
54
- err.success ? `: ${err.data.error}` : ''
55
- }`,
56
- );
57
- }
package/src/expect.ts DELETED
@@ -1,134 +0,0 @@
1
- import type { Locator, Page } from 'playwright';
2
- import type { ScreenshotPromptOptions } from './index';
3
-
4
- import { isLocator, isPage } from './playwright-type-predicates';
5
-
6
- import { verifyPrompt } from './ai/verify-prompt';
7
-
8
- type VerifyTargetType = 'page' | 'locator';
9
-
10
- type MatcherContext = {
11
- isNot: boolean;
12
- message?: () => string;
13
- };
14
-
15
- function createFailureMessage({
16
- targetType,
17
- condition,
18
- didPass,
19
- isNot,
20
- reason,
21
- }: {
22
- targetType: VerifyTargetType;
23
- condition: string;
24
- didPass: boolean;
25
- isNot: boolean;
26
- reason?: string;
27
- }): string {
28
- const expectation = isNot ? 'not to satisfy' : 'to satisfy';
29
- const result = didPass ? 'it did' : 'it did not';
30
-
31
- let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;
32
- if (reason) {
33
- message += `\n\nReason: ${reason}`;
34
- }
35
-
36
- return message;
37
- }
38
-
39
- function areScreenshotsEqual(a: Uint8Array, b: Uint8Array): boolean {
40
- if (a.byteLength !== b.byteLength) {
41
- return false;
42
- }
43
-
44
- for (let index = 0; index < a.byteLength; index += 1) {
45
- if (a[index] !== b[index]) {
46
- return false;
47
- }
48
- }
49
-
50
- return true;
51
- }
52
-
53
- async function takeStableScreenshot(
54
- target: Page | Locator,
55
- options?: ScreenshotPromptOptions,
56
- ): Promise<Uint8Array> {
57
- const page = isPage(target) ? target : target.page();
58
-
59
- // Use a small budget for stabilization within the overall assertion timeout.
60
- // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).
61
- const totalTimeout =
62
- (options as { timeout?: number } | undefined)?.timeout ?? 5000;
63
- // Budget is 25% of the total timeout
64
- const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);
65
- const stabilityBudgetMs = Math.min(
66
- 2000,
67
- Math.max(300, stabilizationBudgetMs),
68
- );
69
- const deadline = Date.now() + stabilityBudgetMs;
70
-
71
- let actual: Uint8Array | undefined;
72
- let previous: Uint8Array | undefined;
73
- const pollIntervals = [0, 100, 250, 500];
74
- let isFirstIteration = true;
75
-
76
- while (true) {
77
- if (Date.now() >= deadline) break;
78
- const delay = pollIntervals.length ? pollIntervals.shift()! : 1000;
79
- if (delay) {
80
- await page.waitForTimeout(delay);
81
- }
82
- previous = actual;
83
- const rawScreenshot = await target.screenshot(options);
84
- actual = Uint8Array.from(rawScreenshot);
85
- if (
86
- !isFirstIteration &&
87
- actual &&
88
- previous &&
89
- areScreenshotsEqual(actual, previous)
90
- ) {
91
- return actual;
92
- }
93
- isFirstIteration = false;
94
- }
95
- return actual ?? Uint8Array.from(await target.screenshot(options));
96
- }
97
-
98
- export const stablyPlaywrightMatchers = {
99
- async toMatchScreenshotPrompt(
100
- this: MatcherContext,
101
- received: Page | Locator,
102
- condition: string,
103
- options?: ScreenshotPromptOptions,
104
- ) {
105
- const target =
106
- isPage(received) ? received
107
- : isLocator(received) ? received
108
- : undefined;
109
- if (!target) {
110
- // Should never happen
111
- throw new Error(
112
- 'toMatchScreenshotPrompt only supports Playwright Page and Locator instances.',
113
- );
114
- }
115
- const targetType: VerifyTargetType = isPage(target) ? 'page' : 'locator';
116
-
117
- // Wait for two consecutive identical screenshots before sending to AI
118
- const screenshot = await takeStableScreenshot(target, options);
119
-
120
- const verifyResult = await verifyPrompt({ prompt: condition, screenshot });
121
-
122
- return {
123
- pass: verifyResult.pass,
124
- message: () =>
125
- createFailureMessage({
126
- targetType,
127
- condition,
128
- didPass: verifyResult.pass,
129
- reason: verifyResult.reason,
130
- isNot: this.isNot,
131
- }),
132
- };
133
- },
134
- } as const;
package/src/index.ts DELETED
@@ -1,69 +0,0 @@
1
- import type { Page } from 'playwright';
2
- import type { LocatorDescribeOptions } from './playwright-augment/augment';
3
- import type { ExtractSchema, SchemaOutput } from './ai/extract';
4
-
5
- import {
6
- augmentBrowser,
7
- augmentBrowserContext,
8
- augmentBrowserType,
9
- augmentLocator,
10
- augmentPage,
11
- } from './playwright-augment/augment';
12
- import { stablyPlaywrightMatchers } from './expect';
13
- import { requireApiKey } from './runtime';
14
-
15
- export { setApiKey } from './runtime';
16
-
17
- export type { LocatorDescribeOptions } from './playwright-augment/augment';
18
- export type { ExtractSchema, SchemaOutput } from './ai/extract';
19
- export type ScreenshotPromptOptions =
20
- import('@playwright/test').PageAssertionsToHaveScreenshotOptions;
21
- export {
22
- augmentBrowser,
23
- augmentBrowserContext,
24
- augmentBrowserType,
25
- augmentLocator,
26
- augmentPage,
27
- stablyPlaywrightMatchers,
28
- requireApiKey,
29
- };
30
-
31
- export interface Expect<T = Page> {
32
- toMatchScreenshotPrompt(
33
- condition: string,
34
- options?: ScreenshotPromptOptions,
35
- ): Promise<void>;
36
- }
37
-
38
- declare module 'playwright' {
39
- interface Locator {
40
- extract(prompt: string): Promise<string>;
41
- extract<T extends ExtractSchema>(
42
- prompt: string,
43
- options: { schema: T },
44
- ): Promise<SchemaOutput<T>>;
45
- describe(description: string, options?: LocatorDescribeOptions): Locator;
46
- }
47
-
48
- interface Page {
49
- extract(prompt: string): Promise<string>;
50
- extract<T extends ExtractSchema>(
51
- prompt: string,
52
- options: { schema: T },
53
- ): Promise<SchemaOutput<T>>;
54
- }
55
-
56
- interface BrowserContext {
57
- agent(
58
- prompt: string,
59
- options: { page: Page; maxCycles?: number },
60
- ): Promise<{ success: boolean }>;
61
- }
62
-
63
- interface Browser {
64
- agent(
65
- prompt: string,
66
- options: { page: Page; maxCycles?: number },
67
- ): Promise<{ success: boolean }>;
68
- }
69
- }
@@ -1,207 +0,0 @@
1
- import type {
2
- Browser,
3
- BrowserContext,
4
- BrowserType,
5
- Locator,
6
- Page,
7
- } from 'playwright';
8
-
9
- import { createLocatorExtract, createPageExtract } from './methods/extract';
10
- import { createAgentStub } from './methods/agent';
11
-
12
- export interface LocatorDescribeOptions {
13
- autoHeal?: boolean;
14
- }
15
-
16
- const LOCATOR_PATCHED = Symbol.for('stably.playwright.locatorPatched');
17
- const LOCATOR_DESCRIBE_WRAPPED = Symbol.for(
18
- 'stably.playwright.locatorDescribeWrapped',
19
- );
20
- const PAGE_PATCHED = Symbol.for('stably.playwright.pagePatched');
21
- const CONTEXT_PATCHED = Symbol.for('stably.playwright.contextPatched');
22
- const BROWSER_PATCHED = Symbol.for('stably.playwright.browserPatched');
23
- const BROWSER_TYPE_PATCHED = Symbol.for('stably.playwright.browserTypePatched');
24
-
25
- function defineHiddenProperty<T, K extends PropertyKey>(
26
- target: T,
27
- key: K,
28
- value: unknown,
29
- ): void {
30
- Object.defineProperty(target as unknown as object, key, {
31
- value,
32
- enumerable: false,
33
- configurable: true,
34
- writable: true,
35
- });
36
- }
37
-
38
- export function augmentLocator<T extends Locator>(locator: T): T {
39
- if (
40
- (locator as unknown as { [LOCATOR_PATCHED]?: boolean })[LOCATOR_PATCHED]
41
- ) {
42
- return locator;
43
- }
44
-
45
- defineHiddenProperty(locator, 'extract', createLocatorExtract(locator));
46
-
47
- const markerTarget = locator as unknown as Record<PropertyKey, unknown>;
48
-
49
- if (
50
- typeof locator.describe === 'function' &&
51
- !markerTarget[LOCATOR_DESCRIBE_WRAPPED]
52
- ) {
53
- const originalDescribe = locator.describe.bind(locator);
54
- locator.describe = ((
55
- description: string,
56
- options?: LocatorDescribeOptions,
57
- ) => {
58
- void options;
59
- const result = originalDescribe(description);
60
- return result ? augmentLocator(result as Locator) : result;
61
- }) as Locator['describe'];
62
-
63
- defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);
64
- }
65
-
66
- defineHiddenProperty(locator, LOCATOR_PATCHED, true);
67
-
68
- return locator;
69
- }
70
-
71
- export function augmentPage<T extends Page>(page: T): T {
72
- if ((page as unknown as { [PAGE_PATCHED]?: boolean })[PAGE_PATCHED]) {
73
- return page;
74
- }
75
-
76
- const originalLocator = page.locator.bind(page);
77
- page.locator = ((...args: Parameters<Page['locator']>) => {
78
- const locator = originalLocator(...args);
79
- return augmentLocator(locator);
80
- }) as Page['locator'];
81
-
82
- defineHiddenProperty(page, 'extract', createPageExtract(page));
83
- defineHiddenProperty(page, PAGE_PATCHED, true);
84
-
85
- return page;
86
- }
87
-
88
- export function augmentBrowserContext<T extends BrowserContext>(context: T): T {
89
- if (
90
- (context as unknown as { [CONTEXT_PATCHED]?: boolean })[CONTEXT_PATCHED]
91
- ) {
92
- return context;
93
- }
94
-
95
- const originalNewPage = context.newPage.bind(context);
96
- context.newPage = (async (...args: Parameters<BrowserContext['newPage']>) => {
97
- const page = await originalNewPage(...args);
98
- return augmentPage(page);
99
- }) as BrowserContext['newPage'];
100
-
101
- const originalPages = context.pages?.bind(context);
102
- if (originalPages) {
103
- context.pages = (() =>
104
- originalPages().map((page) =>
105
- augmentPage(page),
106
- )) as BrowserContext['pages'];
107
- }
108
-
109
- if (!(context as unknown as { agent?: unknown }).agent) {
110
- defineHiddenProperty(context, 'agent', createAgentStub());
111
- }
112
-
113
- defineHiddenProperty(context, CONTEXT_PATCHED, true);
114
-
115
- return context;
116
- }
117
-
118
- export function augmentBrowser<T extends Browser>(browser: T): T {
119
- if (
120
- (browser as unknown as { [BROWSER_PATCHED]?: boolean })[BROWSER_PATCHED]
121
- ) {
122
- return browser;
123
- }
124
-
125
- const originalNewContext = browser.newContext.bind(browser);
126
- browser.newContext = (async (...args: Parameters<Browser['newContext']>) => {
127
- const context = await originalNewContext(...args);
128
- return augmentBrowserContext(context);
129
- }) as Browser['newContext'];
130
-
131
- const originalNewPage = browser.newPage.bind(browser);
132
- browser.newPage = (async (...args: Parameters<Browser['newPage']>) => {
133
- const page = await originalNewPage(...args);
134
- return augmentPage(page);
135
- }) as Browser['newPage'];
136
-
137
- const originalContexts = browser.contexts.bind(browser);
138
- browser.contexts = (() =>
139
- originalContexts().map((context) =>
140
- augmentBrowserContext(context),
141
- )) as Browser['contexts'];
142
-
143
- if (!(browser as unknown as { agent?: unknown }).agent) {
144
- defineHiddenProperty(browser, 'agent', createAgentStub());
145
- }
146
-
147
- defineHiddenProperty(browser, BROWSER_PATCHED, true);
148
-
149
- return browser;
150
- }
151
-
152
- export function augmentBrowserType<TBrowser extends Browser>(
153
- browserType: BrowserType<TBrowser>,
154
- ): BrowserType<TBrowser> {
155
- if (
156
- (browserType as unknown as { [BROWSER_TYPE_PATCHED]?: boolean })[
157
- BROWSER_TYPE_PATCHED
158
- ]
159
- ) {
160
- return browserType;
161
- }
162
-
163
- const originalLaunch = browserType.launch.bind(browserType);
164
- browserType.launch = (async (
165
- ...args: Parameters<BrowserType<TBrowser>['launch']>
166
- ) => {
167
- const browser = await originalLaunch(...args);
168
- return augmentBrowser(browser);
169
- }) as BrowserType<TBrowser>['launch'];
170
-
171
- const originalConnect = browserType.connect?.bind(browserType);
172
- if (originalConnect) {
173
- browserType.connect = (async (
174
- ...args: Parameters<NonNullable<BrowserType<TBrowser>['connect']>>
175
- ) => {
176
- const browser = await originalConnect(...args);
177
- return augmentBrowser(browser);
178
- }) as NonNullable<BrowserType<TBrowser>['connect']>;
179
- }
180
-
181
- const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);
182
- if (originalConnectOverCDP) {
183
- browserType.connectOverCDP = (async (
184
- ...args: Parameters<NonNullable<BrowserType<TBrowser>['connectOverCDP']>>
185
- ) => {
186
- const browser = await originalConnectOverCDP(...args);
187
- return augmentBrowser(browser);
188
- }) as NonNullable<BrowserType<TBrowser>['connectOverCDP']>;
189
- }
190
-
191
- const originalLaunchPersistentContext =
192
- browserType.launchPersistentContext?.bind(browserType);
193
- if (originalLaunchPersistentContext) {
194
- browserType.launchPersistentContext = (async (
195
- ...args: Parameters<
196
- NonNullable<BrowserType<TBrowser>['launchPersistentContext']>
197
- >
198
- ) => {
199
- const context = await originalLaunchPersistentContext(...args);
200
- return augmentBrowserContext(context);
201
- }) as NonNullable<BrowserType<TBrowser>['launchPersistentContext']>;
202
- }
203
-
204
- defineHiddenProperty(browserType, BROWSER_TYPE_PATCHED, true);
205
-
206
- return browserType;
207
- }
@@ -1,19 +0,0 @@
1
- import type { Page } from 'playwright';
2
- import { requireApiKey } from '../../runtime';
3
-
4
- type AgentOptions = {
5
- page: Page;
6
- maxCycles?: number;
7
- };
8
-
9
- export function createAgentStub(): (
10
- prompt: string,
11
- options: AgentOptions,
12
- ) => Promise<{ success: boolean }> {
13
- return async (prompt: string, options: AgentOptions) => {
14
- requireApiKey();
15
- void prompt;
16
- void options;
17
- return { success: true };
18
- };
19
- }