misoai-web 1.0.5 → 1.5.6
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 -21
- package/README.md +9 -353
- package/bin/midscene-playground +2 -2
- package/dist/es/agent.js +9 -191
- package/dist/es/agent.js.map +1 -1
- package/dist/es/bridge-mode-browser.js +3 -3
- package/dist/es/bridge-mode-browser.js.map +1 -1
- package/dist/es/bridge-mode.js +11 -193
- package/dist/es/bridge-mode.js.map +1 -1
- package/dist/es/chrome-extension.js +10 -192
- package/dist/es/chrome-extension.js.map +1 -1
- package/dist/es/index.js +13 -195
- package/dist/es/index.js.map +1 -1
- package/dist/es/midscene-playground.js +9 -191
- package/dist/es/midscene-playground.js.map +1 -1
- package/dist/es/midscene-server.js.map +1 -1
- package/dist/es/playground.js +9 -191
- package/dist/es/playground.js.map +1 -1
- package/dist/es/playwright-report.js.map +1 -1
- package/dist/es/playwright.js +10 -192
- package/dist/es/playwright.js.map +1 -1
- package/dist/es/puppeteer-agent-launcher.js +13 -195
- package/dist/es/puppeteer-agent-launcher.js.map +1 -1
- package/dist/es/puppeteer.js +13 -195
- 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 +5 -3
- package/dist/es/yaml.js.map +1 -1
- package/dist/lib/agent.js +9 -191
- package/dist/lib/agent.js.map +1 -1
- package/dist/lib/bridge-mode-browser.js +3 -3
- package/dist/lib/bridge-mode-browser.js.map +1 -1
- package/dist/lib/bridge-mode.js +11 -193
- package/dist/lib/bridge-mode.js.map +1 -1
- package/dist/lib/chrome-extension.js +10 -192
- package/dist/lib/chrome-extension.js.map +1 -1
- package/dist/lib/index.js +13 -195
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/midscene-playground.js +9 -191
- package/dist/lib/midscene-playground.js.map +1 -1
- package/dist/lib/midscene-server.js.map +1 -1
- package/dist/lib/playground.js +9 -191
- package/dist/lib/playground.js.map +1 -1
- package/dist/lib/playwright-report.js.map +1 -1
- package/dist/lib/playwright.js +10 -192
- package/dist/lib/playwright.js.map +1 -1
- package/dist/lib/puppeteer-agent-launcher.js +13 -195
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
- package/dist/lib/puppeteer.js +13 -195
- 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 +5 -3
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent.d.ts +1 -27
- package/dist/types/bridge-mode-browser.d.ts +2 -2
- package/dist/types/bridge-mode.d.ts +2 -2
- package/dist/types/{browser-a1877d18.d.ts → browser-aec1055d.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-663ece08.d.ts → page-86ab0fe1.d.ts} +34 -34
- 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 +2 -2
- package/iife-script/htmlElementDebug.js +2 -2
- package/package.json +3 -3
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"mappings":"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","names":["import_utils","import_env","dotenv","express","uuid","cors","port"],"ignoreList":[],"sources":["../../src/playground/server.ts","../../src/common/utils.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\r\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\r\nimport type { Server } from 'node:http';\r\nimport { join } from 'node:path';\r\nimport { ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED } from '@/common/utils';\r\nimport { getTmpDir } from 'misoai-core/utils';\r\nimport { PLAYGROUND_SERVER_PORT } from 'misoai-shared/constants';\r\nimport { overrideAIConfig } from 'misoai-shared/env';\r\nimport { ifInBrowser } from 'misoai-shared/utils';\r\nimport cors from 'cors';\r\nimport dotenv from 'dotenv';\r\nimport express from 'express';\r\nimport type { PageAgent } from '../common/agent';\r\nimport type { AbstractPage } from '../page';\r\n\r\nconst defaultPort = PLAYGROUND_SERVER_PORT;\r\n// const staticPath = join(__dirname, '../../static');\r\n\r\nconst errorHandler = (err: any, req: any, res: any, next: any) => {\r\n console.error(err);\r\n res.status(500).json({\r\n error: err.message,\r\n });\r\n};\r\n\r\nconst setup = async () => {\r\n if (!ifInBrowser) {\r\n const { parsed } = dotenv.config();\r\n\r\n if (parsed) {\r\n overrideAIConfig(parsed);\r\n }\r\n }\r\n};\r\n\r\nexport default class PlaygroundServer {\r\n app: express.Application;\r\n tmpDir: string;\r\n server?: Server;\r\n port?: number | null;\r\n pageClass: new (\r\n ...args: any[]\r\n ) => AbstractPage;\r\n agentClass: new (\r\n ...args: any[]\r\n ) => PageAgent;\r\n staticPath?: string;\r\n taskProgressTips: Record<string, string>;\r\n\r\n constructor(\r\n pageClass: new (...args: any[]) => AbstractPage,\r\n agentClass: new (...args: any[]) => PageAgent,\r\n staticPath?: string,\r\n ) {\r\n this.app = express();\r\n this.tmpDir = getTmpDir()!;\r\n this.pageClass = pageClass;\r\n this.agentClass = agentClass;\r\n this.staticPath = staticPath;\r\n this.taskProgressTips = {};\r\n setup();\r\n }\r\n\r\n filePathForUuid(uuid: string) {\r\n return join(this.tmpDir, `${uuid}.json`);\r\n }\r\n\r\n saveContextFile(uuid: string, context: string) {\r\n const tmpFile = this.filePathForUuid(uuid);\r\n console.log(`save context file: ${tmpFile}`);\r\n writeFileSync(tmpFile, context);\r\n return tmpFile;\r\n }\r\n\r\n async launch(port?: number) {\r\n this.port = port || defaultPort;\r\n this.app.use(errorHandler);\r\n\r\n this.app.use(\r\n cors({\r\n origin: '*',\r\n credentials: true,\r\n }),\r\n );\r\n\r\n this.app.get('/status', cors(), async (req, res) => {\r\n // const modelName = g\r\n res.send({\r\n status: 'ok',\r\n });\r\n });\r\n\r\n // this.app.get('/playground/:uuid', async (req, res) => {\r\n // res.sendFile(join(staticPath, 'index.html'));\r\n // });\r\n\r\n this.app.get('/context/:uuid', async (req, res) => {\r\n const { uuid } = req.params;\r\n const contextFile = this.filePathForUuid(uuid);\r\n\r\n if (!existsSync(contextFile)) {\r\n return res.status(404).json({\r\n error: 'Context not found',\r\n });\r\n }\r\n\r\n const context = readFileSync(contextFile, 'utf8');\r\n res.json({\r\n context,\r\n });\r\n });\r\n\r\n this.app.get('/task-progress/:requestId', cors(), async (req, res) => {\r\n const { requestId } = req.params;\r\n res.json({\r\n tip: this.taskProgressTips[requestId] || '',\r\n });\r\n });\r\n\r\n // -------------------------\r\n // actions from report file\r\n this.app.post(\r\n '/playground-with-context',\r\n express.json({ limit: '50mb' }),\r\n async (req, res) => {\r\n const context = req.body.context;\r\n\r\n if (!context) {\r\n return res.status(400).json({\r\n error: 'context is required',\r\n });\r\n }\r\n\r\n const uuid = randomUUID();\r\n this.saveContextFile(uuid, context);\r\n return res.json({\r\n location: `/playground/${uuid}`,\r\n uuid,\r\n });\r\n },\r\n );\r\n\r\n this.app.post(\r\n '/execute',\r\n express.json({ limit: '30mb' }),\r\n async (req, res) => {\r\n const { context, type, prompt, requestId, deepThink } = req.body;\r\n\r\n if (!context) {\r\n return res.status(400).json({\r\n error: 'context is required',\r\n });\r\n }\r\n\r\n if (!type) {\r\n return res.status(400).json({\r\n error: 'type is required',\r\n });\r\n }\r\n\r\n if (!prompt) {\r\n return res.status(400).json({\r\n error: 'prompt is required',\r\n });\r\n }\r\n\r\n // build an agent with context\r\n const page = new this.pageClass(context);\r\n const agent = new this.agentClass(page);\r\n\r\n if (requestId) {\r\n this.taskProgressTips[requestId] = '';\r\n\r\n agent.onTaskStartTip = (tip: string) => {\r\n this.taskProgressTips[requestId] = tip;\r\n };\r\n }\r\n\r\n const response: {\r\n result: any;\r\n dump: string | null;\r\n error: string | null;\r\n reportHTML: string | null;\r\n requestId?: string;\r\n } = {\r\n result: null,\r\n dump: null,\r\n error: null,\r\n reportHTML: null,\r\n requestId,\r\n };\r\n\r\n const startTime = Date.now();\r\n try {\r\n if (type === 'aiQuery') {\r\n response.result = await agent.aiQuery(prompt);\r\n } else if (type === 'aiAction') {\r\n response.result = await agent.aiAction(prompt);\r\n } else if (type === 'aiAssert') {\r\n response.result = await agent.aiAssert(prompt, undefined, {\r\n keepRawResponse: true,\r\n });\r\n } else if (type === 'aiTap') {\r\n response.result = await agent.aiTap(prompt, {\r\n deepThink,\r\n });\r\n } else {\r\n response.error = `Unknown type: ${type}`;\r\n }\r\n } catch (error: any) {\r\n if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {\r\n response.error = error.message;\r\n }\r\n }\r\n\r\n try {\r\n response.dump = JSON.parse(agent.dumpDataString());\r\n response.reportHTML = agent.reportHTMLString() || null;\r\n\r\n agent.writeOutActionDumps();\r\n } catch (error: any) {\r\n console.error(\r\n `write out dump failed: requestId: ${requestId}, ${error.message}`,\r\n );\r\n }\r\n\r\n res.send(response);\r\n const timeCost = Date.now() - startTime;\r\n\r\n if (response.error) {\r\n console.error(\r\n `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`,\r\n );\r\n } else {\r\n console.log(\r\n `handle request done after ${timeCost}ms: requestId: ${requestId}`,\r\n );\r\n }\r\n },\r\n );\r\n\r\n this.app.post(\r\n '/config',\r\n express.json({ limit: '1mb' }),\r\n async (req, res) => {\r\n const { aiConfig } = req.body;\r\n\r\n if (!aiConfig || typeof aiConfig !== 'object') {\r\n return res.status(400).json({\r\n error: 'aiConfig is required and must be an object',\r\n });\r\n }\r\n\r\n try {\r\n overrideAIConfig(aiConfig);\r\n\r\n return res.json({\r\n status: 'ok',\r\n message: 'AI config updated successfully',\r\n });\r\n } catch (error: any) {\r\n console.error(`Failed to update AI config: ${error.message}`);\r\n return res.status(500).json({\r\n error: `Failed to update AI config: ${error.message}`,\r\n });\r\n }\r\n },\r\n );\r\n\r\n // Set up static file serving after all API routes are defined\r\n if (this.staticPath) {\r\n this.app.get('/', (req, res) => {\r\n // compatible with windows\r\n res.redirect('/index.html');\r\n });\r\n\r\n this.app.get('*', (req, res) => {\r\n const requestedPath = join(this.staticPath!, req.path);\r\n if (existsSync(requestedPath)) {\r\n res.sendFile(requestedPath);\r\n } else {\r\n res.sendFile(join(this.staticPath!, 'index.html'));\r\n }\r\n });\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const port = this.port;\r\n this.server = this.app.listen(port, () => {\r\n resolve(this);\r\n });\r\n });\r\n }\r\n\r\n close() {\r\n // close the server\r\n if (this.server) {\r\n return this.server.close();\r\n }\r\n }\r\n}\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"]}
|
1
|
+
{"version":3,"mappings":"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","names":["import_utils","import_env","dotenv","express","uuid","cors","port"],"ignoreList":[],"sources":["../../src/playground/server.ts","../../src/common/utils.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport type { Server } from 'node:http';\nimport { join } from 'node:path';\nimport { ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED } from '@/common/utils';\nimport { getTmpDir } from 'misoai-core/utils';\nimport { PLAYGROUND_SERVER_PORT } from 'misoai-shared/constants';\nimport { overrideAIConfig } from 'misoai-shared/env';\nimport { ifInBrowser } from 'misoai-shared/utils';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport express from 'express';\nimport type { PageAgent } from '../common/agent';\nimport type { AbstractPage } from '../page';\n\nconst defaultPort = PLAYGROUND_SERVER_PORT;\n// const staticPath = join(__dirname, '../../static');\n\nconst errorHandler = (err: any, req: any, res: any, next: any) => {\n console.error(err);\n res.status(500).json({\n error: err.message,\n });\n};\n\nconst setup = async () => {\n if (!ifInBrowser) {\n const { parsed } = dotenv.config();\n\n if (parsed) {\n overrideAIConfig(parsed);\n }\n }\n};\n\nexport default class PlaygroundServer {\n app: express.Application;\n tmpDir: string;\n server?: Server;\n port?: number | null;\n pageClass: new (\n ...args: any[]\n ) => AbstractPage;\n agentClass: new (\n ...args: any[]\n ) => PageAgent;\n staticPath?: string;\n taskProgressTips: Record<string, string>;\n\n constructor(\n pageClass: new (...args: any[]) => AbstractPage,\n agentClass: new (...args: any[]) => PageAgent,\n staticPath?: string,\n ) {\n this.app = express();\n this.tmpDir = getTmpDir()!;\n this.pageClass = pageClass;\n this.agentClass = agentClass;\n this.staticPath = staticPath;\n this.taskProgressTips = {};\n setup();\n }\n\n filePathForUuid(uuid: string) {\n return join(this.tmpDir, `${uuid}.json`);\n }\n\n saveContextFile(uuid: string, context: string) {\n const tmpFile = this.filePathForUuid(uuid);\n console.log(`save context file: ${tmpFile}`);\n writeFileSync(tmpFile, context);\n return tmpFile;\n }\n\n async launch(port?: number) {\n this.port = port || defaultPort;\n this.app.use(errorHandler);\n\n this.app.use(\n cors({\n origin: '*',\n credentials: true,\n }),\n );\n\n this.app.get('/status', cors(), async (req, res) => {\n // const modelName = g\n res.send({\n status: 'ok',\n });\n });\n\n // this.app.get('/playground/:uuid', async (req, res) => {\n // res.sendFile(join(staticPath, 'index.html'));\n // });\n\n this.app.get('/context/:uuid', async (req, res) => {\n const { uuid } = req.params;\n const contextFile = this.filePathForUuid(uuid);\n\n if (!existsSync(contextFile)) {\n return res.status(404).json({\n error: 'Context not found',\n });\n }\n\n const context = readFileSync(contextFile, 'utf8');\n res.json({\n context,\n });\n });\n\n this.app.get('/task-progress/:requestId', cors(), async (req, res) => {\n const { requestId } = req.params;\n res.json({\n tip: this.taskProgressTips[requestId] || '',\n });\n });\n\n // -------------------------\n // actions from report file\n this.app.post(\n '/playground-with-context',\n express.json({ limit: '50mb' }),\n async (req, res) => {\n const context = req.body.context;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n const uuid = randomUUID();\n this.saveContextFile(uuid, context);\n return res.json({\n location: `/playground/${uuid}`,\n uuid,\n });\n },\n );\n\n this.app.post(\n '/execute',\n express.json({ limit: '30mb' }),\n async (req, res) => {\n const { context, type, prompt, requestId, deepThink } = req.body;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\n });\n }\n\n if (!prompt) {\n return res.status(400).json({\n error: 'prompt is required',\n });\n }\n\n // build an agent with context\n const page = new this.pageClass(context);\n const agent = new this.agentClass(page);\n\n if (requestId) {\n this.taskProgressTips[requestId] = '';\n\n agent.onTaskStartTip = (tip: string) => {\n this.taskProgressTips[requestId] = tip;\n };\n }\n\n const response: {\n result: any;\n dump: string | null;\n error: string | null;\n reportHTML: string | null;\n requestId?: string;\n } = {\n result: null,\n dump: null,\n error: null,\n reportHTML: null,\n requestId,\n };\n\n const startTime = Date.now();\n try {\n if (type === 'aiQuery') {\n response.result = await agent.aiQuery(prompt);\n } else if (type === 'aiAction') {\n response.result = await agent.aiAction(prompt);\n } else if (type === 'aiAssert') {\n response.result = await agent.aiAssert(prompt, undefined, {\n keepRawResponse: true,\n });\n } else if (type === 'aiTap') {\n response.result = await agent.aiTap(prompt, {\n deepThink,\n });\n } else {\n response.error = `Unknown type: ${type}`;\n }\n } catch (error: any) {\n if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {\n response.error = error.message;\n }\n }\n\n try {\n response.dump = JSON.parse(agent.dumpDataString());\n response.reportHTML = agent.reportHTMLString() || null;\n\n agent.writeOutActionDumps();\n } catch (error: any) {\n console.error(\n `write out dump failed: requestId: ${requestId}, ${error.message}`,\n );\n }\n\n res.send(response);\n const timeCost = Date.now() - startTime;\n\n if (response.error) {\n console.error(\n `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`,\n );\n } else {\n console.log(\n `handle request done after ${timeCost}ms: requestId: ${requestId}`,\n );\n }\n },\n );\n\n this.app.post(\n '/config',\n express.json({ limit: '1mb' }),\n async (req, res) => {\n const { aiConfig } = req.body;\n\n if (!aiConfig || typeof aiConfig !== 'object') {\n return res.status(400).json({\n error: 'aiConfig is required and must be an object',\n });\n }\n\n try {\n overrideAIConfig(aiConfig);\n\n return res.json({\n status: 'ok',\n message: 'AI config updated successfully',\n });\n } catch (error: any) {\n console.error(`Failed to update AI config: ${error.message}`);\n return res.status(500).json({\n error: `Failed to update AI config: ${error.message}`,\n });\n }\n },\n );\n\n // Set up static file serving after all API routes are defined\n if (this.staticPath) {\n this.app.get('/', (req, res) => {\n // compatible with windows\n res.redirect('/index.html');\n });\n\n this.app.get('*', (req, res) => {\n const requestedPath = join(this.staticPath!, req.path);\n if (existsSync(requestedPath)) {\n res.sendFile(requestedPath);\n } else {\n res.sendFile(join(this.staticPath!, 'index.html'));\n }\n });\n }\n\n return new Promise((resolve, reject) => {\n const port = this.port;\n this.server = this.app.listen(port, () => {\n resolve(this);\n });\n });\n }\n\n close() {\n // close the server\n if (this.server) {\n return this.server.close();\n }\n }\n}\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"]}
|
package/dist/lib/playground.js
CHANGED
@@ -170,10 +170,11 @@ var ScriptPlayer = class {
|
|
170
170
|
this.unnamedResultIndex = 0;
|
171
171
|
this.pageAgent = null;
|
172
172
|
this.result = {};
|
173
|
+
const target = script.target || script.web || script.android;
|
173
174
|
if (import_utils3.ifInBrowser) {
|
174
175
|
this.output = void 0;
|
175
|
-
} else if (
|
176
|
-
this.output = (0, import_node_path.resolve)(process.cwd(),
|
176
|
+
} else if (target?.output) {
|
177
|
+
this.output = (0, import_node_path.resolve)(process.cwd(), target.output);
|
177
178
|
} else {
|
178
179
|
this.output = (0, import_node_path.join)((0, import_common.getMidsceneRunSubDir)("output"), `${process.pid}.json`);
|
179
180
|
}
|
@@ -247,12 +248,13 @@ var ScriptPlayer = class {
|
|
247
248
|
} else if ("aiAssert" in flowItem) {
|
248
249
|
const assertTask = flowItem;
|
249
250
|
const prompt = assertTask.aiAssert;
|
251
|
+
const msg = assertTask.errorMessage;
|
250
252
|
(0, import_utils3.assert)(prompt, "missing prompt for aiAssert");
|
251
253
|
(0, import_utils3.assert)(
|
252
254
|
typeof prompt === "string",
|
253
255
|
"prompt for aiAssert must be a string"
|
254
256
|
);
|
255
|
-
await agent.aiAssert(prompt);
|
257
|
+
await agent.aiAssert(prompt, msg);
|
256
258
|
} else if ("aiQuery" in flowItem) {
|
257
259
|
const queryTask = flowItem;
|
258
260
|
const prompt = queryTask.aiQuery;
|
@@ -1679,7 +1681,7 @@ var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
1679
1681
|
var import_semver = __toESM(require("semver"));
|
1680
1682
|
|
1681
1683
|
// package.json
|
1682
|
-
var version = "1.
|
1684
|
+
var version = "1.5.6";
|
1683
1685
|
|
1684
1686
|
// src/common/task-cache.ts
|
1685
1687
|
var debug3 = (0, import_logger3.getDebug)("cache");
|
@@ -1861,13 +1863,10 @@ var PageAgent = class {
|
|
1861
1863
|
generateReport: true,
|
1862
1864
|
autoPrintReportMsg: true,
|
1863
1865
|
groupName: "Midscene Report",
|
1864
|
-
groupDescription: ""
|
1865
|
-
enableCumulativeContext: true,
|
1866
|
-
autoClearContext: false
|
1866
|
+
groupDescription: ""
|
1867
1867
|
},
|
1868
1868
|
opts || {}
|
1869
1869
|
);
|
1870
|
-
this.initializeContextStore();
|
1871
1870
|
if (this.page.pageType === "puppeteer" || this.page.pageType === "playwright") {
|
1872
1871
|
this.page.waitForNavigationTimeout = this.opts.waitForNavigationTimeout || import_constants2.DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT;
|
1873
1872
|
this.page.waitForNetworkIdleTimeout = this.opts.waitForNetworkIdleTimeout || import_constants2.DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;
|
@@ -1894,69 +1893,6 @@ var PageAgent = class {
|
|
1894
1893
|
opts?.testId || this.page.pageType || "web"
|
1895
1894
|
);
|
1896
1895
|
}
|
1897
|
-
/**
|
1898
|
-
* Initialize context store for cumulative context functionality
|
1899
|
-
*/
|
1900
|
-
async initializeContextStore() {
|
1901
|
-
if (!this.opts.enableCumulativeContext) {
|
1902
|
-
debug4("Cumulative context disabled via options");
|
1903
|
-
return;
|
1904
|
-
}
|
1905
|
-
try {
|
1906
|
-
const aiModel = await import("misoai-core/ai-model");
|
1907
|
-
this.contextStore = aiModel.getContextStore();
|
1908
|
-
debug4("Context store initialized successfully", {
|
1909
|
-
autoClearContext: this.opts.autoClearContext,
|
1910
|
-
testId: this.opts.testId
|
1911
|
-
});
|
1912
|
-
if (this.opts.autoClearContext) {
|
1913
|
-
this.contextStore.clear();
|
1914
|
-
debug4("Context store cleared due to autoClearContext option");
|
1915
|
-
} else {
|
1916
|
-
const existingData = this.contextStore.getAllData();
|
1917
|
-
const existingSteps = this.contextStore.getRecentSteps(100).length;
|
1918
|
-
debug4("Context store preserving existing data", {
|
1919
|
-
existingDataKeys: Object.keys(existingData),
|
1920
|
-
existingStepsCount: existingSteps
|
1921
|
-
});
|
1922
|
-
}
|
1923
|
-
} catch (error) {
|
1924
|
-
debug4("Failed to initialize context store:", error);
|
1925
|
-
console.warn("⚠️ Could not initialize context store:", error);
|
1926
|
-
}
|
1927
|
-
}
|
1928
|
-
/**
|
1929
|
-
* Get the context store instance
|
1930
|
-
*/
|
1931
|
-
getContextStore() {
|
1932
|
-
return this.contextStore;
|
1933
|
-
}
|
1934
|
-
/**
|
1935
|
-
* Clear the context store
|
1936
|
-
*/
|
1937
|
-
clearContext() {
|
1938
|
-
if (this.contextStore) {
|
1939
|
-
this.contextStore.clear();
|
1940
|
-
}
|
1941
|
-
}
|
1942
|
-
/**
|
1943
|
-
* Get all stored data from context store
|
1944
|
-
*/
|
1945
|
-
getStoredData() {
|
1946
|
-
if (this.contextStore) {
|
1947
|
-
return this.contextStore.getAllData();
|
1948
|
-
}
|
1949
|
-
return {};
|
1950
|
-
}
|
1951
|
-
/**
|
1952
|
-
* Get step summary from context store
|
1953
|
-
*/
|
1954
|
-
getStepSummary() {
|
1955
|
-
if (this.contextStore) {
|
1956
|
-
return this.contextStore.getStepSummary();
|
1957
|
-
}
|
1958
|
-
return "";
|
1959
|
-
}
|
1960
1896
|
async getUIContext(action) {
|
1961
1897
|
if (action && (action === "extract" || action === "assert" || action === "captcha")) {
|
1962
1898
|
return await parseContextFromWebPage(this.page, {
|
@@ -2192,31 +2128,6 @@ var PageAgent = class {
|
|
2192
2128
|
};
|
2193
2129
|
}
|
2194
2130
|
async aiAction(taskPrompt, opt) {
|
2195
|
-
if (this.opts.enableCumulativeContext && this.contextStore) {
|
2196
|
-
try {
|
2197
|
-
const originalPrompt = taskPrompt;
|
2198
|
-
const processedPrompt = this.contextStore.replaceAllReferences(taskPrompt, "action");
|
2199
|
-
if (originalPrompt !== processedPrompt) {
|
2200
|
-
debug4("Context replacement in aiAction:", {
|
2201
|
-
original: originalPrompt,
|
2202
|
-
processed: processedPrompt,
|
2203
|
-
storedData: this.contextStore.getAllData()
|
2204
|
-
});
|
2205
|
-
}
|
2206
|
-
this.contextStore.addStep({
|
2207
|
-
type: "action",
|
2208
|
-
summary: `Action: ${processedPrompt}`,
|
2209
|
-
prompt: processedPrompt
|
2210
|
-
});
|
2211
|
-
debug4("Added action step to context store:", {
|
2212
|
-
stepNumber: this.contextStore.getRecentSteps(1)[0]?.stepNumber,
|
2213
|
-
totalSteps: this.contextStore.getRecentSteps(100).length
|
2214
|
-
});
|
2215
|
-
taskPrompt = processedPrompt;
|
2216
|
-
} catch (error) {
|
2217
|
-
debug4("Context store operation failed:", error);
|
2218
|
-
}
|
2219
|
-
}
|
2220
2131
|
const cacheable = opt?.cacheable;
|
2221
2132
|
const isVlmUiTars = (0, import_env2.vlLocateMode)() === "vlm-ui-tars";
|
2222
2133
|
const matchedCache = isVlmUiTars || cacheable === false ? void 0 : this.taskCache?.matchPlanCache(taskPrompt);
|
@@ -2263,75 +2174,7 @@ var PageAgent = class {
|
|
2263
2174
|
};
|
2264
2175
|
}
|
2265
2176
|
async aiQuery(demand) {
|
2266
|
-
|
2267
|
-
let storageKey;
|
2268
|
-
try {
|
2269
|
-
const aiModel = await import("misoai-core/ai-model");
|
2270
|
-
const contextStore = aiModel.getContextStore();
|
2271
|
-
if (typeof demand === "string") {
|
2272
|
-
const storageInstruction = contextStore.parseStorageInstruction(demand);
|
2273
|
-
if (storageInstruction) {
|
2274
|
-
storageKey = storageInstruction.key;
|
2275
|
-
processedDemand = storageInstruction.cleanText;
|
2276
|
-
contextStore._pendingAliases = storageInstruction.aliases;
|
2277
|
-
} else {
|
2278
|
-
const storageMatch = demand.match(/store\s+(?:as\s+)?(\w+)/i);
|
2279
|
-
if (storageMatch) {
|
2280
|
-
storageKey = storageMatch[1];
|
2281
|
-
processedDemand = demand.replace(/,?\s*store\s+(?:as\s+)?\w+/i, "").trim();
|
2282
|
-
}
|
2283
|
-
}
|
2284
|
-
}
|
2285
|
-
} catch (error) {
|
2286
|
-
debug4("Context store not available:", error);
|
2287
|
-
}
|
2288
|
-
const { output, executor } = await this.taskExecutor.query(processedDemand);
|
2289
|
-
if (this.opts.enableCumulativeContext && this.contextStore) {
|
2290
|
-
if (storageKey && output) {
|
2291
|
-
try {
|
2292
|
-
const pendingAliases = this.contextStore._pendingAliases;
|
2293
|
-
if (pendingAliases) {
|
2294
|
-
this.contextStore.storeDataWithAliases(storageKey, output, pendingAliases, typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand));
|
2295
|
-
delete this.contextStore._pendingAliases;
|
2296
|
-
debug4("Stored query result with aliases:", {
|
2297
|
-
key: storageKey,
|
2298
|
-
value: output,
|
2299
|
-
aliases: pendingAliases
|
2300
|
-
});
|
2301
|
-
} else {
|
2302
|
-
this.contextStore.storeData(storageKey, output);
|
2303
|
-
debug4("Stored query result:", {
|
2304
|
-
key: storageKey,
|
2305
|
-
value: output
|
2306
|
-
});
|
2307
|
-
}
|
2308
|
-
this.contextStore.addStep({
|
2309
|
-
type: "query",
|
2310
|
-
summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)} (stored as ${storageKey})`,
|
2311
|
-
data: output,
|
2312
|
-
prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
|
2313
|
-
});
|
2314
|
-
debug4("Added query step to context store:", {
|
2315
|
-
storageKey,
|
2316
|
-
totalStoredItems: Object.keys(this.contextStore.getAllData()).length,
|
2317
|
-
totalSteps: this.contextStore.getRecentSteps(100).length
|
2318
|
-
});
|
2319
|
-
} catch (error) {
|
2320
|
-
debug4("Failed to store query result:", error);
|
2321
|
-
}
|
2322
|
-
} else {
|
2323
|
-
try {
|
2324
|
-
this.contextStore.addStep({
|
2325
|
-
type: "query",
|
2326
|
-
summary: `Query: ${typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)}`,
|
2327
|
-
data: output,
|
2328
|
-
prompt: typeof processedDemand === "string" ? processedDemand : JSON.stringify(processedDemand)
|
2329
|
-
});
|
2330
|
-
} catch (error) {
|
2331
|
-
debug4("Failed to add query step:", error);
|
2332
|
-
}
|
2333
|
-
}
|
2334
|
-
}
|
2177
|
+
const { output, executor } = await this.taskExecutor.query(demand);
|
2335
2178
|
const metadata = this.afterTaskRunning(executor);
|
2336
2179
|
return {
|
2337
2180
|
result: output,
|
@@ -2441,31 +2284,6 @@ var PageAgent = class {
|
|
2441
2284
|
};
|
2442
2285
|
}
|
2443
2286
|
async aiAssert(assertion, msg, opt) {
|
2444
|
-
let processedAssertion = assertion;
|
2445
|
-
if (this.opts.enableCumulativeContext && this.contextStore) {
|
2446
|
-
try {
|
2447
|
-
const originalAssertion = assertion;
|
2448
|
-
processedAssertion = this.contextStore.replaceAllReferences(assertion, "assertion");
|
2449
|
-
if (originalAssertion !== processedAssertion) {
|
2450
|
-
debug4("Context replacement in aiAssert:", {
|
2451
|
-
original: originalAssertion,
|
2452
|
-
processed: processedAssertion,
|
2453
|
-
context: "assertion",
|
2454
|
-
storedData: this.contextStore.getAllData()
|
2455
|
-
});
|
2456
|
-
}
|
2457
|
-
this.contextStore.addStep({
|
2458
|
-
type: "assertion",
|
2459
|
-
summary: `Assertion: ${processedAssertion}`,
|
2460
|
-
prompt: processedAssertion
|
2461
|
-
});
|
2462
|
-
debug4("Added assertion step to context store:", {
|
2463
|
-
totalSteps: this.contextStore.getRecentSteps(100).length
|
2464
|
-
});
|
2465
|
-
} catch (error) {
|
2466
|
-
debug4("Context store operation failed:", error);
|
2467
|
-
}
|
2468
|
-
}
|
2469
2287
|
let currentUrl = "";
|
2470
2288
|
if (this.page.url) {
|
2471
2289
|
try {
|
@@ -2473,7 +2291,7 @@ var PageAgent = class {
|
|
2473
2291
|
} catch (e) {
|
2474
2292
|
}
|
2475
2293
|
}
|
2476
|
-
const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${
|
2294
|
+
const assertionWithContext = currentUrl ? `For the page at URL "${currentUrl}", ${assertion}` : assertion;
|
2477
2295
|
const { output, executor } = await this.taskExecutor.assert(assertionWithContext);
|
2478
2296
|
const metadata = this.afterTaskRunning(executor, true);
|
2479
2297
|
if (output && opt?.keepRawResponse) {
|