misoai-web 1.0.2 → 1.0.4

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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -8
  3. package/bin/midscene-playground +2 -2
  4. package/dist/es/agent.js +167 -44
  5. package/dist/es/agent.js.map +1 -1
  6. package/dist/es/bridge-mode-browser.js +64 -17
  7. package/dist/es/bridge-mode-browser.js.map +1 -1
  8. package/dist/es/bridge-mode.js +169 -46
  9. package/dist/es/bridge-mode.js.map +1 -1
  10. package/dist/es/chrome-extension.js +229 -59
  11. package/dist/es/chrome-extension.js.map +1 -1
  12. package/dist/es/index.js +183 -45
  13. package/dist/es/index.js.map +1 -1
  14. package/dist/es/midscene-playground.js +173 -44
  15. package/dist/es/midscene-playground.js.map +1 -1
  16. package/dist/es/midscene-server.js.map +1 -1
  17. package/dist/es/playground.js +173 -44
  18. package/dist/es/playground.js.map +1 -1
  19. package/dist/es/playwright-report.js.map +1 -1
  20. package/dist/es/playwright.js +183 -45
  21. package/dist/es/playwright.js.map +1 -1
  22. package/dist/es/puppeteer-agent-launcher.js +183 -45
  23. package/dist/es/puppeteer-agent-launcher.js.map +1 -1
  24. package/dist/es/puppeteer.js +183 -45
  25. package/dist/es/puppeteer.js.map +1 -1
  26. package/dist/es/ui-utils.js.map +1 -1
  27. package/dist/es/utils.js.map +1 -1
  28. package/dist/es/yaml.js +21 -3
  29. package/dist/es/yaml.js.map +1 -1
  30. package/dist/lib/agent.js +167 -44
  31. package/dist/lib/agent.js.map +1 -1
  32. package/dist/lib/bridge-mode-browser.js +64 -17
  33. package/dist/lib/bridge-mode-browser.js.map +1 -1
  34. package/dist/lib/bridge-mode.js +169 -46
  35. package/dist/lib/bridge-mode.js.map +1 -1
  36. package/dist/lib/chrome-extension.js +229 -59
  37. package/dist/lib/chrome-extension.js.map +1 -1
  38. package/dist/lib/index.js +181 -46
  39. package/dist/lib/index.js.map +1 -1
  40. package/dist/lib/midscene-playground.js +173 -44
  41. package/dist/lib/midscene-playground.js.map +1 -1
  42. package/dist/lib/midscene-server.js.map +1 -1
  43. package/dist/lib/playground.js +173 -44
  44. package/dist/lib/playground.js.map +1 -1
  45. package/dist/lib/playwright-report.js.map +1 -1
  46. package/dist/lib/playwright.js +181 -46
  47. package/dist/lib/playwright.js.map +1 -1
  48. package/dist/lib/puppeteer-agent-launcher.js +181 -46
  49. package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
  50. package/dist/lib/puppeteer.js +181 -46
  51. package/dist/lib/puppeteer.js.map +1 -1
  52. package/dist/lib/ui-utils.js.map +1 -1
  53. package/dist/lib/utils.js.map +1 -1
  54. package/dist/lib/yaml.js +21 -3
  55. package/dist/lib/yaml.js.map +1 -1
  56. package/dist/types/agent.d.ts +16 -6
  57. package/dist/types/bridge-mode-browser.d.ts +2 -2
  58. package/dist/types/bridge-mode.d.ts +2 -2
  59. package/dist/types/{browser-d447695b.d.ts → browser-a1877d18.d.ts} +1 -1
  60. package/dist/types/chrome-extension.d.ts +2 -2
  61. package/dist/types/index.d.ts +1 -1
  62. package/dist/types/midscene-server.d.ts +1 -1
  63. package/dist/types/{page-b8ada1f3.d.ts → page-663ece08.d.ts} +41 -30
  64. package/dist/types/playground.d.ts +2 -2
  65. package/dist/types/playwright.d.ts +1 -1
  66. package/dist/types/puppeteer-agent-launcher.d.ts +1 -1
  67. package/dist/types/puppeteer.d.ts +1 -1
  68. package/dist/types/utils.d.ts +1 -1
  69. package/dist/types/yaml.d.ts +1 -1
  70. package/iife-script/htmlElement.js +99 -37
  71. package/iife-script/htmlElementDebug.js +92 -9
  72. package/package.json +23 -24
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":["import_utils","dayjs"],"ignoreList":[],"sources":["../../src/playwright/reporter/index.ts","../../src/common/utils.ts"],"sourcesContent":["import {\n printReportMsg,\n replaceIllegalPathCharsAndSpace,\n reportFileName,\n} from '@/common/utils';\nimport type { ReportDumpWithAttributes } from 'misoai-core';\nimport { writeDumpReport } from 'misoai-core/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\nfunction logger(...message: any[]) {\n if (process.env.DEBUG === 'true') {\n console.log('Midscene e2e report:', ...message);\n }\n}\n\nconst testDataList: Array<ReportDumpWithAttributes> = [];\nlet mergedFilename: string;\nconst testTitleToFilename: Map<string, string> = new Map();\n\nfunction getStableFilename(testTitle: string): string {\n if (!testTitleToFilename.has(testTitle)) {\n // use reportFileName to generate the base filename\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = reportFileName(baseTag);\n testTitleToFilename.set(testTitle, generatedFilename);\n }\n return testTitleToFilename.get(testTitle)!;\n}\n\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\n if (mode === 'separate') {\n // in separate mode, find the data for the corresponding testID and generate a separate report\n const testData = testDataList.find(\n (data) => data.attributes?.playwright_test_id === testId,\n );\n\n if (testData) {\n // use the stable filename\n const stableFilename = getStableFilename(\n testData.attributes?.playwright_test_title,\n );\n\n const reportPath = writeDumpReport(stableFilename, [testData]);\n reportPath && printReportMsg(reportPath);\n }\n } else if (mode === 'merged') {\n // in merged mode, write all test data into one file\n if (!mergedFilename) {\n mergedFilename = reportFileName('playwright-merged');\n }\n\n const reportPath = writeDumpReport(mergedFilename, testDataList);\n reportPath && printReportMsg(reportPath);\n } else {\n throw new Error(\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\n );\n }\n}\n\nfunction getMode(reporterType: string) {\n if (!reporterType) {\n return 'merged';\n }\n\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n\n return reporterType;\n}\n\nclass MidsceneReporter implements Reporter {\n mode?: 'merged' | 'separate';\n\n async onBegin(config: FullConfig, suite: Suite) {\n const reporterType = config.reporter?.[1]?.[1]?.type;\n\n this.mode = getMode(reporterType);\n\n // const suites = suite.allTests();\n // logger(`Starting the run with ${suites.length} tests`);\n }\n\n onTestBegin(test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString: dumpAnnotation.description,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n testDataList.push(testData);\n\n updateReport(this.mode!, testId);\n\n test.annotations = test.annotations.filter(\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\n );\n }\n\n onEnd(result: FullResult) {\n updateReport(this.mode!);\n\n logger(`Finished the run: ${result.status}`);\n }\n}\n\nexport default MidsceneReporter;\n","import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n"]}
1
+ {"version":3,"mappings":"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","names":["import_utils","dayjs"],"ignoreList":[],"sources":["../../src/playwright/reporter/index.ts","../../src/common/utils.ts"],"sourcesContent":["import {\r\n printReportMsg,\r\n replaceIllegalPathCharsAndSpace,\r\n reportFileName,\r\n} from '@/common/utils';\r\nimport type { ReportDumpWithAttributes } from 'misoai-core';\r\nimport { writeDumpReport } from 'misoai-core/utils';\r\nimport type {\r\n FullConfig,\r\n FullResult,\r\n Reporter,\r\n Suite,\r\n TestCase,\r\n TestResult,\r\n} from '@playwright/test/reporter';\r\n\r\nfunction logger(...message: any[]) {\r\n if (process.env.DEBUG === 'true') {\r\n console.log('Midscene e2e report:', ...message);\r\n }\r\n}\r\n\r\nconst testDataList: Array<ReportDumpWithAttributes> = [];\r\nlet mergedFilename: string;\r\nconst testTitleToFilename: Map<string, string> = new Map();\r\n\r\nfunction getStableFilename(testTitle: string): string {\r\n if (!testTitleToFilename.has(testTitle)) {\r\n // use reportFileName to generate the base filename\r\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\r\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\r\n const generatedFilename = reportFileName(baseTag);\r\n testTitleToFilename.set(testTitle, generatedFilename);\r\n }\r\n return testTitleToFilename.get(testTitle)!;\r\n}\r\n\r\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\r\n if (mode === 'separate') {\r\n // in separate mode, find the data for the corresponding testID and generate a separate report\r\n const testData = testDataList.find(\r\n (data) => data.attributes?.playwright_test_id === testId,\r\n );\r\n\r\n if (testData) {\r\n // use the stable filename\r\n const stableFilename = getStableFilename(\r\n testData.attributes?.playwright_test_title,\r\n );\r\n\r\n const reportPath = writeDumpReport(stableFilename, [testData]);\r\n reportPath && printReportMsg(reportPath);\r\n }\r\n } else if (mode === 'merged') {\r\n // in merged mode, write all test data into one file\r\n if (!mergedFilename) {\r\n mergedFilename = reportFileName('playwright-merged');\r\n }\r\n\r\n const reportPath = writeDumpReport(mergedFilename, testDataList);\r\n reportPath && printReportMsg(reportPath);\r\n } else {\r\n throw new Error(\r\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\r\n );\r\n }\r\n}\r\n\r\nfunction getMode(reporterType: string) {\r\n if (!reporterType) {\r\n return 'merged';\r\n }\r\n\r\n if (reporterType !== 'merged' && reporterType !== 'separate') {\r\n throw new Error(\r\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\r\n );\r\n }\r\n\r\n return reporterType;\r\n}\r\n\r\nclass MidsceneReporter implements Reporter {\r\n mode?: 'merged' | 'separate';\r\n\r\n async onBegin(config: FullConfig, suite: Suite) {\r\n const reporterType = config.reporter?.[1]?.[1]?.type;\r\n\r\n this.mode = getMode(reporterType);\r\n\r\n // const suites = suite.allTests();\r\n // logger(`Starting the run with ${suites.length} tests`);\r\n }\r\n\r\n onTestBegin(test: TestCase, _result: TestResult) {\r\n // logger(`Starting test ${test.title}`);\r\n }\r\n\r\n onTestEnd(test: TestCase, result: TestResult) {\r\n const dumpAnnotation = test.annotations.find((annotation) => {\r\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\r\n });\r\n if (!dumpAnnotation?.description) return;\r\n const retry = result.retry ? `(retry #${result.retry})` : '';\r\n const testId = `${test.id}${retry}`;\r\n const testData: ReportDumpWithAttributes = {\r\n dumpString: dumpAnnotation.description,\r\n attributes: {\r\n playwright_test_id: testId,\r\n playwright_test_title: `${test.title}${retry}`,\r\n playwright_test_status: result.status,\r\n playwright_test_duration: result.duration,\r\n },\r\n };\r\n\r\n testDataList.push(testData);\r\n\r\n updateReport(this.mode!, testId);\r\n\r\n test.annotations = test.annotations.filter(\r\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\r\n );\r\n }\r\n\r\n onEnd(result: FullResult) {\r\n updateReport(this.mode!);\r\n\r\n logger(`Finished the run: ${result.status}`);\r\n }\r\n}\r\n\r\nexport default MidsceneReporter;\r\n","import type { StaticPage } from '@/playground';\r\nimport type {\r\n BaseElement,\r\n ElementTreeNode,\r\n PlanningLocateParam,\r\n PlaywrightParserOpt,\r\n UIContext,\r\n} from 'misoai-core';\r\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\r\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\r\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\r\nimport type { ElementInfo } from 'misoai-shared/extractor';\r\nimport {\r\n generateElementByPosition,\r\n getNodeFromCacheList,\r\n traverseTree,\r\n treeToList,\r\n} from 'misoai-shared/extractor';\r\nimport { resizeImgBase64 } from 'misoai-shared/img';\r\nimport type { DebugFunction } from 'misoai-shared/logger';\r\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\r\nimport dayjs from 'dayjs';\r\nimport type { Page as PlaywrightPage } from 'playwright';\r\nimport type { Page as PuppeteerPage } from 'puppeteer';\r\nimport { WebElementInfo } from '../web-element';\r\nimport type { WebPage } from './page';\r\n\r\nexport type WebUIContext = UIContext<WebElementInfo> & {\r\n url: string;\r\n};\r\n\r\nexport async function parseContextFromWebPage(\r\n page: WebPage,\r\n _opt?: PlaywrightParserOpt,\r\n): Promise<WebUIContext> {\r\n assert(page, 'page is required');\r\n if ((page as StaticPage)._forceUsePageContext) {\r\n return await (page as any)._forceUsePageContext();\r\n }\r\n const url = await page.url();\r\n uploadTestInfoToServer({ testUrl: url });\r\n\r\n let screenshotBase64: string;\r\n let tree: ElementTreeNode<ElementInfo>;\r\n\r\n await Promise.all([\r\n page.screenshotBase64().then((base64) => {\r\n screenshotBase64 = base64;\r\n }),\r\n page.getElementsNodeTree().then(async (treeRoot) => {\r\n tree = treeRoot;\r\n }),\r\n ]);\r\n\r\n const webTree = traverseTree(tree!, (elementInfo) => {\r\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\r\n return new WebElementInfo({\r\n rect,\r\n locator,\r\n id,\r\n content,\r\n attributes,\r\n indexId,\r\n });\r\n });\r\n\r\n assert(screenshotBase64!, 'screenshotBase64 is required');\r\n\r\n const elementsInfo = treeToList(webTree);\r\n const size = await page.size();\r\n\r\n if (size.dpr && size.dpr > 1) {\r\n // console.time('resizeImgBase64');\r\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\r\n width: size.width,\r\n height: size.height,\r\n });\r\n // console.timeEnd('resizeImgBase64');\r\n }\r\n\r\n return {\r\n content: elementsInfo!,\r\n tree: webTree,\r\n size,\r\n screenshotBase64: screenshotBase64!,\r\n url,\r\n };\r\n}\r\n\r\nexport function reportFileName(tag = 'web') {\r\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\r\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\r\n // ensure uniqueness at the same time\r\n const uniqueId = uuid().substring(0, 8);\r\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\r\n}\r\n\r\nexport function printReportMsg(filepath: string) {\r\n logMsg(`Midscene - report file updated: ${filepath}`);\r\n}\r\n\r\n/**\r\n * Get the current execution file name\r\n * @returns The name of the current execution file\r\n */\r\nexport function getCurrentExecutionFile(trace?: string): string | false {\r\n const error = new Error();\r\n const stackTrace = trace || error.stack;\r\n const pkgDir = process.cwd() || '';\r\n if (stackTrace) {\r\n const stackLines = stackTrace.split('\\n');\r\n for (const line of stackLines) {\r\n if (\r\n line.includes('.spec.') ||\r\n line.includes('.test.') ||\r\n line.includes('.ts') ||\r\n line.includes('.js')\r\n ) {\r\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\r\n if (match?.[1]) {\r\n const targetFileName = match[1]\r\n .replace(pkgDir, '')\r\n .trim()\r\n .replace('at ', '');\r\n return targetFileName;\r\n }\r\n }\r\n }\r\n }\r\n return false;\r\n}\r\n\r\nconst testFileIndex = new Map<string, number>();\r\n\r\nexport function generateCacheId(fileName?: string): string {\r\n let taskFile = fileName || getCurrentExecutionFile();\r\n if (!taskFile) {\r\n taskFile = uuid();\r\n console.warn(\r\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\r\n );\r\n }\r\n\r\n if (testFileIndex.has(taskFile)) {\r\n const currentIndex = testFileIndex.get(taskFile);\r\n if (currentIndex !== undefined) {\r\n testFileIndex.set(taskFile, currentIndex + 1);\r\n }\r\n } else {\r\n testFileIndex.set(taskFile, 1);\r\n }\r\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\r\n}\r\n\r\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\r\n 'NOT_IMPLEMENTED_AS_DESIGNED';\r\n\r\nexport function replaceIllegalPathCharsAndSpace(str: string) {\r\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\r\n}\r\n\r\nexport function forceClosePopup(\r\n page: PuppeteerPage | PlaywrightPage,\r\n debug: DebugFunction,\r\n) {\r\n page.on('popup', async (popup) => {\r\n if (!popup) {\r\n console.warn('got a popup event, but the popup is not ready yet, skip');\r\n return;\r\n }\r\n const url = await (popup as PuppeteerPage).url();\r\n console.log(`Popup opened: ${url}`);\r\n if (!(popup as PuppeteerPage).isClosed()) {\r\n try {\r\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\r\n } catch (error) {\r\n debug(`failed to close popup ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`popup is already closed, skip close ${url}`);\r\n }\r\n\r\n if (!page.isClosed()) {\r\n try {\r\n await page.goto(url);\r\n } catch (error) {\r\n debug(`failed to goto ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`page is already closed, skip goto ${url}`);\r\n }\r\n });\r\n}\r\n\r\nexport function matchElementFromPlan(\r\n planLocateParam: PlanningLocateParam,\r\n tree: ElementTreeNode<BaseElement>,\r\n) {\r\n if (!planLocateParam) {\r\n return undefined;\r\n }\r\n if (planLocateParam.id) {\r\n return getNodeFromCacheList(planLocateParam.id);\r\n }\r\n\r\n if (planLocateParam.bbox) {\r\n const centerPosition = {\r\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\r\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\r\n };\r\n let element = elementByPositionWithElementInfo(tree, centerPosition);\r\n\r\n if (!element) {\r\n element = generateElementByPosition(centerPosition) as BaseElement;\r\n }\r\n\r\n return element;\r\n }\r\n\r\n return undefined;\r\n}\r\n"]}
@@ -127,7 +127,9 @@ var ScriptPlayer = class {
127
127
  typeof prompt === "string",
128
128
  "prompt for aiAction must be a string"
129
129
  );
