@probolabs/playwright 1.0.8 → 1.0.11

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.
package/dist/index.d.ts CHANGED
@@ -240,6 +240,71 @@ declare class NavTracker {
240
240
  static resetInstance(): void;
241
241
  }
242
242
 
243
+ interface MailinatorMessage {
244
+ id: string;
245
+ subject: string;
246
+ from: string;
247
+ origfrom?: string;
248
+ to: string;
249
+ time: number;
250
+ seconds_ago: number;
251
+ domain?: string;
252
+ source?: string;
253
+ parts?: Array<{
254
+ headers: Record<string, string>;
255
+ body: string;
256
+ }>;
257
+ textBody?: string;
258
+ htmlBody?: string;
259
+ }
260
+ /**
261
+ * OTP utility class for working with Mailinator API
262
+ */
263
+ declare class OTP {
264
+ /**
265
+ * Fetches the last messages from Mailinator for a specific inbox
266
+ * @param inbox - The inbox name to check (without @domain)
267
+ * @param since - Unix timestamp to fetch messages since (optional)
268
+ * @returns Promise<MailinatorMessage[]> - Array of messages
269
+ */
270
+ static fetchLastMessages(inbox: string, since?: number): Promise<MailinatorMessage[]>;
271
+ /**
272
+ * Fetches a specific message by ID
273
+ * @param messageId - The message ID
274
+ * @returns Promise<MailinatorMessage> - The message details
275
+ */
276
+ static fetchMessage(messageId: string): Promise<MailinatorMessage>;
277
+ /**
278
+ * Extracts OTP codes from message content
279
+ * @param message - The message to extract OTP from
280
+ * @returns string | null - The OTP code or null if not found
281
+ */
282
+ static extractOTPFromMessage(message: MailinatorMessage): string | null;
283
+ /**
284
+ * Gets the text content from a message for debugging/display purposes
285
+ * @param message - The message to extract text from
286
+ * @returns string - The text content of the message
287
+ */
288
+ static getMessageTextContent(message: MailinatorMessage): string;
289
+ /**
290
+ * Fetches messages from all inboxes in the domain
291
+ * @param limit - Number of messages to fetch (default: 50)
292
+ * @param sort - Sort order: 'ascending' or 'descending' (default: 'descending')
293
+ * @param full - Whether to fetch full message content (default: false)
294
+ * @returns Promise<MailinatorMessage[]> - Array of messages from all inboxes
295
+ */
296
+ static fetchAllInboxMessages(limit?: number, sort?: 'ascending' | 'descending', full?: boolean): Promise<MailinatorMessage[]>;
297
+ /**
298
+ * Waits for an OTP to arrive in the inbox and extracts it
299
+ * @param inbox - The inbox name to monitor (optional - if not provided, searches all inboxes)
300
+ * @param timeout - Maximum time to wait in milliseconds (default: 30000)
301
+ * @param checkInterval - How often to check in milliseconds (default: 1000)
302
+ * @param checkRecentMessagesSinceMs - When > 0, check messages from the last X milliseconds and return the most recent OTP (default: 0)
303
+ * @returns Promise<string | null> - The extracted OTP code or null if timeout/no OTP found
304
+ */
305
+ static waitForOTP(inbox?: string, timeout?: number, checkInterval?: number, checkRecentMessagesSinceMs?: number): Promise<string | null>;
306
+ }
307
+
243
308
  /**
244
309
  * Configuration options for Probo client
245
310
  */
@@ -284,5 +349,5 @@ declare class Probo {
284
349
  askAIHelper(page: Page, question: string): Promise<ServerResponse>;
285
350
  }
286
351
 
287
- export { Highlighter, NavTracker, Probo, ProboPlaywright, findClosestVisibleElement };
288
- export type { ElementTagType, RunStepOptions };
352
+ export { Highlighter, NavTracker, OTP, Probo, ProboPlaywright, findClosestVisibleElement };
353
+ export type { ElementTagType, MailinatorMessage, RunStepOptions };
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ var PlaywrightAction;
44
44
  PlaywrightAction["EXECUTE_SCRIPT"] = "EXECUTE_SCRIPT";
45
45
  PlaywrightAction["UPLOAD_FILES"] = "UPLOAD_FILES";
