@probolabs/playwright 0.4.3 → 0.4.5

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.
@@ -0,0 +1,24 @@
1
+ import { Page } from 'playwright';
2
+ export declare function waitForNavigationToSettle(page: Page, options?: {
3
+ initialWait?: number;
4
+ navigationTimeout?: number;
5
+ totalTimeout?: number;
6
+ }): Promise<boolean>;
7
+ export declare function stabilizePage(page: Page): Promise<void>;
8
+ export declare const PlaywrightAction: {
9
+ readonly VISIT_BASE_URL: "VISIT_BASE_URL";
10
+ readonly VISIT_URL: "VISIT_URL";
11
+ readonly CLICK: "CLICK";
12
+ readonly FILL_IN: "FILL_IN";
13
+ readonly SELECT_DROPDOWN: "SELECT_DROPDOWN";
14
+ readonly SELECT_MULTIPLE_DROPDOWN: "SELECT_MULTIPLE_DROPDOWN";
15
+ readonly CHECK_CHECKBOX: "CHECK_CHECKBOX";
16
+ readonly SELECT_RADIO: "SELECT_RADIO";
17
+ readonly TOGGLE_SWITCH: "TOGGLE_SWITCH";
18
+ readonly TYPE_KEYS: "TYPE_KEYS";
19
+ readonly VALIDATE_EXACT_VALUE: "VALIDATE_EXACT_VALUE";
20
+ readonly VALIDATE_CONTAINS_VALUE: "VALIDATE_CONTAINS_VALUE";
21
+ readonly VALIDATE_URL: "VALIDATE_URL";
22
+ };
23
+ export type PlaywrightActionType = typeof PlaywrightAction[keyof typeof PlaywrightAction];
24
+ export declare const executePlaywrightAction: (page: Page, action: PlaywrightActionType, value: string, element_css_selector: string) => Promise<boolean>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAS,MAAM,YAAY,CAAC;AAmMzC,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;CAcnB,CAAC;AAGX,MAAM,MAAM,oBAAoB,GAAG,OAAO,gBAAgB,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAE1F,eAAO,MAAM,uBAAuB,SAC5B,IAAI,UACF,oBAAoB,SACrB,MAAM,wBACS,MAAM,qBAiF7B,CAAA"}
@@ -0,0 +1,241 @@
1
+ import { proboLogger } from './utils';
2
+ // Utility functions for enhanced Playwright interactions
3
+ async function handleNavigation(page, url, navigationTimeout = 5000, totalTimeout = 10000) {
4
+ let navigationCount = 0;
5
+ let lastNavigationTime = null;
6
+ const startTime = Date.now();
7
+ // Declare the function outside try block so it's accessible in finally
8
+ const onFrameNavigated = (frame) => {
9
+ if (frame === page.mainFrame()) {
10
+ navigationCount++;
11
+ lastNavigationTime = Date.now();
12
+ proboLogger.debug(`Navigation ${navigationCount} detected at ${lastNavigationTime}`);
13
+ }
14
+ };
15
+ try {
16
+ // Setup navigation tracking
17
+ page.on('framenavigated', onFrameNavigated);
18
+ await page.goto(url, {
19
+ waitUntil: 'domcontentloaded',
20
+ timeout: totalTimeout
21
+ });
22
+ // Final wait only for DOM, not network
23
+ if (navigationCount > 0) {
24
+ proboLogger.debug(`Total navigations detected: ${navigationCount}`);
25
+ await page.waitForLoadState('domcontentloaded');
26
+ // Add scroll to bottom to trigger lazy loading
27
+ await scrollToBottomRight(page);
28
+ }
29
+ }
30
+ finally {
31
+ page.removeListener('framenavigated', onFrameNavigated);
32
+ proboLogger.debug('Navigation listener removed');
33
+ }
34
+ }
35
+ async function enhancedClick(page, selector, options = {}) {
36
+ // Track initial scroll position
37
+ let initScrollY = await page.evaluate(() => window.scrollY);
38
+ let initScrollX = await page.evaluate(() => window.scrollX);
39
+ // Perform the click
40
+ await page.click(selector, { noWaitAfter: false, ...options });
41
+ // Monitor for any scrolling that might occur due to the click
42
+ let scrollingDetected = false;
43
+ while (true) {
44
+ const currScrollX = await page.evaluate(() => window.scrollX);
45
+ const currScrollY = await page.evaluate(() => window.scrollY);
46
+ if (currScrollY !== initScrollY || currScrollX !== initScrollX) {
47
+ proboLogger.debug('Click detected scrolling, waiting 50ms for stabilization...');
48
+ scrollingDetected = true;
49
+ await page.waitForTimeout(50);
50
+ initScrollX = currScrollX;
51
+ initScrollY = currScrollY;
52
+ }
53
+ else {
54
+ break;
55
+ }
56
+ }
57
+ // If scrolling occurred, reset to top
58
+ if (scrollingDetected) {
59
+ await page.evaluate(() => window.scrollTo(0, 0));
60
+ await page.waitForTimeout(50); // Small delay between scrolls
61
+ }
62
+ }
63
+ async function selectDropdownOption(page, selector, value) {
64
+ // Get the element's tag name
65
+ const locator = page.locator(selector);
66
+ const tagName = await locator.evaluate(el => el.tagName.toLowerCase());
67
+ if (tagName === 'select') {
68
+ // Handle native select element
69
+ proboLogger.debug('selectDropdownOption: simple select tag');
70
+ await page.selectOption(selector, value);
71
+ }
72
+ else { // not a native select
73
+ try {
74
+ // Look for child <select> if exists
75
+ const listboxSelector = locator.locator('select');
76
+ const count = await listboxSelector.count();
77
+ if (count === 1) { // delegate to child <select>
78
+ proboLogger.debug('Identified child element with tag select');
79
+ await listboxSelector.selectOption(value);
80
+ }
81
+ else {
82
+ // Click the original locator to expose the real listbox and options
83
+ proboLogger.debug('Clicking container element, looking for child element with role listbox');
84
+ await locator.click();
85
+ const listbox = locator.getByRole('listbox');
86
+ proboLogger.debug(`Clicking option ${value}`);
87
+ // Handle both string and array values
88
+ const values = Array.isArray(value) ? value : [value];
89
+ for (const val of values) {
90
+ const optionSelector = listbox
91
+ .getByRole('option')
92
+ .getByText(new RegExp(`^${val.toLowerCase()}$`, 'i'));
93
+ const optionCount = await optionSelector.count();
94
+ if (optionCount !== 1) {
95
+ throw new Error(`Found ${optionCount} option selectors for value: ${val}`);
96
+ }
97
+ await optionSelector.click();
98
+ }
99
+ }
100
+ }
101
+ catch (error) {
102
+ throw new Error(`Failed to select option '${value}' from custom dropdown: ${error}`);
103
+ }
104
+ }
105
+ }
106
+ async function getElementValue(page, selector) {
107
+ // TODO: Implement getting all text contents
108
+ proboLogger.log("TODO: Implement getting all text contents");
109
+ return "TODO: Implement getting all text contents";
110
+ }
111
+ async function waitForMutationsToSettle(page) {
112
+ // TODO: Implement mutation observer
113
+ //proboLogger.log("TODO: Implement mutation observer");
114
+ await page.waitForTimeout(500);
115
+ }
116
+ async function scrollToBottomRight(page) {
117
+ proboLogger.debug("Scrolling to bottom of page...");
118
+ // Get initial page dimensions
119
+ let lastHeight = await page.evaluate(() => document.documentElement.scrollHeight);
120
+ let lastWidth = await page.evaluate(() => document.documentElement.scrollWidth);
121
+ while (true) {
122
+ let smoothingSteps = 0;
123
+ // Get current scroll positions and dimensions
124
+ const initScrollY = await page.evaluate(() => window.scrollY);
125
+ const initScrollX = await page.evaluate(() => window.scrollX);
126
+ const maxScrollY = await page.evaluate(() => document.documentElement.scrollHeight);
127
+ const maxScrollX = await page.evaluate(() => document.documentElement.scrollWidth);
128
+ const maxClientY = await page.evaluate(() => document.documentElement.clientHeight);
129
+ const maxClientX = await page.evaluate(() => document.documentElement.clientWidth);
130
+ // Scroll both horizontally and vertically in steps
131
+ let currScrollX = initScrollX;
132
+ while (currScrollX <= (maxScrollX - maxClientX)) {
133
+ let currScrollY = initScrollY;
134
+ while (currScrollY <= (maxScrollY - maxClientY)) {
135
+ currScrollY += maxClientY;
136
+ await page.evaluate(({ x, y }) => window.scrollTo(x, y), { x: currScrollX, y: currScrollY });
137
+ await page.waitForTimeout(50); // Small delay between scrolls
138
+ smoothingSteps++;
139
+ }
140
+ currScrollX += maxClientX;
141
+ }
142
+ proboLogger.debug(`Performed ${smoothingSteps} smoothing steps while scrolling`);
143
+ // Check if content was lazy loaded (dimensions changed)
144
+ const newHeight = await page.evaluate(() => document.documentElement.scrollHeight);
145
+ const newWidth = await page.evaluate(() => document.documentElement.scrollWidth);
146
+ if (newHeight === lastHeight && newWidth === lastWidth) {
147
+ break;
148
+ }
149
+ proboLogger.debug("Page dimensions updated, repeating smoothing steps with new dimensions");
150
+ lastHeight = newHeight;
151
+ lastWidth = newWidth;
152
+ }
153
+ // Reset scroll position to top left
154
+ await page.waitForTimeout(200);
155
+ await page.evaluate(() => window.scrollTo(0, 0));
156
+ await page.waitForTimeout(50);
157
+ }
158
+ export const PlaywrightAction = {
159
+ VISIT_BASE_URL: 'VISIT_BASE_URL',
160
+ VISIT_URL: 'VISIT_URL',
161
+ CLICK: 'CLICK',
162
+ FILL_IN: 'FILL_IN',
163
+ SELECT_DROPDOWN: 'SELECT_DROPDOWN',
164
+ SELECT_MULTIPLE_DROPDOWN: 'SELECT_MULTIPLE_DROPDOWN',
165
+ CHECK_CHECKBOX: 'CHECK_CHECKBOX',
166
+ SELECT_RADIO: 'SELECT_RADIO',
167
+ TOGGLE_SWITCH: 'TOGGLE_SWITCH',
168
+ PRESS_KEY: 'PRESS_KEY',
169
+ VALIDATE_EXACT_VALUE: 'VALIDATE_EXACT_VALUE',
170
+ VALIDATE_CONTAINS_VALUE: 'VALIDATE_CONTAINS_VALUE',
171
+ VALIDATE_URL: 'VALIDATE_URL'
172
+ };
173
+ export const executePlaywrightAction = async (page, action, value, element_css_selector) => {
174
+ proboLogger.info('Executing playwright action:', { action, value, element_css_selector });
175
+ try {
176
+ switch (action) {
177
+ case PlaywrightAction.VISIT_BASE_URL:
178
+ case PlaywrightAction.VISIT_URL:
179
+ await handleNavigation(page, value);
180
+ break;
181
+ case PlaywrightAction.CLICK:
182
+ await enhancedClick(page, element_css_selector);
183
+ break;
184
+ case PlaywrightAction.FILL_IN:
185
+ await enhancedClick(page, element_css_selector);
186
+ await page.fill(element_css_selector, value);
187
+ break;
188
+ case PlaywrightAction.SELECT_DROPDOWN:
189
+ await selectDropdownOption(page, element_css_selector, value);
190
+ break;
191
+ case PlaywrightAction.SELECT_MULTIPLE_DROPDOWN:
192
+ const options = value.split(',').map(opt => opt.trim());
193
+ await selectDropdownOption(page, element_css_selector, options);
194
+ break;
195
+ case PlaywrightAction.CHECK_CHECKBOX:
196
+ const checked = value.toLowerCase() === 'true';
197
+ await page.setChecked(element_css_selector, checked);
198
+ break;
199
+ case PlaywrightAction.SELECT_RADIO:
200
+ case PlaywrightAction.TOGGLE_SWITCH:
201
+ await enhancedClick(page, element_css_selector);
202
+ break;
203
+ case PlaywrightAction.PRESS_KEY:
204
+ await page.press(element_css_selector, value);
205
+ break;
206
+ case PlaywrightAction.VALIDATE_EXACT_VALUE:
207
+ const exactValue = await getElementValue(page, element_css_selector);
208
+ if (exactValue !== value) {
209
+ proboLogger.info(`Validation FAIL: expected ${value} but got ${exactValue}`);
210
+ return false;
211
+ }
212
+ proboLogger.info('Validation PASS');
213
+ return true;
214
+ case PlaywrightAction.VALIDATE_CONTAINS_VALUE:
215
+ const containsValue = await getElementValue(page, element_css_selector);
216
+ if (!containsValue.includes(value)) {
217
+ proboLogger.info(`Validation FAIL: expected ${value} to be contained in ${containsValue}`);
218
+ return false;
219
+ }
220
+ proboLogger.info('Validation PASS');
221
+ return true;
222
+ case PlaywrightAction.VALIDATE_URL:
223
+ const currentUrl = page.url();
224
+ if (currentUrl !== value) {
225
+ proboLogger.info(`Validation FAIL: expected url ${value} but got ${currentUrl}`);
226
+ return false;
227
+ }
228
+ proboLogger.info('Validation PASS');
229
+ return true;
230
+ default:
231
+ throw new Error(`Unknown action: ${action}`);
232
+ }
233
+ // Wait for mutations to settle after any action
234
+ await waitForMutationsToSettle(page);
235
+ return true;
236
+ }
237
+ catch (error) {
238
+ proboLogger.error(`Failed to execute action ${action}:`, error);
239
+ throw error;
240
+ }
241
+ };
@@ -0,0 +1,32 @@
1
+ export interface Instruction {
2
+ what_to_do: string;
3
+ args: any;
4
+ result: any;
5
+ }
6
+ export interface CreateStepOptions {
7
+ stepIdFromServer?: number | null;
8
+ scenarioName: string;
9
+ stepPrompt: string;
10
+ initial_screenshot_url: string;
11
+ initial_html_content: string;
12
+ use_cache: boolean;
13
+ }
14
+ export declare class ApiError extends Error {
15
+ status: number;
16
+ data?: any | undefined;
17
+ constructor(status: number, message: string, data?: any | undefined);
18
+ toString(): string;
19
+ }
20
+ export declare class ApiClient {
21
+ private apiUrl;
22
+ private token;
23
+ private maxRetries;
24
+ private initialBackoff;
25
+ constructor(apiUrl: string, token?: string, // Default to demo token
26
+ maxRetries?: number, initialBackoff?: number);
27
+ private handleResponse;
28
+ private getHeaders;
29
+ createStep(options: CreateStepOptions): Promise<string>;
30
+ resolveNextInstruction(stepId: string, instruction: Instruction | null): Promise<any>;
31
+ uploadScreenshot(screenshot_bytes: Buffer): Promise<string>;
32
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;IACV,MAAM,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,QAAS,SAAQ,KAAK;IAExB,MAAM,EAAE,MAAM;IAEd,IAAI,CAAC,EAAE,GAAG;gBAFV,MAAM,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACR,IAAI,CAAC,EAAE,GAAG,YAAA;IASnB,QAAQ;CAGT;AAED,qBAAa,SAAS;IAElB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,cAAc;gBAHd,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAmD,EAAG,wBAAwB;IACrF,UAAU,GAAE,MAAU,EACtB,cAAc,GAAE,MAAa;YAGzB,cAAc;IA+B5B,OAAO,CAAC,UAAU;YAWJ,SAAS;IAyBjB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBvD,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IAatE,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAclE"}
@@ -0,0 +1,125 @@
1
+ export class ApiError extends Error {
2
+ constructor(status, message, data) {
3
+ super(message);
4
+ this.status = status;
5
+ this.data = data;
6
+ this.name = 'ApiError';
7
+ // Remove stack trace for cleaner error messages
8
+ this.stack = undefined;
9
+ }
10
+ toString() {
11
+ return `${this.message} (Status: ${this.status})`;
12
+ }
13
+ }
14
+ export class ApiClient {
15
+ constructor(apiUrl, token = 'b31793f81a1f58b8a153c86a5fdf4df0e5179c51', // Default to demo token
16
+ maxRetries = 3, initialBackoff = 1000) {
17
+ this.apiUrl = apiUrl;
18
+ this.token = token;
19
+ this.maxRetries = maxRetries;
20
+ this.initialBackoff = initialBackoff;
21
+ }
22
+ async handleResponse(response) {
23
+ var _a;
24
+ try {
25
+ const data = await response.json();
26
+ if (!response.ok) {
27
+ switch (response.status) {
28
+ case 401:
29
+ throw new ApiError(401, 'Unauthorized - Invalid or missing authentication token');
30
+ case 403:
31
+ throw new ApiError(403, 'Forbidden - You do not have permission to perform this action');
32
+ case 400:
33
+ throw new ApiError(400, 'Bad Request', data);
34
+ case 404:
35
+ throw new ApiError(404, 'Not Found', data);
36
+ case 500:
37
+ throw new ApiError(500, 'Internal Server Error', data);
38
+ default:
39
+ throw new ApiError(response.status, `API Error: ${data.error || 'Unknown error'}`, data);
40
+ }
41
+ }
42
+ return data;
43
+ }
44
+ catch (error) {
45
+ // Only throw the original error if it's not a network error
46
+ if (!((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('fetch failed'))) {
47
+ throw error;
48
+ }
49
+ throw new ApiError(0, 'Network error: fetch failed');
50
+ }
51
+ }
52
+ getHeaders() {
53
+ const headers = {
54
+ 'Content-Type': 'application/json',
55
+ };
56
+ // Always include token in headers now that we have a default
57
+ headers['Authorization'] = `Token ${this.token}`;
58
+ return headers;
59
+ }
60
+ async withRetry(operation) {
61
+ var _a;
62
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
63
+ try {
64
+ return await operation();
65
+ }
66
+ catch (error) {
67
+ // Only retry on network errors
68
+ if (!((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('Network error: fetch failed'))) {
69
+ throw error;
70
+ }
71
+ // On last attempt, throw the error
72
+ if (attempt === this.maxRetries) {
73
+ throw error;
74
+ }
75
+ // Otherwise, wait and retry
76
+ console.log(`[probolib-LOG] Network error, retrying (${attempt}/${this.maxRetries}). Waiting ${this.initialBackoff * Math.pow(2, attempt - 1)}ms...`);
77
+ await new Promise(resolve => setTimeout(resolve, this.initialBackoff * Math.pow(2, attempt - 1)));
78
+ }
79
+ }
80
+ // TypeScript needs this, but it will never be reached
81
+ throw new Error('Unreachable');
82
+ }
83
+ async createStep(options) {
84
+ return this.withRetry(async () => {
85
+ const response = await fetch(`${this.apiUrl}/step-runners/`, {
86
+ method: 'POST',
87
+ headers: this.getHeaders(),
88
+ body: JSON.stringify({
89
+ step_id: options.stepIdFromServer,
90
+ scenario_name: options.scenarioName,
91
+ step_prompt: options.stepPrompt,
92
+ initial_screenshot: options.initial_screenshot_url,
93
+ initial_html_content: options.initial_html_content,
94
+ use_cache: options.use_cache,
95
+ }),
96
+ });
97
+ const data = await this.handleResponse(response);
98
+ return data.step.id;
99
+ });
100
+ }
101
+ async resolveNextInstruction(stepId, instruction) {
102
+ return this.withRetry(async () => {
103
+ const response = await fetch(`${this.apiUrl}/step-runners/${stepId}/run/`, {
104
+ method: 'POST',
105
+ headers: this.getHeaders(),
106
+ body: JSON.stringify({ executed_instruction: instruction }),
107
+ });
108
+ const data = await this.handleResponse(response);
109
+ return data.instruction;
110
+ });
111
+ }
112
+ async uploadScreenshot(screenshot_bytes) {
113
+ return this.withRetry(async () => {
114
+ const response = await fetch(`${this.apiUrl}/upload-screenshots/`, {
115
+ method: 'POST',
116
+ headers: {
117
+ 'Authorization': `Token ${this.token}`
118
+ },
119
+ body: screenshot_bytes,
120
+ });
121
+ const data = await this.handleResponse(response);
122
+ return data.screenshot_url;
123
+ });
124
+ }
125
+ }
@@ -0,0 +1,28 @@
1
+ import type { Page } from 'playwright';
2
+ import { ElementTag } from '@probolabs/highlighter/src/constants';
3
+ type ElementTagType = typeof ElementTag[keyof typeof ElementTag];
4
+ declare global {
5
+ var highlighterCode: string;
6
+ interface Window {
7
+ ProboLabs?: {
8
+ highlight: {
9
+ execute: (elementTypes: ElementTagType[]) => Promise<any>;
10
+ unexecute: () => Promise<any>;
11
+ };
12
+ highlightElements: (elements: Array<{
13
+ css_selector: string;
14
+ index: string;
15
+ }>) => void;
16
+ ElementTag: typeof ElementTag;
17
+ };
18
+ }
19
+ }
20
+ export declare class Highlighter {
21
+ private enableConsoleLogs;
22
+ constructor(enableConsoleLogs?: boolean);
23
+ private ensureHighlighterScript;
24
+ highlightElements(page: Page, elementTag: ElementTagType): Promise<any>;
25
+ unhighlightElements(page: Page): Promise<void>;
26
+ highlightElement(page: Page, element_css_selector: string, element_index: string): Promise<void>;
27
+ }
28
+ export { ElementTag, ElementTagType };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAIlE,KAAK,cAAc,GAAG,OAAO,UAAU,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAGjE,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,eAAe,EAAE,MAAM,CAAC;IAE5B,UAAU,MAAM;QACd,SAAS,CAAC,EAAE;YACV,SAAS,EAAE;gBACT,OAAO,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC1D,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;aAC/B,CAAC;YACF,iBAAiB,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;gBAAE,YAAY,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC,KAAK,IAAI,CAAC;YACtF,UAAU,EAAE,OAAO,UAAU,CAAC;SAC/B,CAAC;KACH;CACF;AAED,qBAAa,WAAW;IACV,OAAO,CAAC,iBAAiB;gBAAjB,iBAAiB,GAAE,OAAc;YAEvC,uBAAuB;IA6BxB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc;IAsBxD,mBAAmB,CAAC,IAAI,EAAE,IAAI;IAQ9B,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;CA0B9F;AAED,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { ElementTag } from '@probolabs/highlighter/src/constants';
2
+ import { proboLogger } from './utils';
3
+ export class Highlighter {
4
+ constructor(enableConsoleLogs = true) {
5
+ this.enableConsoleLogs = enableConsoleLogs;
6
+ }
7
+ async ensureHighlighterScript(page, maxRetries = 3) {
8
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
9
+ try {
10
+ const scriptExists = await page.evaluate(`typeof window.ProboLabs?.highlight?.execute === 'function'`);
11
+ if (!scriptExists) {
12
+ proboLogger.debug('Injecting highlighter script...');
13
+ await page.evaluate(highlighterCode);
14
+ // Verify the script was injected correctly
15
+ const verified = await page.evaluate(`
16
+ //console.log('ProboLabs global:', window.ProboLabs);
17
+ typeof window.ProboLabs?.highlight?.execute === 'function'
18
+ `);
19
+ proboLogger.debug('Script injection verified:', verified);
20
+ }
21
+ return; // Success - exit the function
22
+ }
23
+ catch (error) {
24
+ if (attempt === maxRetries - 1) {
25
+ throw error;
26
+ }
27
+ proboLogger.debug(`Script injection attempt ${attempt + 1} failed, retrying after delay...`);
28
+ await new Promise(resolve => setTimeout(resolve, 100));
29
+ }
30
+ }
31
+ }
32
+ async highlightElements(page, elementTag) {
33
+ proboLogger.debug('highlightElements called with:', elementTag);
34
+ await this.ensureHighlighterScript(page);
35
+ // Execute the highlight function and await its result
36
+ const result = await page.evaluate(async (tag) => {
37
+ var _a, _b;
38
+ //proboLogger.debug('Browser: Starting highlight execution with tag:', tag);
39
+ if (!((_b = (_a = window.ProboLabs) === null || _a === void 0 ? void 0 : _a.highlight) === null || _b === void 0 ? void 0 : _b.execute)) {
40
+ console.error('Browser: ProboLabs.highlight.execute is not available!');
41
+ return null;
42
+ }
43
+ const elements = await window.ProboLabs.highlight.execute([tag]);
44
+ //proboLogger.debug('Browser: Found elements:', elements);
45
+ return elements;
46
+ }, elementTag);
47
+ return result;
48
+ }
49
+ async unhighlightElements(page) {
50
+ proboLogger.debug('unhighlightElements called');
51
+ await this.ensureHighlighterScript(page);
52
+ await page.evaluate(() => {
53
+ var _a, _b;
54
+ (_b = (_a = window === null || window === void 0 ? void 0 : window.ProboLabs) === null || _a === void 0 ? void 0 : _a.highlight) === null || _b === void 0 ? void 0 : _b.unexecute();
55
+ });
56
+ }
57
+ async highlightElement(page, element_css_selector, element_index) {
58
+ await this.ensureHighlighterScript(page);
59
+ proboLogger.debug('Highlighting element with:', { element_css_selector, element_index });
60
+ await page.evaluate(({ css_selector, index }) => {
61
+ const proboLabs = window.ProboLabs;
62
+ if (!proboLabs) {
63
+ proboLogger.warn('ProboLabs not initialized');
64
+ return;
65
+ }
66
+ // Create ElementInfo object for the element
67
+ const elementInfo = {
68
+ css_selector: css_selector,
69
+ index: index
70
+ };
71
+ // Call highlightElements directly
72
+ proboLabs.highlightElements([elementInfo]);
73
+ }, {
74
+ css_selector: element_css_selector,
75
+ index: element_index
76
+ });
77
+ }
78
+ }
79
+ export { ElementTag };
package/dist/index.d.ts CHANGED
@@ -29,6 +29,22 @@ declare enum ProboLogLevel {
29
29
  ERROR = "ERROR"
30
30
  }
31
31
 
32
+ /**
33
+ * Available AI models for LLM operations
34
+ */
35
+ declare enum AIModel {
36
+ AZURE_GPT4 = "AZURE_GPT4",
37
+ AZURE_GPT4_MINI = "AZURE_GPT4_MINI",
38
+ GEMINI_1_5_FLASH = "GEMINI_1_5_FLASH",
39
+ GEMINI_2_5_FLASH = "GEMINI_2_5_FLASH",
40
+ GPT4 = "GPT4",
41
+ GPT4_MINI = "GPT4_MINI",
42
+ CLAUDE_3_5 = "CLAUDE_3_5",
43
+ GROK_2 = "GROK_2",
44
+ LLAMA_4_SCOUT = "LLAMA_4_SCOUT",
45
+ DEEPSEEK_V3 = "DEEPSEEK_V3",
46
+ DEFAULT_AI_MODEL = "DEFAULT_AI_MODEL"
47
+ }
32
48
  declare const DEMO_TOKEN = "b31793f81a1f58b8a153c86a5fdf4df0e5179c51";
33
49
 
34
50
  interface ProboConfig {
@@ -37,17 +53,20 @@ interface ProboConfig {
37
53
  apiUrl: string;
38
54
  enableConsoleLogs?: boolean;
39
55
  debugLevel?: ProboLogLevel;
56
+ aiModel?: AIModel;
40
57
  }
41
58
  interface RunStepOptions {
42
59
  useCache: boolean;
43
60
  stepIdFromServer: number | null | undefined;
61
+ aiModel?: AIModel;
44
62
  }
45
63
  declare class Probo {
46
64
  private highlighter;
47
65
  private apiClient;
48
66
  private readonly enableConsoleLogs;
49
67
  private readonly scenarioName;
50
- constructor({ scenarioName, token, apiUrl, enableConsoleLogs, debugLevel }: ProboConfig);
68
+ private readonly aiModel;
69
+ constructor({ scenarioName, token, apiUrl, enableConsoleLogs, debugLevel, aiModel }: ProboConfig);
51
70
  runStep(page: Page, stepPrompt: string, options?: RunStepOptions): Promise<boolean>;
52
71
  private _handleCachedStep;
53
72
  private _handleStepCreation;
@@ -59,4 +78,4 @@ declare class Probo {
59
78
  private _handlePerformAction;
60
79
  }
61
80
 
62
- export { DEMO_TOKEN, Probo, ProboLogLevel, type RunStepOptions };
81
+ export { AIModel, DEMO_TOKEN, Probo, ProboLogLevel, type RunStepOptions };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAElE,OAAO,EAAe,cAAc,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAgB,aAAa,EAAE,MAAM,SAAS,CAAC;AAEtD,eAAO,MAAM,UAAU,6CAA6C,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,CAAC;AACzB,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,aAAa,CAAC;CAC5B;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAC1B,EACV,YAAY,EACZ,KAAK,EACL,MAAM,EACN,iBAAyB,EAC1B,EAAE,WAAW;IAQD,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAc,GAAI,OAAO,CAAC,OAAO,CAAC;YAgE7G,mBAAmB;IAgBjC,OAAO,CAAC,gBAAgB;IAUX,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc;IAIxD,mBAAmB,CAAC,IAAI,EAAE,IAAI;IAI9B,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAIhF,UAAU,CAAC,IAAI,EAAE,IAAI;YAQpB,oBAAoB;CAmCnC;AAGD,OAAO,EAAE,UAAU,EAAE,CAAC"}