130
- await agent.aiAction(prompt);
130
+ await agent.aiAction(prompt, {
131
+ cacheable: actionTask.cacheable
132
+ });
131
133
  } else if ("aiAssert" in flowItem) {
132
134
  const assertTask = flowItem;
133
135
  const prompt = assertTask.aiAssert;
@@ -329,8 +331,24 @@ function interpolateEnvVars(content) {
329
331
  });
330
332
  }
331
333
  function parseYamlScript(content, filePath, ignoreCheckingTarget) {
332
- const interpolatedContent = interpolateEnvVars(content);
333
- const obj = import_js_yaml2.default.load(interpolatedContent);
334
+ let processedContent = content;
335
+ if (content.indexOf("android") !== -1 && content.match(/deviceId:\s*(\d+)/)) {
336
+ let matchedDeviceId;
337
+ processedContent = content.replace(
338
+ /deviceId:\s*(\d+)/g,
339
+ (match, deviceId) => {
340
+ matchedDeviceId = deviceId;
341
+ return `deviceId: '${deviceId}'`;
342
+ }
343
+ );
344
+ console.warn(
345
+ `please use string-style deviceId in yaml script, for example: deviceId: "${matchedDeviceId}"`
346
+ );
347
+ }
348
+ const interpolatedContent = interpolateEnvVars(processedContent);
349
+ const obj = import_js_yaml2.default.load(interpolatedContent, {
350
+ schema: import_js_yaml2.default.JSON_SCHEMA
351
+ });
334
352
  const pathTip = filePath ? `, failed to load ${filePath}` : "";
335
353
  const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
336
354
  const webConfig = obj.web || obj.target;
@@ -371,7 +389,6 @@ var import_misoai_core = require("misoai-core");
371
389
  var import_ai_model2 = require("misoai-core/ai-model");
372
390
  var import_utils5 = require("misoai-core/utils");
373
391
  var import_constants = require("misoai-shared/constants");
374
- var import_fs = require("misoai-shared/fs");
375
392
  var import_logger = require("misoai-shared/logger");
376
393
  var import_utils6 = require("misoai-shared/utils");
377
394
 
@@ -631,16 +648,18 @@ var PageTaskExecutor = class {
631
648
  );
632
649
  if (info?.id) {
633
650
  elementId = info.id;
651
+ } else {
652
+ debug(
653
+ "no element id found for position node, will not update cache",
654
+ element
655
+ );
634
656
  }
635
657
  }