46
46
  PlaywrightAction["WAIT_FOR"] = "WAIT_FOR";
47
+ PlaywrightAction["WAIT_FOR_OTP"] = "WAIT_FOR_OTP";
47
48
  })(PlaywrightAction || (PlaywrightAction = {}));
48
49
 
49
50
  // WebSocketsMessageType enum for WebSocket and event message types shared across the app
@@ -64,6 +65,7 @@ var WebSocketsMessageType;
64
65
  WebSocketsMessageType["INTERACTION_STEP_CREATED"] = "INTERACTION_STEP_CREATED";
65
66
  WebSocketsMessageType["INTERACTION_APPLY_AI_SUMMARY_COMPLETED"] = "INTERACTION_APPLY_AI_SUMMARY_COMPLETED";
66
67
  WebSocketsMessageType["INTERACTION_APPLY_AI_SUMMARY_ERROR"] = "INTERACTION_APPLY_AI_SUMMARY_ERROR";
68
+ WebSocketsMessageType["OTP_RETRIEVED"] = "OTP_RETRIEVED";
67
69
  })(WebSocketsMessageType || (WebSocketsMessageType = {}));
68
70
 
69
71
  /**
@@ -616,7 +618,7 @@ const decorateErrorWithCounts = (error, attemptNumber, options) => {
616
618
  return error;
617
619
  };
618
620
 
619
- async function pRetry$2(input, options) {
621
+ async function pRetry(input, options) {
620
622
  return new Promise((resolve, reject) => {
621
623
  options = {...options};
622
624
  options.onFailedAttempt ??= () => {};
@@ -743,7 +745,7 @@ class ApiClient {
743
745
  }
744
746
  async createStep(options) {
745
747
  apiLogger.debug('creating step ', options.stepPrompt);
746
- return pRetry$2(async () => {
748
+ return pRetry(async () => {
747
749
  const response = await fetch(`${this.apiUrl}/step-runners/`, {
748
750
  method: 'POST',
749
751
  headers: this.getHeaders(),
@@ -770,7 +772,7 @@ class ApiClient {
770
772
  async patchStep(stepId, fields) {
771
773
  // Use PATCH /steps/:id/ endpoint
772
774
  apiLogger.debug(`patching step #${stepId}`);
773
- return pRetry$2(async () => {
775
+ return pRetry(async () => {
774
776
  const response = await fetch(`${this.apiUrl}/steps/${stepId}/`, {
775
777
  method: 'PATCH',
776
778
  headers: this.getHeaders(),
@@ -788,7 +790,7 @@ class ApiClient {
788
790
  }
789
791
  async resolveNextInstruction(stepId, instruction, aiModel) {
790
792
  apiLogger.debug(`resolving next instruction: ${instruction}`);
791
- return pRetry$2(async () => {
793
+ return pRetry(async () => {
792
794
  apiLogger.debug(`API client: Resolving next instruction for step ${stepId}`);
793
795
  const cleanInstruction = cleanupInstructionElements(instruction);
794
796
  const response = await fetch(`${this.apiUrl}/step-runners/${stepId}/run/`, {
@@ -810,7 +812,7 @@ class ApiClient {
810
812
  });
811
813
  }
812
814
  async uploadScreenshot(screenshot_bytes) {
813
- return pRetry$2(async () => {
815
+ return pRetry(async () => {
814
816
  const response = await fetch(`${this.apiUrl}/upload-screenshots/`, {
815
817
  method: 'POST',
816
818
  headers: {
@@ -830,7 +832,7 @@ class ApiClient {
830
832
  }
831
833
  async findStepByPrompt(prompt, scenarioName, url = '') {
832
834
  apiLogger.debug(`Finding step by prompt: ${prompt} and scenario: ${scenarioName}`);
833
- return pRetry$2(async () => {
835
+ return pRetry(async () => {
834
836
  const response = await fetch(`${this.apiUrl}/step-runners/find-step-by-prompt/`, {
835
837
  method: 'POST',
836
838
  headers: this.getHeaders(),
@@ -864,7 +866,7 @@ class ApiClient {
864
866
  });
865
867
  }
866
868
  async resetStep(stepId) {
867
- return pRetry$2(async () => {
869
+ return pRetry(async () => {
868
870
  const response = await fetch(`${this.apiUrl}/steps/${stepId}/reset/`, {
869
871
  method: 'POST',
870
872
  headers: this.getHeaders(),
@@ -876,7 +878,7 @@ class ApiClient {
876
878
  async interactionToStep(scenarioName, interaction, position = -1) {
877
879
  // Use POST /interaction-to-step/ endpoint
878
880
  apiLogger.debug(`converting interaction #${interaction.interactionId} to step`);
879
- return pRetry$2(async () => {
881
+ return pRetry(async () => {
880
882
  var _a, _b;
881
883
  const response = await fetch(`${this.apiUrl}/interaction-to-step/`, {
882
884
  method: 'POST',
@@ -910,7 +912,7 @@ class ApiClient {
910
912
  async actionToPrompt(action2promptInput, aiModel) {
911
913
  // Use POST /action-to-prompt/ endpoint
912
914
  apiLogger.debug(`running action2prompt for step #${action2promptInput.step_id}`);
913
- return pRetry$2(async () => {
915
+ return pRetry(async () => {
914
916
  const response = await fetch(`${this.apiUrl}/steps/${action2promptInput.step_id}/action_to_prompt/`, {
915
917
  method: 'POST',
916
918
  headers: this.getHeaders(),
@@ -941,7 +943,7 @@ class ApiClient {
941
943
  async askAI(question, scenarioName, screenshot, aiModel) {
942
944
  apiLogger.debug(`Asking AI question: "${question}", scenarioName: ${scenarioName}`);
943
945
  apiLogger.debug(`headers: ${JSON.stringify(this.getHeaders())}`);
944
- return pRetry$2(async () => {
946
+ return pRetry(async () => {
945
947
  const response = await fetch(`${this.apiUrl}/api/ask-ai/`, {
946
948
  method: 'POST',
947
949
  headers: this.getHeaders(),
@@ -1277,103 +1279,286 @@ class Highlighter {
1277
1279
  }
1278
1280
  }
1279
1281
 
1280
- var pRetry$1 = {exports: {}};
1281
-
1282
- var hasRequiredPRetry;
1283
-
1284
- function requirePRetry () {
1285
- if (hasRequiredPRetry) return pRetry$1.exports;
1286
- hasRequiredPRetry = 1;
1287
- const retry = requireRetry();
1288
-
1289
- const networkErrorMsgs = [
1290
- 'Failed to fetch', // Chrome
1291
- 'NetworkError when attempting to fetch resource.', // Firefox
1292
- 'The Internet connection appears to be offline.', // Safari
1293
- 'Network request failed' // `cross-fetch`
1294
- ];
1295
-
1296
- class AbortError extends Error {
1297
- constructor(message) {
1298
- super();
1299
-
1300
- if (message instanceof Error) {
1301
- this.originalError = message;
1302
- ({message} = message);
1303
- } else {
1304
- this.originalError = new Error(message);
1305
- this.originalError.stack = this.stack;
1306
- }
1307
-
1308
- this.name = 'AbortError';
1309
- this.message = message;
1310
- }
1311
- }
1312
-
1313
- const decorateErrorWithCounts = (error, attemptNumber, options) => {
1314
- // Minus 1 from attemptNumber because the first attempt does not count as a retry
1315
- const retriesLeft = options.retries - (attemptNumber - 1);
1316
-
1317
- error.attemptNumber = attemptNumber;
1318
- error.retriesLeft = retriesLeft;
1319
- return error;
1320
- };
1321
-
1322
- const isNetworkError = errorMessage => networkErrorMsgs.includes(errorMessage);
1323
-
1324
- const pRetry = (input, options) => new Promise((resolve, reject) => {
1325
- options = {
1326
- onFailedAttempt: () => {},
1327
- retries: 10,
1328
- ...options
1329
- };
1330
-
1331
- const operation = retry.operation(options);
1332
-
1333
- operation.attempt(async attemptNumber => {
1334
- try {
1335
- resolve(await input(attemptNumber));
1336
- } catch (error) {
1337
- if (!(error instanceof Error)) {
1338
- reject(new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`));
1339
- return;
1340
- }
1341
-
1342
- if (error instanceof AbortError) {
1343
- operation.stop();
1344
- reject(error.originalError);
1345
- } else if (error instanceof TypeError && !isNetworkError(error.message)) {
1346
- operation.stop();
1347
- reject(error);
1348
- } else {
1349
- decorateErrorWithCounts(error, attemptNumber, options);
1350
-
1351
- try {
1352
- await options.onFailedAttempt(error);
1353
- } catch (error) {
1354
- reject(error);
1355
- return;
1356
- }
1357
-
1358
- if (!operation.retry(error)) {
1359
- reject(operation.mainError());
1360
- }
1361
- }
1362
- }
1363
- });
1364
- });
1365
-
1366
- pRetry$1.exports = pRetry;
1367
- // TODO: remove this in the next major version
1368
- pRetry$1.exports.default = pRetry;
1369
-
1370
- pRetry$1.exports.AbortError = AbortError;
1371
- return pRetry$1.exports;
1282
+ const MAILINATOR_API_KEY = '5bfef31518e84dfbb861b36f59259695';
1283
+ const MAILINATOR_DOMAIN = 'probolabs.testinator.com';
1284
+ /**
1285
+ * OTP utility class for working with Mailinator API
1286
+ */
1287
+ class OTP {
1288
+ /**
1289
+ * Fetches the last messages from Mailinator for a specific inbox
1290
+ * @param inbox - The inbox name to check (without @domain)
1291
+ * @param since - Unix timestamp to fetch messages since (optional)
1292
+ * @returns Promise<MailinatorMessage[]> - Array of messages
1293
+ */
1294
+ static async fetchLastMessages(inbox, since) {
1295
+ try {
1296
+ // Use the correct Mailinator API endpoint structure
1297
+ const url = `https://api.mailinator.com/v2/domains/${MAILINATOR_DOMAIN}/inboxes/${inbox}`;
1298
+ const response = await fetch(url, {
1299
+ method: 'GET',
1300
+ headers: {
1301
+ 'Authorization': `Bearer ${MAILINATOR_API_KEY}`,
1302
+ 'Content-Type': 'application/json'
1303
+ }
1304
+ });
1305
+ if (!response.ok) {
1306
+ throw new Error(`Mailinator API error: ${response.status} ${response.statusText}`);
1307
+ }
1308
+ const data = await response.json();
1309
+ // The API returns messages in 'msgs' property, not 'messages'
1310
+ const messages = data.msgs || data.messages || data || [];
1311
+ // Filter messages by 'since' timestamp if provided
1312
+ if (since) {
1313
+ return messages.filter((message) => message.time >= since);
1314
+ }
1315
+ return messages;
1316
+ }
1317
+ catch (error) {
1318
+ console.error('Error fetching messages from Mailinator:', error);
1319
+ throw error;
1320
+ }
1321
+ }
1322
+ /**
1323
+ * Fetches a specific message by ID
1324
+ * @param messageId - The message ID
1325
+ * @returns Promise<MailinatorMessage> - The message details
1326
+ */
1327
+ static async fetchMessage(messageId) {
1328
+ try {
1329
+ const url = `https://api.mailinator.com/v2/domains/${MAILINATOR_DOMAIN}/messages/${messageId}`;
1330
+ const response = await fetch(url, {
1331
+ method: 'GET',
1332
+ headers: {
1333
+ 'Authorization': `Bearer ${MAILINATOR_API_KEY}`,
1334
+ 'Content-Type': 'application/json'
1335
+ }
1336
+ });
1337
+ if (!response.ok) {
1338
+ throw new Error(`Mailinator API error: ${response.status} ${response.statusText}`);
1339
+ }
1340
+ return await response.json();
1341
+ }
1342
+ catch (error) {
1343
+ console.error('Error fetching message from Mailinator:', error);
1344
+ throw error;
1345
+ }
1346
+ }
1347
+ /**
1348
+ * Extracts OTP codes from message content
1349
+ * @param message - The message to extract OTP from
1350
+ * @returns string | null - The OTP code or null if not found
1351
+ */
1352
+ static extractOTPFromMessage(message) {
1353
+ // Common OTP patterns - ordered by specificity (most specific first)
1354
+ const otpPatterns = [
1355
+ // Very specific patterns for common OTP formats
1356
+ /(?:verification\s+code|verification\s+number|6-digit\s+verification\s+code)[\s\S]*?(\d{6})/i,
1357
+ /(?:use\s+the\s+6-digit\s+verification\s+code\s+below)[\s\S]*?(\d{6})/i,
1358
+ /(?:code\s+below\s+to\s+complete)[\s\S]*?(\d{6})/i,
1359
+ /(?:verification\s+code)[\s\S]*?(\d{6})/i,
1360
+ // Patterns with context around the code
1361
+ /(?:OTP|code|verification code|verification):\s*(\d{4,8})/i,
1362
+ /(?:enter|use|input)\s+(?:the\s+)?(?:code|OTP|verification code):\s*(\d{4,8})/i,
1363
+ /(?:your\s+)?(?:verification\s+)?(?:code\s+is|OTP\s+is):\s*(\d{4,8})/i,
1364
+ /(?:code|OTP|pin):\s*(\d{4,8})/i,
1365
+ /(?:code|OTP)\s+is\s+(\d{4,8})/i,
1366
+ // Look for codes in specific contexts (avoiding URLs and tracking IDs)
1367
+ /(?:complete\s+your\s+sign-in)[\s\S]*?(\d{6})/i,
1368
+ /(?:valid\s+for\s+the\s+next)[\s\S]*?(\d{6})/i,
1369
+ // Fallback: 6-digit numbers (most common OTP length)
1370
+ /(?:^|\s)(\d{6})(?:\s|$)/i,
1371
+ // Last resort: 4-8 digit numbers (but avoid very long ones that are likely tracking IDs)
1372
+ /(?:^|\s)(\d{4,8})(?:\s|$)/i
1373
+ ];
1374
+ // Helper function to check if a number is likely an OTP (not a tracking ID, phone number, etc.)
1375
+ const isValidOTP = (number) => {
1376
+ // Common OTP lengths
1377
+ if (number.length < 4 || number.length > 8)
1378
+ return false;
1379
+ // Only filter out very obvious non-OTPs
1380
+ // Avoid numbers that are all the same digit (unlikely to be OTPs)
1381
+ if (/^(\d)\1+$/.test(number))
1382
+ return false;
1383
+ // Avoid very long numbers that are clearly tracking IDs (but be more lenient)
1384
+ if (number.length >= 8 && parseInt(number) > 99999999)
1385
+ return false;
1386
+ return true;
1387
+ };
1388
+ // Helper function to extract OTP from text content
1389
+ const extractFromText = (text) => {
1390
+ if (!text)
1391
+ return null;
1392
+ for (const pattern of otpPatterns) {
1393
+ const match = text.match(pattern);
1394
+ if (match && match[1] && isValidOTP(match[1])) {
1395
+ return match[1];
1396
+ }
1397
+ }
1398
+ return null;
1399
+ };
1400
+ // Helper function to strip HTML tags
1401
+ const stripHtml = (html) => {
1402
+ return html.replace(/<[^>]*>/g, '').replace(/&[^;]+;/g, ' ');
1403
+ };
1404
+ // 1. Try to extract from text body first (preferred)
1405
+ if (message.textBody) {
1406
+ const otp = extractFromText(message.textBody);
1407
+ if (otp)
1408
+ return otp;
1409
+ }
1410
+ // 2. Try to extract from HTML body (strip HTML tags first)
1411
+ if (message.htmlBody) {
1412
+ const plainText = stripHtml(message.htmlBody);
1413
+ const otp = extractFromText(plainText);
1414
+ if (otp)
1415
+ return otp;
1416
+ }
1417
+ // 3. Try to extract from message parts
1418
+ if (message.parts && message.parts.length > 0) {
1419
+ for (const part of message.parts) {
1420
+ if (part.body) {
1421
+ const otp = extractFromText(part.body);
1422
+ if (otp)
1423
+ return otp;
1424
+ }
1425
+ }
1426
+ }
1427
+ // 4. Fallback: try to extract from subject
1428
+ const otp = extractFromText(message.subject);
1429
+ if (otp)
1430
+ return otp;
1431
+ return null;
1432
+ }
1433
+ /**
1434
+ * Gets the text content from a message for debugging/display purposes
1435
+ * @param message - The message to extract text from
1436
+ * @returns string - The text content of the message
1437
+ */
1438
+ static getMessageTextContent(message) {
1439
+ // Helper function to strip HTML tags
1440
+ const stripHtml = (html) => {
1441
+ return html.replace(/<[^>]*>/g, '').replace(/&[^;]+;/g, ' ');
1442
+ };
1443
+ // Priority order: textBody > htmlBody > parts > subject
1444
+ if (message.textBody) {
1445
+ return message.textBody;
1446
+ }
1447
+ if (message.htmlBody) {
1448
+ return stripHtml(message.htmlBody);
1449
+ }
1450
+ if (message.parts && message.parts.length > 0) {
1451
+ return message.parts.map(part => part.body || '').join('\n');
1452
+ }
1453
+ return message.subject || '';
1454
+ }
1455
+ /**
1456
+ * Fetches messages from all inboxes in the domain
1457
+ * @param limit - Number of messages to fetch (default: 50)
1458
+ * @param sort - Sort order: 'ascending' or 'descending' (default: 'descending')
1459
+ * @param full - Whether to fetch full message content (default: false)
1460
+ * @returns Promise<MailinatorMessage[]> - Array of messages from all inboxes
1461
+ */
1462
+ static async fetchAllInboxMessages(limit = 50, sort = 'descending', full = false) {
1463
+ try {
1464
+ const url = `https://api.mailinator.com/v2/domains/${MAILINATOR_DOMAIN}/inboxes/?limit=${limit}&sort=${sort}${full ? '&full=true' : ''}`;
1465
+ const response = await fetch(url, {
1466
+ method: 'GET',
1467
+ headers: {
1468
+ 'Authorization': `Bearer ${MAILINATOR_API_KEY}`,
1469
+ 'Content-Type': 'application/json'
1470
+ }
1471
+ });
1472
+ if (!response.ok) {
1473
+ throw new Error(`Mailinator API error: ${response.status} ${response.statusText}`);
1474
+ }
1475
+ const data = await response.json();
1476
+ return data.msgs || data.messages || data || [];
1477
+ }
1478
+ catch (error) {
1479
+ console.error('Error fetching messages from all inboxes:', error);
1480
+ throw error;
1481
+ }
1482
+ }
1483
+ /**
1484
+ * Waits for an OTP to arrive in the inbox and extracts it
1485
+ * @param inbox - The inbox name to monitor (optional - if not provided, searches all inboxes)
1486
+ * @param timeout - Maximum time to wait in milliseconds (default: 30000)
1487
+ * @param checkInterval - How often to check in milliseconds (default: 1000)
1488
+ * @param checkRecentMessagesSinceMs - When > 0, check messages from the last X milliseconds and return the most recent OTP (default: 0)
1489
+ * @returns Promise<string | null> - The extracted OTP code or null if timeout/no OTP found
1490
+ */
1491
+ static async waitForOTP(inbox, timeout = 30000, checkInterval = 1000, checkRecentMessagesSinceMs = 0) {
1492
+ const startTime = Date.now();
1493
+ // If checkRecentMessagesSinceMs > 0, check for recent messages first
1494
+ if (checkRecentMessagesSinceMs > 0) {
1495
+ console.log(`Checking for OTP in messages from the last ${checkRecentMessagesSinceMs}ms...`);
1496
+ const recentMessagesCutoff = Date.now() - checkRecentMessagesSinceMs;
1497
+ const recentMessages = inbox
1498
+ ? await OTP.fetchLastMessages(inbox)
1499
+ : await OTP.fetchAllInboxMessages();
1500
+ // Filter messages from the specified time window
1501
+ const messagesFromWindow = recentMessages.filter(msg => msg.time >= recentMessagesCutoff);
1502
+ // Sort by time (most recent first) and check for OTP
1503
+ const sortedMessages = messagesFromWindow.sort((a, b) => b.time - a.time);
1504
+ for (const message of sortedMessages) {
1505
+ let otp = OTP.extractOTPFromMessage(message);
1506
+ if (otp) {
1507
+ console.log(`Found OTP in recent message: ${otp}`);
1508
+ return otp;
1509
+ }
1510
+ // If no OTP found in summary, fetch full message details
1511
+ try {
1512
+ const fullMessage = await OTP.fetchMessage(message.id);
1513
+ otp = OTP.extractOTPFromMessage(fullMessage);
1514
+ if (otp) {
1515
+ console.log(`Found OTP in recent full message: ${otp}`);
1516
+ return otp;
1517
+ }
1518
+ }
1519
+ catch (error) {
1520
+ console.warn(`Error fetching full message ${message.id}:`, error);
1521
+ }
1522
+ }
1523
+ console.log(`No OTP found in recent messages, starting to monitor for new messages...`);
1524
+ }
1525
+ // Get initial messages for monitoring new ones (from specific inbox or all inboxes)
1526
+ const initialMessages = inbox
1527
+ ? await OTP.fetchLastMessages(inbox)
1528
+ : await OTP.fetchAllInboxMessages();
1529
+ const initialMessageIds = new Set(initialMessages.map(msg => msg.id));
1530
+ while (Date.now() - startTime < timeout) {
1531
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
1532
+ try {
1533
+ // Get current messages (from specific inbox or all inboxes)
1534
+ const currentMessages = inbox
1535
+ ? await OTP.fetchLastMessages(inbox)
1536
+ : await OTP.fetchAllInboxMessages();
1537
+ const newMessages = currentMessages.filter(msg => !initialMessageIds.has(msg.id));
1538
+ if (newMessages.length > 0) {
1539
+ // Get the first new message and try to extract OTP
1540
+ const newMessage = newMessages[0];
1541
+ // First try to extract OTP from the message summary (faster)
1542
+ let otp = OTP.extractOTPFromMessage(newMessage);
1543
+ if (otp) {
1544
+ return otp;
1545
+ }
1546
+ // If no OTP found in summary, fetch full message details
1547
+ const fullMessage = await OTP.fetchMessage(newMessage.id);
1548
+ otp = OTP.extractOTPFromMessage(fullMessage);
1549
+ if (otp) {
1550
+ return otp;
1551
+ }
1552
+ }
1553
+ }
1554
+ catch (error) {
1555
+ console.warn('Error checking for new messages:', error);
1556
+ }
1557
+ }
1558
+ return null; // Timeout reached or no OTP found
1559
+ }
1372
1560
  }
