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.
- package/LICENSE +21 -0
- package/README.md +8 -8
- package/bin/midscene-playground +2 -2
- package/dist/es/agent.js +167 -44
- package/dist/es/agent.js.map +1 -1
- package/dist/es/bridge-mode-browser.js +64 -17
- package/dist/es/bridge-mode-browser.js.map +1 -1
- package/dist/es/bridge-mode.js +169 -46
- package/dist/es/bridge-mode.js.map +1 -1
- package/dist/es/chrome-extension.js +229 -59
- package/dist/es/chrome-extension.js.map +1 -1
- package/dist/es/index.js +183 -45
- package/dist/es/index.js.map +1 -1
- package/dist/es/midscene-playground.js +173 -44
- package/dist/es/midscene-playground.js.map +1 -1
- package/dist/es/midscene-server.js.map +1 -1
- package/dist/es/playground.js +173 -44
- package/dist/es/playground.js.map +1 -1
- package/dist/es/playwright-report.js.map +1 -1
- package/dist/es/playwright.js +183 -45
- package/dist/es/playwright.js.map +1 -1
- package/dist/es/puppeteer-agent-launcher.js +183 -45
- package/dist/es/puppeteer-agent-launcher.js.map +1 -1
- package/dist/es/puppeteer.js +183 -45
- package/dist/es/puppeteer.js.map +1 -1
- package/dist/es/ui-utils.js.map +1 -1
- package/dist/es/utils.js.map +1 -1
- package/dist/es/yaml.js +21 -3
- package/dist/es/yaml.js.map +1 -1
- package/dist/lib/agent.js +167 -44
- package/dist/lib/agent.js.map +1 -1
- package/dist/lib/bridge-mode-browser.js +64 -17
- package/dist/lib/bridge-mode-browser.js.map +1 -1
- package/dist/lib/bridge-mode.js +169 -46
- package/dist/lib/bridge-mode.js.map +1 -1
- package/dist/lib/chrome-extension.js +229 -59
- package/dist/lib/chrome-extension.js.map +1 -1
- package/dist/lib/index.js +181 -46
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/midscene-playground.js +173 -44
- package/dist/lib/midscene-playground.js.map +1 -1
- package/dist/lib/midscene-server.js.map +1 -1
- package/dist/lib/playground.js +173 -44
- package/dist/lib/playground.js.map +1 -1
- package/dist/lib/playwright-report.js.map +1 -1
- package/dist/lib/playwright.js +181 -46
- package/dist/lib/playwright.js.map +1 -1
- package/dist/lib/puppeteer-agent-launcher.js +181 -46
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
- package/dist/lib/puppeteer.js +181 -46
- package/dist/lib/puppeteer.js.map +1 -1
- package/dist/lib/ui-utils.js.map +1 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/yaml.js +21 -3
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent.d.ts +16 -6
- package/dist/types/bridge-mode-browser.d.ts +2 -2
- package/dist/types/bridge-mode.d.ts +2 -2
- package/dist/types/{browser-d447695b.d.ts → browser-a1877d18.d.ts} +1 -1
- package/dist/types/chrome-extension.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/midscene-server.d.ts +1 -1
- package/dist/types/{page-b8ada1f3.d.ts → page-663ece08.d.ts} +41 -30
- package/dist/types/playground.d.ts +2 -2
- package/dist/types/playwright.d.ts +1 -1
- package/dist/types/puppeteer-agent-launcher.d.ts +1 -1
- package/dist/types/puppeteer.d.ts +1 -1
- package/dist/types/utils.d.ts +1 -1
- package/dist/types/yaml.d.ts +1 -1
- package/iife-script/htmlElement.js +99 -37
- package/iife-script/htmlElementDebug.js +92 -9
- 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"]}
|
package/dist/lib/playwright.js
CHANGED
@@ -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
|
-
|
333
|
-
|
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
|
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
|
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
|
-
|
730
|
-
|
731
|
-
|
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(
|
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.
|
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
|
2021
|
-
const deepThink = opt.deepThink
|
2022
|
-
const cacheable = opt.cacheable
|
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
|
-
|
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}", ${
|
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
|
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,
|
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;
|