636
658
  if (!elementId) {
637
659
  return void 0;
638
660
  }
639
661
  try {
640
- const elementInfosScriptContent = (0, import_fs.getElementInfosScriptContent)();
641
- const result = await this.page.evaluateJavaScript?.(
642
- `${elementInfosScriptContent}midscene_element_inspector.getXpathsById('${elementId}')`
643
- );
662
+ const result = await this.page.getXpathsById(elementId);
644
663
  return result;
645
664
  } catch (error) {
646
665
  debug("getXpathsById error: ", error);
@@ -679,7 +698,7 @@ var PageTaskExecutor = class {
679
698
  };
680
699
  return taskWithScreenshot;
681
700
  }
682
- async convertPlanToExecutable(plans) {
701
+ async convertPlanToExecutable(plans, opts) {
683
702
  const tasks = [];
684
703
  plans.forEach((plan2) => {
685
704
  if (plan2.type === "Locate") {
@@ -689,7 +708,10 @@ var PageTaskExecutor = class {
689
708
  const taskFind = {
690
709
  type: "Insight",
691
710
  subType: "Locate",
692
- param: plan2.locate || void 0,
711
+ param: plan2.locate ? {
712
+ ...plan2.locate,
713
+ cacheable: opts?.cacheable
714
+ } : void 0,
693
715
  thought: plan2.thought,
694
716
  locate: plan2.locate,
695
717
  executor: async (param, taskContext) => {
@@ -726,19 +748,21 @@ var PageTaskExecutor = class {
726
748
  let elementFromCache = null;
727
749
  try {
728
750
  if (xpaths?.length && this.taskCache?.isCacheResultUsed && param?.cacheable !== false) {
729
- const elementInfosScriptContent = (0, import_fs.getElementInfosScriptContent)();
730
- const element2 = await this.page.evaluateJavaScript?.(
731
- `${elementInfosScriptContent}midscene_element_inspector.getElementInfoByXpath('${xpaths[0]}')`
732
- );
733
- if (element2?.id) {
734
- elementFromCache = element2;
735
- debug("cache hit, prompt: %s", cachePrompt);
736
- cacheHitFlag = true;
737
- debug(
738
- "found a new new element with same xpath, xpath: %s, id: %s",
739
- xpaths[0],
740
- element2?.id
751
+ for (let i = 0; i < xpaths.length; i++) {
752
+ const element2 = await this.page.getElementInfoByXpath(
753
+ xpaths[i]
741
754
  );
755
+ if (element2?.id) {
756
+ elementFromCache = element2;
757
+ debug("cache hit, prompt: %s", cachePrompt);
758
+ cacheHitFlag = true;
759
+ debug(
760
+ "found a new new element with same xpath, xpath: %s, id: %s",
761
+ xpaths[i],
762
+ element2?.id
763
+ );
764
+ break;
765
+ }
742
766
  }
743
767
  }
744
768
  } catch (error) {
@@ -751,12 +775,14 @@ var PageTaskExecutor = class {
751
775
  context: pageContext
752
776
  })).element;
753
777
  const aiCost = Date.now() - startTime;
778
+ let currentXpaths;
754
779
  if (element && this.taskCache && !cacheHitFlag && param?.cacheable !== false) {
755
780
  const elementXpaths = await this.getElementXpath(
756
781
  pageContext,
757
782
  element
758
783
  );
759
- if (elementXpaths) {
784
+ if (elementXpaths?.length) {
785
+ currentXpaths = elementXpaths;
760
786
  this.taskCache.updateOrAppendCacheRecord(
761
787
  {
762
788
  type: "locate",
@@ -766,7 +792,11 @@ var PageTaskExecutor = class {
766
792
  locateCacheRecord
767
793
  );
768
794
  } else {
769
- debug("no xpaths found, will not update cache", cachePrompt);
795
+ debug(
796
+ "no xpaths found, will not update cache",
797
+ cachePrompt,
798
+ elementXpaths
799
+ );
770
800
  }
771
801
  }
772
802
  if (!element) {
@@ -778,7 +808,9 @@ var PageTaskExecutor = class {
778
808
  },
779
809
  pageContext,
780
810
  cache: {
781
- hit: cacheHitFlag
811
+ hit: cacheHitFlag,
812
+ originalXpaths: xpaths,
813
+ currentXpaths
782
814
  },
783
815
  aiCost
784
816
  };
@@ -1146,6 +1178,7 @@ var PageTaskExecutor = class {
1146
1178
  sleep: sleep3
1147
1179
  } = planResult;
1148
1180
  executorContext.task.log = {
1181
+ ...executorContext.task.log || {},
1149
1182
  rawResponse
1150
1183
  };
1151
1184
  executorContext.task.usage = usage;
@@ -1270,11 +1303,11 @@ var PageTaskExecutor = class {
1270
1303
  };
1271
1304
  return task;
1272
1305
  }
1273
- async runPlans(title, plans) {
1306
+ async runPlans(title, plans, opts) {
1274
1307
  const taskExecutor = new import_misoai_core.Executor(title, {
1275
1308
  onTaskStart: this.onTaskStartCallback
1276
1309
  });
1277
- const { tasks } = await this.convertPlanToExecutable(plans);
1310
+ const { tasks } = await this.convertPlanToExecutable(plans, opts);
1278
1311
  await taskExecutor.append(tasks);
1279
1312
  const result = await taskExecutor.flush();
1280
1313
  return {
@@ -1282,7 +1315,7 @@ var PageTaskExecutor = class {
1282
1315
  executor: taskExecutor
1283
1316
  };
1284
1317
  }
1285
- async action(userPrompt, actionContext) {
1318
+ async action(userPrompt, actionContext, opts) {
1286
1319
  const taskExecutor = new import_misoai_core.Executor(taskTitleStr("Action", userPrompt), {
1287
1320
  onTaskStart: this.onTaskStartCallback
1288
1321
  });
@@ -1307,7 +1340,7 @@ var PageTaskExecutor = class {
1307
1340
  yamlFlow.push(...planResult.yamlFlow || []);
1308
1341
  let executables;
1309
1342
  try {
1310
- executables = await this.convertPlanToExecutable(plans);
1343
+ executables = await this.convertPlanToExecutable(plans, opts);
1311
1344
  taskExecutor.append(executables.tasks);
1312
1345
  } catch (error) {
1313
1346
  return this.appendErrorPlan(
@@ -1345,7 +1378,7 @@ var PageTaskExecutor = class {
1345
1378
  executor: taskExecutor
1346
1379
  };
1347
1380
  }
1348
- async actionToGoal(userPrompt) {
1381
+ async actionToGoal(userPrompt, opts) {
1349
1382
  const taskExecutor = new import_misoai_core.Executor(taskTitleStr("Action", userPrompt), {
1350
1383
  onTaskStart: this.onTaskStartCallback
1351
1384
  });
@@ -1369,7 +1402,7 @@ var PageTaskExecutor = class {
1369
1402
  yamlFlow.push(...output.yamlFlow || []);
1370
1403
  let executables;
1371
1404
  try {
1372
- executables = await this.convertPlanToExecutable(plans);
1405
+ executables = await this.convertPlanToExecutable(plans, opts);
1373
1406
  taskExecutor.append(executables.tasks);
1374
1407
  } catch (error) {
1375
1408
  return this.appendErrorPlan(
@@ -1674,7 +1707,7 @@ var import_js_yaml3 = __toESM(require("js-yaml"));
1674
1707
  var import_semver = __toESM(require("semver"));
1675
1708
 
1676
1709
  // package.json
1677
- var version = "1.0.2";
1710
+ var version = "1.0.4";
1678
1711
 
1679
1712
  // src/common/task-cache.ts
1680
1713
  var debug3 = (0, import_logger3.getDebug)("cache");
@@ -2017,9 +2050,9 @@ var PageAgent = class {
2017
2050
  buildDetailedLocateParam(locatePrompt, opt) {
2018
2051
  (0, import_utils12.assert)(locatePrompt, "missing locate prompt");
2019
2052
  if (typeof opt === "object") {
2020
- const prompt = opt.prompt || locatePrompt;
2021
- const deepThink = opt.deepThink || false;
2022
- const cacheable = opt.cacheable || true;
2053
+ const prompt = opt.prompt ?? locatePrompt;
2054
+ const deepThink = opt.deepThink ?? false;
2055
+ const cacheable = opt.cacheable ?? true;
2023
2056
  return {
2024
2057
  prompt,
2025
2058
  deepThink,
@@ -2038,7 +2071,8 @@ var PageAgent = class {
2038
2071
  const plans = buildPlans("Tap", detailedLocateParam);
2039
2072
  const { executor, output } = await this.taskExecutor.runPlans(
2040
2073
  taskTitleStr("Tap", locateParamStr(detailedLocateParam)),
2041
- plans
2074
+ plans,
2075
+ { cacheable: opt?.cacheable }
2042
2076
  );
2043
2077
  const metadata = this.afterTaskRunning(executor);
2044
2078
  return {
@@ -2054,7 +2088,8 @@ var PageAgent = class {
2054
2088
  const plans = buildPlans("Hover", detailedLocateParam);
2055
2089
  const { executor, output } = await this.taskExecutor.runPlans(
2056
2090
  taskTitleStr("Hover", locateParamStr(detailedLocateParam)),
2057
- plans
2091
+ plans,
2092
+ { cacheable: opt?.cacheable }
2058
2093
  );
2059
2094
  const metadata = this.afterTaskRunning(executor);
2060
2095
  return {
@@ -2077,7 +2112,8 @@ var PageAgent = class {
2077
2112
  });
2078
2113
  const { executor, output } = await this.taskExecutor.runPlans(
2079
2114
  taskTitleStr("Input", locateParamStr(detailedLocateParam)),
2080
- plans
2115
+ plans,
2116
+ { cacheable: opt?.cacheable }
2081
2117
  );
2082
2118
  const metadata = this.afterTaskRunning(executor);
2083
2119
  return {
@@ -2093,7 +2129,8 @@ var PageAgent = class {
2093
2129
  });
2094
2130
  const { executor, output } = await this.taskExecutor.runPlans(
2095
2131
  taskTitleStr("KeyboardPress", locateParamStr(detailedLocateParam)),
2096
- plans
2132
+ plans,
2133
+ { cacheable: opt?.cacheable }
2097
2134
  );
2098
2135
  const metadata = this.afterTaskRunning(executor);
2099
2136
  return {
@@ -2107,7 +2144,8 @@ var PageAgent = class {
2107
2144
  const paramInTitle = locatePrompt ? `${locateParamStr(detailedLocateParam)} - ${scrollParamStr(scrollParam)}` : scrollParamStr(scrollParam);
2108
2145
  const { executor, output } = await this.taskExecutor.runPlans(
2109
2146
  taskTitleStr("Scroll", paramInTitle),
2110
- plans
2147
+ plans,
2148
+ { cacheable: opt?.cacheable }
2111
2149
  );
2112
2150
  const metadata = this.afterTaskRunning(executor);
2113
2151
  return {
@@ -2116,6 +2154,19 @@ var PageAgent = class {
2116
2154
  };
2117
2155
  }
2118
2156
  async aiAction(taskPrompt, opt) {
2157
+ try {
2158
+ const aiModel = await import("misoai-core/ai-model");
2159
+ const contextStore = aiModel.getContextStore();
2160
+ const processedPrompt = contextStore.replaceAllReferences(taskPrompt, "action");
2161
+ contextStore.addStep({
2162
+ type: "action",
2163
+ summary: `Action: ${processedPrompt}`,
2164
+ prompt: processedPrompt
2165
+ });
2166
+ taskPrompt = processedPrompt;
2167
+ } catch (error) {
2168
+ debug4("Context store not available:", error);
2169
+ }
2119
2170
  const cacheable = opt?.cacheable;
2120
2171
  const isVlmUiTars = (0, import_env2.vlLocateMode)() === "vlm-ui-tars";
2121
2172
  const matchedCache = isVlmUiTars || cacheable === false ? void 0 : this.taskCache?.matchPlanCache(taskPrompt);
@@ -2133,7 +2184,9 @@ var PageAgent = class {
2133
2184
  metadata: metadata2
2134
2185
  };
2135
2186
  }
2136
- const { output, executor } = await (isVlmUiTars ? this.taskExecutor.actionToGoal(taskPrompt) : this.taskExecutor.action(taskPrompt, this.opts.aiActionContext));
2187
+ const { output, executor } = await (isVlmUiTars ? this.taskExecutor.actionToGoal(taskPrompt, { cacheable }) : this.taskExecutor.action(taskPrompt, this.opts.aiActionContext, {
2188
+ cacheable
2189
+ }));
2137
2190
  if (this.taskCache && output?.yamlFlow && cacheable !== false) {
2138
2191
  const yamlContent = {
2139
2192
  tasks: [
@@ -2160,7 +2213,63 @@ var PageAgent = class {
2160
2213
  };
2161
2214
  }
2162
2215
  async aiQuery(demand) {
2163
- const { output, executor } = await this.taskExecutor.query(demand);
2216
+ let processedDemand = demand;
2217
+ let storageKey;
2218
+ try {
2219
+ const aiModel = await import("misoai-core/ai-model");
2220
+ const contextStore = aiModel.getContextStore();
2221
+ if (typeof demand === "string") {
2222
+ const storageInstruction = contextStore.parseStorageInstruction(demand);
2223
+ if (storageInstruction) {
2224
+ storageKey = storageInstruction.key;
2225
+ processedDemand = storageInstruction.cleanText;
2226
+ contextStore._pendingAliases = storageInstruction.aliases;
2227
+ } else {
2228
+ const storageMatch = demand.match(/store\s+(?:as\s+)?(\w+)/i);
2229
+ if (storageMatch) {
2230
+ storageKey = storageMatch[1];
2231
+ processedDemand = demand.replace(/,?\s*store\s+(?:as\s+)?\w+/i, "").trim();
2232
+ }
2233
+ }
2234
+ }
2235
+ } catch (error) {
2236
+ debug4("Context store not available:", error);
2237
+ }
2238
+ const { output, executor } = await this.taskExecutor.query(processedDemand);
2239
+ if (storageKey && output) {
2240
+ try {
2241
+ const aiModel = await import("misoai-core/ai-model");
2242
+ const contextStore = aiModel.getContextStore();
2243
+ const pendingAliases = contextStore._pendingAliases;
2244
+ if (pendingAliases) {
2245
+ contextStore.storeDataWithAliases(storageKey, output, pendingAliases, typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand));
2246
+ delete contextStore._pendingAliases;
2247
+ } else {
2248
+ contextStore.storeData(storageKey, output);
2249
+ }
2250
+ contextStore.addStep({
2251
+ type: "query",
2252
+ summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)} (stored as ${storageKey})`,
2253
+ data: output,
2254
+ prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
2255
+ });
2256
+ } catch (error) {
2257
+ debug4("Failed to store query result:", error);
2258
+ }
2259
+ } else {
2260
+ try {
2261
+ const aiModel = await import("misoai-core/ai-model");
2262
+ const contextStore = aiModel.getContextStore();
2263
+ contextStore.addStep({
2264
+ type: "query",
2265
+ summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)}`,
2266
+ data: output,
2267
+ prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
2268
+ });
2269
+ } catch (error) {
2270
+ debug4("Failed to add query step:", error);
2271
+ }
2272
+ }
2164
2273
  const metadata = this.afterTaskRunning(executor);
2165
2274
  return {
2166
2275
  result: output,
@@ -2255,7 +2364,8 @@ var PageAgent = class {
2255
2364
  const plans = buildPlans("Locate", detailedLocateParam);
2256
2365
  const { executor, output } = await this.taskExecutor.runPlans(
2257
2366
  taskTitleStr("Locate", locateParamStr(detailedLocateParam)),
2258
- plans
2367
+ plans,
2368
+ { cacheable: opt?.cacheable }
2259
2369
  );
2260
2370
  const metadata = this.afterTaskRunning(executor);
2261
2371
  const { element } = output;
@@ -2269,6 +2379,19 @@ var PageAgent = class {
2269
2379
  };
2270
2380
  }
2271
2381
  async aiAssert(assertion, msg, opt) {
2382
+ let processedAssertion = assertion;
2383
+ try {
2384
+ const aiModel = await import("misoai-core/ai-model");
2385
+ const contextStore = aiModel.getContextStore();
2386
+ processedAssertion = contextStore.replaceAllReferences(assertion, "assertion");
2387
+ contextStore.addStep({
2388
+ type: "assertion",
2389
+ summary: `Assertion: ${processedAssertion}`,
2390
+ prompt: processedAssertion
2391
+ });
2392
+ } catch (error) {
2393
+ debug4("Context store not available:", error);
2394
+ }
2272
2395
  let currentUrl = "";
2273
2396
  if (this.page.url) {
2274
2397
  try {
@@ -2276,7 +2399,7 @@ var PageAgent = class {
2276
2399
  } catch (e) {
2277
2400
  }
2278
2401
  }
2279
- const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${assertion}` : assertion;
2402
+ const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${processedAssertion}` : processedAssertion;
2280
2403
  const { output, executor } = await this.taskExecutor.assert(assertionWithContext);
2281
2404
  const metadata = this.afterTaskRunning(executor, true);
2282
2405
  if (output && opt?.keepRawResponse) {
@@ -2496,7 +2619,7 @@ ${errors}`);
2496
2619
  var import_utils15 = require("misoai-core/utils");
2497
2620
  var import_constants3 = require("misoai-shared/constants");
2498
2621
  var import_extractor2 = require("misoai-shared/extractor");
2499
- var import_fs2 = require("misoai-shared/fs");
2622
+ var import_fs = require("misoai-shared/fs");
2500
2623
  var import_logger5 = require("misoai-shared/logger");
2501
2624
  var import_utils16 = require("misoai-shared/utils");
2502
2625
  var debugPage = (0, import_logger5.getDebug)("web:page");
@@ -2551,9 +2674,21 @@ var Page = class {
2551
2674
  debugPage("getElementsInfo end");
2552
2675
  return (0, import_extractor2.treeToList)(tree);
2553
2676
  }
2677
+ async getXpathsById(id) {
2678
+ const elementInfosScriptContent = (0, import_fs.getElementInfosScriptContent)();
2679
+ return this.evaluateJavaScript(
2680
+ `${elementInfosScriptContent}midscene_element_inspector.getXpathsById('${id}')`
2681
+ );
2682
+ }
2683
+ async getElementInfoByXpath(xpath) {
2684
+ const elementInfosScriptContent = (0, import_fs.getElementInfosScriptContent)();
2685
+ return this.evaluateJavaScript(
2686
+ `${elementInfosScriptContent}midscene_element_inspector.getElementInfoByXpath('${xpath}')`
2687
+ );
2688
+ }
2554
2689
  async getElementsNodeTree() {
2555
2690
  await this.waitForNavigation();
2556
- const scripts = await (0, import_fs2.getExtraReturnLogic)(true);
2691
+ const scripts = await (0, import_fs.getExtraReturnLogic)(true);
2557
2692
  (0, import_utils16.assert)(scripts, "scripts should be set before writing report in browser");
2558
2693
  const captureElementSnapshot = await this.evaluate(scripts);
2559
2694
  return captureElementSnapshot;