1373
1561
 
1374
- var pRetryExports = requirePRetry();
1375
- var pRetry = /*@__PURE__*/getDefaultExportFromCjs(pRetryExports);
1376
-
1377
1562
  class ProboPlaywright {
1378
1563
  constructor(config = DEFAULT_PLAYWRIGHT_TIMEOUT_CONFIG, page = null) {
1379
1564
  this.page = null;
@@ -1466,6 +1651,17 @@ class ProboPlaywright {
1466
1651
  case PlaywrightAction.SET_SLIDER:
1467
1652
  await this.setSliderValue(locator, argument);
1468
1653
  break;
1654
+ case PlaywrightAction.WAIT_FOR_OTP:
1655
+ // till we figure out how to get the inbox name we will wait for ANY OTP in all inboxes
1656
+ const otp = await OTP.waitForOTP();
1657
+ if (otp) {
1658
+ console.log(`✅ OTP found: ${otp}`);
1659
+ await locator.fill(otp);
1660
+ }
1661
+ else {
1662
+ console.log(`❌ OTP not found`);
1663
+ }
1664
+ break;
1469
1665
  case PlaywrightAction.ASSERT_CONTAINS_VALUE:
1470
1666
  const containerText = await this.getTextValue(locator);
1471
1667
  if (!matchRegex(containerText, argument)) {
@@ -2437,5 +2633,5 @@ class Probo {
2437
2633
  }
2438
2634
  }
2439
2635
 
2440
- export { Highlighter, NavTracker, PlaywrightAction, Probo, ProboLogLevel, ProboPlaywright, findClosestVisibleElement };
2636
+ export { Highlighter, NavTracker, OTP, PlaywrightAction, Probo, ProboLogLevel, ProboPlaywright, findClosestVisibleElement };
2441
2637
  //# sourceMappingURL=index.js.map