@midscene/core 0.18.1-beta-20250611082446.0 → 0.18.1

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 (36) hide show
  1. package/dist/es/ai-model.d.ts +52 -4
  2. package/dist/es/ai-model.js +9 -1
  3. package/dist/es/{chunk-D4ZWVRRG.js → chunk-A7OTPIZJ.js} +19 -6
  4. package/dist/es/chunk-A7OTPIZJ.js.map +1 -0
  5. package/dist/es/{chunk-X2UALJ3B.js → chunk-EYIL4AHP.js} +353 -3
  6. package/dist/es/chunk-EYIL4AHP.js.map +1 -0
  7. package/dist/es/index.d.ts +4 -4
  8. package/dist/es/index.js +19 -26
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
  11. package/dist/es/{types-00c9086f.d.ts → types-dce56c26.d.ts} +6 -6
  12. package/dist/es/utils.d.ts +1 -1
  13. package/dist/es/utils.js +1 -1
  14. package/dist/lib/ai-model.d.ts +52 -4
  15. package/dist/lib/ai-model.js +10 -2
  16. package/dist/lib/{chunk-D4ZWVRRG.js → chunk-A7OTPIZJ.js} +18 -5
  17. package/dist/lib/chunk-A7OTPIZJ.js.map +1 -0
  18. package/dist/lib/{chunk-X2UALJ3B.js → chunk-EYIL4AHP.js} +371 -21
  19. package/dist/lib/chunk-EYIL4AHP.js.map +1 -0
  20. package/dist/lib/index.d.ts +4 -4
  21. package/dist/lib/index.js +37 -44
  22. package/dist/lib/index.js.map +1 -1
  23. package/dist/lib/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
  24. package/dist/{types/types-00c9086f.d.ts → lib/types-dce56c26.d.ts} +6 -6
  25. package/dist/lib/utils.d.ts +1 -1
  26. package/dist/lib/utils.js +2 -2
  27. package/dist/types/ai-model.d.ts +52 -4
  28. package/dist/types/index.d.ts +4 -4
  29. package/dist/types/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
  30. package/dist/{lib/types-00c9086f.d.ts → types/types-dce56c26.d.ts} +6 -6
  31. package/dist/types/utils.d.ts +1 -1
  32. package/package.json +4 -3
  33. package/dist/es/chunk-D4ZWVRRG.js.map +0 -1
  34. package/dist/es/chunk-X2UALJ3B.js.map +0 -1
  35. package/dist/lib/chunk-D4ZWVRRG.js.map +0 -1
  36. package/dist/lib/chunk-X2UALJ3B.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/utils.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport * as path from 'node:path';\nimport { dirname } from 'node:path';\nimport {\n defaultRunDirName,\n getMidsceneRunSubDir,\n} from '@midscene/shared/common';\nimport {\n MIDSCENE_DEBUG_MODE,\n MIDSCENE_OPENAI_INIT_CONFIG_JSON,\n getAIConfig,\n getAIConfigInJson,\n} from '@midscene/shared/env';\nimport { getRunningPkgInfo } from '@midscene/shared/fs';\nimport { assert, logMsg } from '@midscene/shared/utils';\nimport { escapeHtml, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport xss from 'xss';\nimport type { Rect, ReportDumpWithAttributes } from './types';\n\nlet logEnvReady = false;\n\nconst xssOptions = {\n escapeHtml,\n};\nexport const groupedActionDumpFileExt = 'web-dump.json';\n\nconst reportTpl = 'REPLACE_ME_WITH_REPORT_HTML';\n\nfunction getReportTpl() {\n return reportTpl;\n}\n\nexport function replaceStringWithFirstAppearance(\n str: string,\n target: string,\n replacement: string,\n) {\n const index = str.indexOf(target);\n return str.slice(0, index) + replacement + str.slice(index + target.length);\n}\n\nexport function reportHTMLContent(\n dumpData: string | ReportDumpWithAttributes[],\n reportPath?: string,\n): string {\n const tpl = getReportTpl();\n if (!tpl) {\n console.warn('reportTpl is not set, will not write report');\n return '';\n }\n\n const dumpPlaceholder = '{{dump}}';\n\n // verify the template contains the placeholder\n if (!tpl.includes(dumpPlaceholder)) {\n console.warn(\n 'Failed to get the Midscene report template due to the lack of the {{dump}} placeholder. If you are building Midscene.js by yourself, please refer to the contribution guide for more information: https://github.com/web-infra-dev/midscene/blob/main/CONTRIBUTING.md#FAQ',\n );\n return '';\n }\n\n // find the first placeholder position\n const placeholderIndex = tpl.indexOf(dumpPlaceholder);\n\n // split the template into two parts before and after the placeholder\n const firstPart = tpl.substring(0, placeholderIndex);\n const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);\n\n // if reportPath is set, it means we are in write to file mode\n const writeToFile = reportPath && !ifInBrowser;\n let resultContent = '';\n\n // helper function: decide to write to file or append to resultContent\n const appendOrWrite = (content: string): void => {\n if (writeToFile) {\n writeFileSync(reportPath!, `${content}\\n`, {\n flag: 'a',\n });\n } else {\n resultContent += `${content}\\n`;\n }\n };\n\n // if writeToFile is true, write the first part to file, otherwise set the first part to the initial value of resultContent\n if (writeToFile) {\n writeFileSync(reportPath!, firstPart, { flag: 'w' }); // use 'w' flag to overwrite the existing file\n } else {\n resultContent = firstPart;\n }\n\n // generate dump content\n // handle empty data or undefined\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n const dumpContent =\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>';\n appendOrWrite(dumpContent);\n }\n // handle string type dumpData\n else if (typeof dumpData === 'string') {\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n xss(dumpData, xssOptions) +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n // handle array type dumpData\n else {\n // for array, handle each item\n for (let i = 0; i < dumpData.length; i++) {\n const { dumpString, attributes } = dumpData[i];\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n xss(dumpString, xssOptions) +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n }\n\n // add the second part\n if (writeToFile) {\n writeFileSync(reportPath!, secondPart, { flag: 'a' });\n return reportPath!;\n }\n\n resultContent += secondPart;\n return resultContent;\n}\n\nexport function writeDumpReport(\n fileName: string,\n dumpData: string | ReportDumpWithAttributes[],\n): string | null {\n if (ifInBrowser) {\n console.log('will not write report in browser');\n return null;\n }\n\n const __dirname = dirname(__filename);\n const midscenePkgInfo = getRunningPkgInfo(__dirname);\n if (!midscenePkgInfo) {\n console.warn('midscenePkgInfo not found, will not write report');\n return null;\n }\n\n const reportPath = path.join(\n getMidsceneRunSubDir('report'),\n `${fileName}.html`,\n );\n\n reportHTMLContent(dumpData, reportPath);\n\n if (process.env.MIDSCENE_DEBUG_LOG_JSON) {\n const jsonPath = `${reportPath}.json`;\n let data = dumpData as ReportDumpWithAttributes[];\n\n if (typeof dumpData === 'string') {\n data = JSON.parse(dumpData) as ReportDumpWithAttributes[];\n }\n\n writeFileSync(\n jsonPath,\n JSON.stringify(\n data.map((item) => ({\n dumpString: JSON.parse(item.dumpString),\n attributes: item.attributes,\n })),\n null,\n 2,\n ),\n );\n\n logMsg(`Midscene - dump file written: ${jsonPath}`);\n }\n\n return reportPath;\n}\n\nexport function writeLogFile(opts: {\n fileName: string;\n fileExt: string;\n fileContent: string;\n type: 'dump' | 'cache' | 'report' | 'tmp';\n generateReport?: boolean;\n}) {\n if (ifInBrowser) {\n return '/mock/report.html';\n }\n const { fileName, fileExt, fileContent, type = 'dump' } = opts;\n const targetDir = getMidsceneRunSubDir(type);\n // Ensure directory exists\n if (!logEnvReady) {\n assert(targetDir, 'logDir should be set before writing dump file');\n\n // gitIgnore in the parent directory\n const gitIgnorePath = path.join(targetDir, '../../.gitignore');\n const gitPath = path.join(targetDir, '../../.git');\n let gitIgnoreContent = '';\n\n if (existsSync(gitPath)) {\n // if the git path exists, we need to add the log folder to the git ignore file\n if (existsSync(gitIgnorePath)) {\n gitIgnoreContent = readFileSync(gitIgnorePath, 'utf-8');\n }\n\n // ignore the log folder\n if (!gitIgnoreContent.includes(`${defaultRunDirName}/`)) {\n writeFileSync(\n gitIgnorePath,\n `${gitIgnoreContent}\\n# Midscene.js dump files\\n${defaultRunDirName}/dump\\n${defaultRunDirName}/report\\n${defaultRunDirName}/tmp\\n${defaultRunDirName}/log\\n`,\n 'utf-8',\n );\n }\n }\n\n logEnvReady = true;\n }\n\n const filePath = path.join(targetDir, `${fileName}.${fileExt}`);\n\n if (type !== 'dump') {\n // do not write dump file any more\n writeFileSync(filePath, fileContent);\n }\n\n if (opts?.generateReport) {\n return writeDumpReport(fileName, fileContent);\n }\n\n return filePath;\n}\n\nexport function getTmpDir(): string | null {\n try {\n const runningPkgInfo = getRunningPkgInfo();\n if (!runningPkgInfo) {\n return null;\n }\n const { name } = runningPkgInfo;\n const tmpPath = path.join(tmpdir(), name);\n mkdirSync(tmpPath, { recursive: true });\n return tmpPath;\n } catch (e) {\n return null;\n }\n}\n\nexport function getTmpFile(fileExtWithoutDot: string): string | null {\n if (ifInBrowser) {\n return null;\n }\n const tmpDir = getTmpDir();\n const filename = `${uuid()}.${fileExtWithoutDot}`;\n return path.join(tmpDir!, filename);\n}\n\nexport function overlapped(container: Rect, target: Rect) {\n // container and the target have some part overlapped\n return (\n container.left < target.left + target.width &&\n container.left + container.width > target.left &&\n container.top < target.top + target.height &&\n container.top + container.height > target.top\n );\n}\n\nexport async function sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function replacerForPageObject(key: string, value: any) {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n return value;\n}\n\nexport function stringifyDumpData(data: any, indents?: number) {\n return JSON.stringify(data, replacerForPageObject, indents);\n}\n\ndeclare const __VERSION__: string;\n\nexport function getVersion() {\n return __VERSION__;\n}\n\nfunction debugLog(...message: any[]) {\n const debugMode = getAIConfig(MIDSCENE_DEBUG_MODE);\n if (debugMode) {\n console.log('[Midscene]', ...message);\n }\n}\n\nlet lastReportedRepoUrl = '';\nexport function uploadTestInfoToServer({ testUrl }: { testUrl: string }) {\n let repoUrl = '';\n let userEmail = '';\n\n const extraConfig = getAIConfigInJson(MIDSCENE_OPENAI_INIT_CONFIG_JSON);\n const serverUrl = extraConfig?.REPORT_SERVER_URL;\n\n try {\n repoUrl = execSync('git config --get remote.origin.url').toString().trim();\n userEmail = execSync('git config --get user.email').toString().trim();\n } catch (error) {\n debugLog('Failed to get git info:', error);\n }\n\n // Only upload test info if:\n // 1. Server URL is configured AND\n // 2. Either:\n // - We have a repo URL that's different from last reported one (to avoid duplicate reports)\n // - OR we don't have a repo URL but have a test URL (for non-git environments)\n if (\n serverUrl &&\n ((repoUrl && repoUrl !== lastReportedRepoUrl) || (!repoUrl && testUrl))\n ) {\n debugLog('Uploading test info to server', {\n serverUrl,\n repoUrl,\n testUrl,\n userEmail,\n });\n\n fetch(serverUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n repo_url: repoUrl,\n test_url: testUrl,\n user_email: userEmail,\n }),\n })\n .then((response) => response.json())\n .then((data) => {\n debugLog('Successfully uploaded test info to server:', data);\n })\n .catch((error) =>\n debugLog('Failed to upload test info to server:', error),\n );\n lastReportedRepoUrl = repoUrl;\n }\n}\n"]}
@@ -47,6 +47,14 @@ var _constants = require('@midscene/shared/constants');
47
47
  var _extractor = require('@midscene/shared/extractor');
48
48
  var _img = require('@midscene/shared/img');
49
49
 
50
+ var AIActionType = /* @__PURE__ */ ((AIActionType2) => {
51
+ AIActionType2[AIActionType2["ASSERT"] = 0] = "ASSERT";
52
+ AIActionType2[AIActionType2["INSPECT_ELEMENT"] = 1] = "INSPECT_ELEMENT";
53
+ AIActionType2[AIActionType2["EXTRACT_DATA"] = 2] = "EXTRACT_DATA";
54
+ AIActionType2[AIActionType2["PLAN"] = 3] = "PLAN";
55
+ AIActionType2[AIActionType2["DESCRIBE_ELEMENT"] = 4] = "DESCRIBE_ELEMENT";
56
+ return AIActionType2;
57
+ })(AIActionType || {});
50
58
  async function callAiFn(msgs, AIActionTypeValue) {
51
59
  _utils.assert.call(void 0,
52
60
  checkAIConfig(),
@@ -694,6 +702,11 @@ async function describeUserPage(context, opt) {
694
702
  const treeRoot = context.tree;
695
703
  const idElementMap = {};
696
704
  const flatElements = _extractor.treeToList.call(void 0, treeRoot);
705
+ if (_optionalChain([opt, 'optionalAccess', _15 => _15.domIncluded]) === true && flatElements.length >= 5e3) {
706
+ console.warn(
707
+ 'The number of elements is too large, it may cause the prompt to be too long, please use domIncluded: "visible-only" to reduce the number of elements'
708
+ );
709
+ }
697
710
  flatElements.forEach((element) => {
698
711
  idElementMap[element.id] = element;
699
712
  if (typeof element.indexId !== "undefined") {
@@ -701,12 +714,13 @@ async function describeUserPage(context, opt) {
701
714
  }
702
715
  });
703
716
  let pageDescription = "";
704
- if (_optionalChain([opt, 'optionalAccess', _15 => _15.domIncluded])) {
717
+ const visibleOnly = _nullishCoalesce(_optionalChain([opt, 'optionalAccess', _16 => _16.visibleOnly]), () => ( _optionalChain([opt, 'optionalAccess', _17 => _17.domIncluded]) === "visible-only"));
718
+ if (_optionalChain([opt, 'optionalAccess', _18 => _18.domIncluded])) {
705
719
  const contentTree = await _extractor.descriptionOfTree.call(void 0,
706
720
  treeRoot,
707
- _optionalChain([opt, 'optionalAccess', _16 => _16.truncateTextLength]),
708
- _optionalChain([opt, 'optionalAccess', _17 => _17.filterNonTextContent]),
709
- _optionalChain([opt, 'optionalAccess', _18 => _18.visibleOnly])
721
+ _optionalChain([opt, 'optionalAccess', _19 => _19.truncateTextLength]),
722
+ _optionalChain([opt, 'optionalAccess', _20 => _20.filterNonTextContent]),
723
+ visibleOnly
710
724
  );
711
725
  const sizeDescription = describeSize({ width, height });
712
726
  pageDescription = `The size of the page: ${sizeDescription}
@@ -1239,7 +1253,7 @@ Please check your config.`
1239
1253
  httpAgent: proxyAgent,
1240
1254
  ...extraConfig,
1241
1255
  defaultHeaders: {
1242
- ..._optionalChain([extraConfig, 'optionalAccess', _19 => _19.defaultHeaders]) || {},
1256
+ ..._optionalChain([extraConfig, 'optionalAccess', _21 => _21.defaultHeaders]) || {},
1243
1257
  [_env.MIDSCENE_API_TYPE]: AIActionTypeValue.toString()
1244
1258
  },
1245
1259
  dangerouslyAllowBrowser: true
@@ -1288,6 +1302,7 @@ async function call(messages, AIActionTypeValue, responseFormat) {
1288
1302
  const model = getModelName();
1289
1303
  let content;
1290
1304
  let usage;
1305
+ let timeCost;
1291
1306
  const commonConfig = {
1292
1307
  temperature: _env.vlLocateMode.call(void 0, ) === "vlm-ui-tars" ? 0 : 0.1,
1293
1308
  stream: false,
@@ -1300,12 +1315,14 @@ async function call(messages, AIActionTypeValue, responseFormat) {
1300
1315
  debugCall(`sending request to ${model}`);
1301
1316
  let result;
1302
1317
  try {
1318
+ const startTime2 = Date.now();
1303
1319
  result = await completion.create({
1304
1320
  model,
1305
1321
  messages,
1306
1322
  response_format: responseFormat,
1307
1323
  ...commonConfig
1308
1324
  });
1325
+ timeCost = Date.now() - startTime2;
1309
1326
  } catch (e) {
1310
1327
  const newError = new Error(
1311
1328
  `failed to call AI model service: ${e.message}. Trouble shooting: https://midscenejs.com/model-provider.html`,
@@ -1316,7 +1333,7 @@ async function call(messages, AIActionTypeValue, responseFormat) {
1316
1333
  throw newError;
1317
1334
  }
1318
1335
  debugProfileStats(
1319
- `model, ${model}, mode, ${_env.vlLocateMode.call(void 0, ) || "default"}, ui-tars-version, ${_env.uiTarsModelVersion.call(void 0, )}, prompt-tokens, ${_optionalChain([result, 'access', _20 => _20.usage, 'optionalAccess', _21 => _21.prompt_tokens]) || ""}, completion-tokens, ${_optionalChain([result, 'access', _22 => _22.usage, 'optionalAccess', _23 => _23.completion_tokens]) || ""}, total-tokens, ${_optionalChain([result, 'access', _24 => _24.usage, 'optionalAccess', _25 => _25.total_tokens]) || ""}, cost-ms, ${Date.now() - startTime}, requestId, ${result._request_id || ""}`
1336
+ `model, ${model}, mode, ${_env.vlLocateMode.call(void 0, ) || "default"}, ui-tars-version, ${_env.uiTarsModelVersion.call(void 0, )}, prompt-tokens, ${_optionalChain([result, 'access', _22 => _22.usage, 'optionalAccess', _23 => _23.prompt_tokens]) || ""}, completion-tokens, ${_optionalChain([result, 'access', _24 => _24.usage, 'optionalAccess', _25 => _25.completion_tokens]) || ""}, total-tokens, ${_optionalChain([result, 'access', _26 => _26.usage, 'optionalAccess', _27 => _27.total_tokens]) || ""}, cost-ms, ${Date.now() - startTime}, requestId, ${result._request_id || ""}`
1320
1337
  );
1321
1338
  debugProfileDetail(`model usage detail: ${JSON.stringify(result.usage)}`);
1322
1339
  _utils.assert.call(void 0,
@@ -1343,6 +1360,7 @@ async function call(messages, AIActionTypeValue, responseFormat) {
1343
1360
  }
1344
1361
  return content2;
1345
1362
  };
1363
+ const startTime2 = Date.now();
1346
1364
  const result = await completion.create({
1347
1365
  model,
1348
1366
  system: "You are a versatile professional in software UI automation",
@@ -1353,11 +1371,20 @@ async function call(messages, AIActionTypeValue, responseFormat) {
1353
1371
  response_format: responseFormat,
1354
1372
  ...commonConfig
1355
1373
  });
1374
+ timeCost = Date.now() - startTime2;
1356
1375
  content = result.content[0].text;
1357
1376
  _utils.assert.call(void 0, content, "empty content");
1358
1377
  usage = result.usage;
1359
1378
  }
1360
- return { content: content || "", usage };
1379
+ return {
1380
+ content: content || "",
1381
+ usage: {
1382
+ prompt_tokens: _nullishCoalesce(_optionalChain([usage, 'optionalAccess', _28 => _28.prompt_tokens]), () => ( 0)),
1383
+ completion_tokens: _nullishCoalesce(_optionalChain([usage, 'optionalAccess', _29 => _29.completion_tokens]), () => ( 0)),
1384
+ total_tokens: _nullishCoalesce(_optionalChain([usage, 'optionalAccess', _30 => _30.total_tokens]), () => ( 0)),
1385
+ time_cost: _nullishCoalesce(timeCost, () => ( 0))
1386
+ }
1387
+ };
1361
1388
  }
1362
1389
  async function callToGetJSONObject(messages, AIActionTypeValue) {
1363
1390
  let responseFormat;
@@ -1417,8 +1444,8 @@ function preprocessDoubaoBboxJson(input) {
1417
1444
  }
1418
1445
  function safeParseJson(input) {
1419
1446
  const cleanJsonString = extractJSONFromCodeBlock(input);
1420
- if (_optionalChain([cleanJsonString, 'optionalAccess', _26 => _26.match, 'call', _27 => _27(/\((\d+),(\d+)\)/)])) {
1421
- return _optionalChain([cleanJsonString, 'access', _28 => _28.match, 'call', _29 => _29(/\((\d+),(\d+)\)/), 'optionalAccess', _30 => _30.slice, 'call', _31 => _31(1), 'access', _32 => _32.map, 'call', _33 => _33(Number)]);
1447
+ if (_optionalChain([cleanJsonString, 'optionalAccess', _31 => _31.match, 'call', _32 => _32(/\((\d+),(\d+)\)/)])) {
1448
+ return _optionalChain([cleanJsonString, 'access', _33 => _33.match, 'call', _34 => _34(/\((\d+),(\d+)\)/), 'optionalAccess', _35 => _35.slice, 'call', _36 => _36(1), 'access', _37 => _37.map, 'call', _38 => _38(Number)]);
1422
1449
  }
1423
1450
  try {
1424
1451
  return JSON.parse(cleanJsonString);
@@ -1435,6 +1462,325 @@ function safeParseJson(input) {
1435
1462
  throw Error(`failed to parse json response: ${input}`);
1436
1463
  }
1437
1464
 
1465
+ // src/ai-model/prompt/playwright-generator.ts
1466
+
1467
+ var getScreenshotsForLLM = (events, maxScreenshots = 1) => {
1468
+ const eventsWithScreenshots = events.filter(
1469
+ (event) => event.screenshotBefore || event.screenshotAfter || event.screenshotWithBox
1470
+ );
1471
+ const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {
1472
+ if (a.type === "navigation" && b.type !== "navigation")
1473
+ return -1;
1474
+ if (a.type !== "navigation" && b.type === "navigation")
1475
+ return 1;
1476
+ if (a.type === "click" && b.type !== "click")
1477
+ return -1;
1478
+ if (a.type !== "click" && b.type === "click")
1479
+ return 1;
1480
+ return 0;
1481
+ });
1482
+ const screenshots = [];
1483
+ for (const event of sortedEvents) {
1484
+ const screenshot = event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
1485
+ if (screenshot && !screenshots.includes(screenshot)) {
1486
+ screenshots.push(screenshot);
1487
+ if (screenshots.length >= maxScreenshots)
1488
+ break;
1489
+ }
1490
+ }
1491
+ return screenshots;
1492
+ };
1493
+ var filterEventsByType = (events) => {
1494
+ return {
1495
+ navigationEvents: events.filter((event) => event.type === "navigation"),
1496
+ clickEvents: events.filter((event) => event.type === "click"),
1497
+ inputEvents: events.filter((event) => event.type === "input"),
1498
+ scrollEvents: events.filter((event) => event.type === "scroll")
1499
+ };
1500
+ };
1501
+ var createEventCounts = (filteredEvents, totalEvents) => {
1502
+ return {
1503
+ navigation: filteredEvents.navigationEvents.length,
1504
+ click: filteredEvents.clickEvents.length,
1505
+ input: filteredEvents.inputEvents.length,
1506
+ scroll: filteredEvents.scrollEvents.length,
1507
+ total: totalEvents
1508
+ };
1509
+ };
1510
+ var extractInputDescriptions = (inputEvents) => {
1511
+ return inputEvents.map((event) => ({
1512
+ description: event.elementDescription || "",
1513
+ value: event.value || ""
1514
+ })).filter((item) => item.description && item.value);
1515
+ };
1516
+ var processEventsForLLM = (events) => {
1517
+ return events.map((event) => ({
1518
+ type: event.type,
1519
+ timestamp: event.timestamp,
1520
+ url: event.url,
1521
+ title: event.title,
1522
+ elementDescription: event.elementDescription,
1523
+ value: event.value,
1524
+ pageInfo: event.pageInfo,
1525
+ elementRect: event.elementRect
1526
+ }));
1527
+ };
1528
+ var prepareEventSummary = (events, options = {}) => {
1529
+ const filteredEvents = filterEventsByType(events);
1530
+ const eventCounts = createEventCounts(filteredEvents, events.length);
1531
+ const startUrl = filteredEvents.navigationEvents.length > 0 ? filteredEvents.navigationEvents[0].url || "" : "";
1532
+ const pageTitles = filteredEvents.navigationEvents.map((event) => event.title).filter((title) => Boolean(title)).slice(0, 5);
1533
+ const clickDescriptions = filteredEvents.clickEvents.map((event) => event.elementDescription).filter((desc) => Boolean(desc)).slice(0, 10);
1534
+ const inputDescriptions = extractInputDescriptions(
1535
+ filteredEvents.inputEvents
1536
+ ).slice(0, 10);
1537
+ const urls = filteredEvents.navigationEvents.map((e) => e.url).filter((url) => Boolean(url)).slice(0, 5);
1538
+ const processedEvents = processEventsForLLM(events);
1539
+ return {
1540
+ testName: options.testName || "Automated test from recorded events",
1541
+ startUrl,
1542
+ eventCounts,
1543
+ pageTitles,
1544
+ urls,
1545
+ clickDescriptions,
1546
+ inputDescriptions,
1547
+ events: processedEvents
1548
+ };
1549
+ };
1550
+ var createMessageContent = (promptText, screenshots = [], includeScreenshots = true) => {
1551
+ const messageContent = [
1552
+ {
1553
+ type: "text",
1554
+ text: promptText
1555
+ }
1556
+ ];
1557
+ if (includeScreenshots && screenshots.length > 0) {
1558
+ messageContent.unshift({
1559
+ type: "text",
1560
+ text: "Here are screenshots from the recording session to help you understand the context:"
1561
+ });
1562
+ screenshots.forEach((screenshot) => {
1563
+ messageContent.push({
1564
+ type: "image_url",
1565
+ image_url: {
1566
+ url: screenshot
1567
+ }
1568
+ });
1569
+ });
1570
+ }
1571
+ return messageContent;
1572
+ };
1573
+ var validateEvents = (events) => {
1574
+ if (!events.length) {
1575
+ throw new Error("No events provided for test generation");
1576
+ }
1577
+ };
1578
+ var generatePlaywrightTest = async (events, options = {}) => {
1579
+ validateEvents(events);
1580
+ const summary = prepareEventSummary(events, {
1581
+ testName: options.testName,
1582
+ maxScreenshots: options.maxScreenshots || 3
1583
+ });
1584
+ const playwrightSummary = {
1585
+ ...summary,
1586
+ waitForNetworkIdle: options.waitForNetworkIdle !== false,
1587
+ waitForNetworkIdleTimeout: options.waitForNetworkIdleTimeout || 2e3,
1588
+ viewportSize: options.viewportSize || { width: 1280, height: 800 }
1589
+ };
1590
+ const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
1591
+ const promptText = `Generate a Playwright test using @midscene/web/playwright that reproduces this recorded browser session. The test should be based on the following events and follow the structure of the example provided. Make the test descriptive with appropriate assertions and validations.
1592
+
1593
+ Event Summary:
1594
+ ${JSON.stringify(playwrightSummary, null, 2)}
1595
+
1596
+ Generated code should:
1597
+ 1. Import required dependencies
1598
+ 2. Set up the test with proper configuration
1599
+ 3. Include a beforeEach hook to navigate to the starting URL
1600
+ 4. Implement a test that uses Midscene AI methods (aiTap, aiInput, aiAssert, etc.)
1601
+ 5. Include appropriate assertions and validations
1602
+ 6. Follow best practices for Playwright tests
1603
+ 7. Be ready to execute without further modification
1604
+
1605
+ Respond ONLY with the complete Playwright test code, no explanations.`;
1606
+ const messageContent = createMessageContent(
1607
+ promptText,
1608
+ screenshots,
1609
+ options.includeScreenshots !== false
1610
+ );
1611
+ const systemPrompt = `You are an expert test automation engineer specializing in Playwright and Midscene.
1612
+ Your task is to generate a complete, executable Playwright test using @midscene/web/playwright that reproduces a recorded browser session.
1613
+
1614
+ ${_constants.PLAYWRIGHT_EXAMPLE_CODE}`;
1615
+ const prompt = [
1616
+ {
1617
+ role: "system",
1618
+ content: systemPrompt
1619
+ },
1620
+ {
1621
+ role: "user",
1622
+ content: messageContent
1623
+ }
1624
+ ];
1625
+ const response = await call(prompt, 2 /* EXTRACT_DATA */);
1626
+ if (_optionalChain([response, 'optionalAccess', _39 => _39.content]) && typeof response.content === "string") {
1627
+ return response.content;
1628
+ }
1629
+ throw new Error("Failed to generate Playwright test code");
1630
+ };
1631
+
1632
+ // src/ai-model/prompt/yaml-generator.ts
1633
+
1634
+ var getScreenshotsForLLM2 = (events, maxScreenshots = 1) => {
1635
+ const eventsWithScreenshots = events.filter(
1636
+ (event) => event.screenshotBefore || event.screenshotAfter || event.screenshotWithBox
1637
+ );
1638
+ const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {
1639
+ if (a.type === "navigation" && b.type !== "navigation")
1640
+ return -1;
1641
+ if (a.type !== "navigation" && b.type === "navigation")
1642
+ return 1;
1643
+ if (a.type === "click" && b.type !== "click")
1644
+ return -1;
1645
+ if (a.type !== "click" && b.type === "click")
1646
+ return 1;
1647
+ return 0;
1648
+ });
1649
+ const screenshots = [];
1650
+ for (const event of sortedEvents) {
1651
+ const screenshot = event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
1652
+ if (screenshot && !screenshots.includes(screenshot)) {
1653
+ screenshots.push(screenshot);
1654
+ if (screenshots.length >= maxScreenshots)
1655
+ break;
1656
+ }
1657
+ }
1658
+ return screenshots;
1659
+ };
1660
+ var filterEventsByType2 = (events) => {
1661
+ return {
1662
+ navigationEvents: events.filter((event) => event.type === "navigation"),
1663
+ clickEvents: events.filter((event) => event.type === "click"),
1664
+ inputEvents: events.filter((event) => event.type === "input"),
1665
+ scrollEvents: events.filter((event) => event.type === "scroll")
1666
+ };
1667
+ };
1668
+ var createEventCounts2 = (filteredEvents, totalEvents) => {
1669
+ return {
1670
+ navigation: filteredEvents.navigationEvents.length,
1671
+ click: filteredEvents.clickEvents.length,
1672
+ input: filteredEvents.inputEvents.length,
1673
+ scroll: filteredEvents.scrollEvents.length,
1674
+ total: totalEvents
1675
+ };
1676
+ };
1677
+ var extractInputDescriptions2 = (inputEvents) => {
1678
+ return inputEvents.map((event) => ({
1679
+ description: event.elementDescription || "",
1680
+ value: event.value || ""
1681
+ })).filter((item) => item.description && item.value);
1682
+ };
1683
+ var processEventsForLLM2 = (events) => {
1684
+ return events.map((event) => ({
1685
+ type: event.type,
1686
+ timestamp: event.timestamp,
1687
+ url: event.url,
1688
+ title: event.title,
1689
+ elementDescription: event.elementDescription,
1690
+ value: event.value,
1691
+ pageInfo: event.pageInfo,
1692
+ elementRect: event.elementRect
1693
+ }));
1694
+ };
1695
+ var prepareEventSummary2 = (events, options = {}) => {
1696
+ const filteredEvents = filterEventsByType2(events);
1697
+ const eventCounts = createEventCounts2(filteredEvents, events.length);
1698
+ const startUrl = filteredEvents.navigationEvents.length > 0 ? filteredEvents.navigationEvents[0].url || "" : "";
1699
+ const pageTitles = filteredEvents.navigationEvents.map((event) => event.title).filter((title) => Boolean(title)).slice(0, 5);
1700
+ const clickDescriptions = filteredEvents.clickEvents.map((event) => event.elementDescription).filter((desc) => Boolean(desc)).slice(0, 10);
1701
+ const inputDescriptions = extractInputDescriptions2(
1702
+ filteredEvents.inputEvents
1703
+ ).slice(0, 10);
1704
+ const urls = filteredEvents.navigationEvents.map((e) => e.url).filter((url) => Boolean(url)).slice(0, 5);
1705
+ const processedEvents = processEventsForLLM2(events);
1706
+ return {
1707
+ testName: options.testName || "Automated test from recorded events",
1708
+ startUrl,
1709
+ eventCounts,
1710
+ pageTitles,
1711
+ urls,
1712
+ clickDescriptions,
1713
+ inputDescriptions,
1714
+ events: processedEvents
1715
+ };
1716
+ };
1717
+ var validateEvents2 = (events) => {
1718
+ if (!events.length) {
1719
+ throw new Error("No events provided for test generation");
1720
+ }
1721
+ };
1722
+ var generateYamlTest = async (events, options = {}) => {
1723
+ try {
1724
+ validateEvents2(events);
1725
+ const summary = prepareEventSummary2(events, {
1726
+ testName: options.testName,
1727
+ maxScreenshots: options.maxScreenshots || 3
1728
+ });
1729
+ const yamlSummary = {
1730
+ ...summary,
1731
+ includeTimestamps: options.includeTimestamps || false
1732
+ };
1733
+ const screenshots = getScreenshotsForLLM2(
1734
+ events,
1735
+ options.maxScreenshots || 3
1736
+ );
1737
+ const prompt = [
1738
+ {
1739
+ role: "system",
1740
+ content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${_constants.YAML_EXAMPLE_CODE}`
1741
+ },
1742
+ {
1743
+ role: "user",
1744
+ content: `Generate YAML test for Midscene.js automation from recorded browser events.
1745
+
1746
+ Event Summary:
1747
+ ${JSON.stringify(yamlSummary, null, 2)}
1748
+
1749
+ Convert events:
1750
+ - navigation → target.url
1751
+ - click → aiTap with element description
1752
+ - input → aiInput with value and locate
1753
+ - scroll → aiScroll with appropriate direction
1754
+ - Add aiAssert for important state changes
1755
+
1756
+ Respond with YAML only, no explanations.`
1757
+ }
1758
+ ];
1759
+ if (screenshots.length > 0) {
1760
+ prompt.push({
1761
+ role: "user",
1762
+ content: "Here are screenshots from the recording session to help you understand the context:"
1763
+ });
1764
+ prompt.push({
1765
+ role: "user",
1766
+ content: screenshots.map((screenshot) => ({
1767
+ type: "image_url",
1768
+ image_url: {
1769
+ url: screenshot
1770
+ }
1771
+ }))
1772
+ });
1773
+ }
1774
+ const response = await call(prompt, 2 /* EXTRACT_DATA */);
1775
+ if (_optionalChain([response, 'optionalAccess', _40 => _40.content]) && typeof response.content === "string") {
1776
+ return response.content;
1777
+ }
1778
+ throw new Error("Failed to generate YAML test configuration");
1779
+ } catch (error) {
1780
+ throw new Error(`Failed to generate YAML test: ${error}`);
1781
+ }
1782
+ };
1783
+
1438
1784
  // src/ai-model/inspect.ts
1439
1785
 
1440
1786
 
@@ -1630,7 +1976,7 @@ async function AiLocateElement(options) {
1630
1976
  );
1631
1977
  }
1632
1978
  let referenceImagePayload;
1633
- if (_optionalChain([options, 'access', _34 => _34.referenceImage, 'optionalAccess', _35 => _35.rect]) && options.referenceImage.base64) {
1979
+ if (_optionalChain([options, 'access', _41 => _41.referenceImage, 'optionalAccess', _42 => _42.rect]) && options.referenceImage.base64) {
1634
1980
  referenceImagePayload = await _img.cropByRect.call(void 0,
1635
1981
  options.referenceImage.base64,
1636
1982
  options.referenceImage.rect,
@@ -1666,10 +2012,10 @@ async function AiLocateElement(options) {
1666
2012
  if ("bbox" in res.content && Array.isArray(res.content.bbox)) {
1667
2013
  resRect = adaptBboxToRect(
1668
2014
  res.content.bbox,
1669
- _optionalChain([options, 'access', _36 => _36.searchConfig, 'optionalAccess', _37 => _37.rect, 'optionalAccess', _38 => _38.width]) || context.size.width,
1670
- _optionalChain([options, 'access', _39 => _39.searchConfig, 'optionalAccess', _40 => _40.rect, 'optionalAccess', _41 => _41.height]) || context.size.height,
1671
- _optionalChain([options, 'access', _42 => _42.searchConfig, 'optionalAccess', _43 => _43.rect, 'optionalAccess', _44 => _44.left]),
1672
- _optionalChain([options, 'access', _45 => _45.searchConfig, 'optionalAccess', _46 => _46.rect, 'optionalAccess', _47 => _47.top])
2015
+ _optionalChain([options, 'access', _43 => _43.searchConfig, 'optionalAccess', _44 => _44.rect, 'optionalAccess', _45 => _45.width]) || context.size.width,
2016
+ _optionalChain([options, 'access', _46 => _46.searchConfig, 'optionalAccess', _47 => _47.rect, 'optionalAccess', _48 => _48.height]) || context.size.height,
2017
+ _optionalChain([options, 'access', _49 => _49.searchConfig, 'optionalAccess', _50 => _50.rect, 'optionalAccess', _51 => _51.left]),
2018
+ _optionalChain([options, 'access', _52 => _52.searchConfig, 'optionalAccess', _53 => _53.rect, 'optionalAccess', _54 => _54.top])
1673
2019
  );
1674
2020
  debugInspect("resRect", resRect);
1675
2021
  const rectCenter = {
@@ -1688,7 +2034,7 @@ async function AiLocateElement(options) {
1688
2034
  }
1689
2035
  } catch (e) {
1690
2036
  const msg = e instanceof Error ? `Failed to parse bbox: ${e.message}` : "unknown error in locate";
1691
- if (!errors || _optionalChain([errors, 'optionalAccess', _48 => _48.length]) === 0) {
2037
+ if (!errors || _optionalChain([errors, 'optionalAccess', _55 => _55.length]) === 0) {
1692
2038
  errors = [msg];
1693
2039
  } else {
1694
2040
  errors.push(`(${msg})`);
@@ -1779,14 +2125,14 @@ async function AiExtractElementInfo(options) {
1779
2125
  truncateTextLength: 200,
1780
2126
  filterNonTextContent: false,
1781
2127
  visibleOnly: false,
1782
- domIncluded: _optionalChain([extractOption, 'optionalAccess', _49 => _49.domIncluded])
2128
+ domIncluded: _optionalChain([extractOption, 'optionalAccess', _56 => _56.domIncluded])
1783
2129
  });
1784
2130
  const extractDataPromptText = await extractDataQueryPrompt(
1785
2131
  description,
1786
2132
  dataQuery
1787
2133
  );
1788
2134
  const userContent = [];
1789
- if (_optionalChain([extractOption, 'optionalAccess', _50 => _50.screenshotIncluded]) !== false) {
2135
+ if (_optionalChain([extractOption, 'optionalAccess', _57 => _57.screenshotIncluded]) !== false) {
1790
2136
  userContent.push({
1791
2137
  type: "image_url",
1792
2138
  image_url: {
@@ -1914,7 +2260,7 @@ async function plan(userInstruction, opts) {
1914
2260
  const { content, usage } = await call2(msgs, 3 /* PLAN */);
1915
2261
  const rawResponse = JSON.stringify(content, void 0, 2);
1916
2262
  const planFromAI = content;
1917
- const actions = (_optionalChain([planFromAI, 'access', _51 => _51.action, 'optionalAccess', _52 => _52.type]) ? [planFromAI.action] : planFromAI.actions) || [];
2263
+ const actions = (_optionalChain([planFromAI, 'access', _58 => _58.action, 'optionalAccess', _59 => _59.type]) ? [planFromAI.action] : planFromAI.actions) || [];
1918
2264
  const returnValue = {
1919
2265
  ...planFromAI,
1920
2266
  actions,
@@ -1941,7 +2287,7 @@ async function plan(userInstruction, opts) {
1941
2287
  _utils.assert.call(void 0, !planFromAI.error, `Failed to plan actions: ${planFromAI.error}`);
1942
2288
  } else {
1943
2289
  actions.forEach((action) => {
1944
- if (_optionalChain([action, 'access', _53 => _53.locate, 'optionalAccess', _54 => _54.id])) {
2290
+ if (_optionalChain([action, 'access', _60 => _60.locate, 'optionalAccess', _61 => _61.id])) {
1945
2291
  const element = elementById(action.locate.id);
1946
2292
  if (element) {
1947
2293
  action.locate.id = element.id;
@@ -2226,6 +2572,10 @@ async function resizeImageForUiTars(imageBase64, size) {
2226
2572
 
2227
2573
 
2228
2574
 
2229
- exports.systemPromptToLocateElement = systemPromptToLocateElement; exports.elementByPositionWithElementInfo = elementByPositionWithElementInfo; exports.describeUserPage = describeUserPage; exports.callToGetJSONObject = callToGetJSONObject; exports.callAiFn = callAiFn; exports.adaptBboxToRect = adaptBboxToRect; exports.expandSearchArea = expandSearchArea; exports.AiLocateElement = AiLocateElement; exports.AiLocateSection = AiLocateSection; exports.AiExtractElementInfo = AiExtractElementInfo; exports.AiAssert = AiAssert; exports.plan = plan; exports.vlmPlanning = vlmPlanning; exports.resizeImageForUiTars = resizeImageForUiTars;
2230
2575
 
2231
- //# sourceMappingURL=chunk-X2UALJ3B.js.map
2576
+
2577
+
2578
+
2579
+ exports.systemPromptToLocateElement = systemPromptToLocateElement; exports.elementByPositionWithElementInfo = elementByPositionWithElementInfo; exports.describeUserPage = describeUserPage; exports.call = call; exports.callToGetJSONObject = callToGetJSONObject; exports.AIActionType = AIActionType; exports.callAiFn = callAiFn; exports.adaptBboxToRect = adaptBboxToRect; exports.expandSearchArea = expandSearchArea; exports.generatePlaywrightTest = generatePlaywrightTest; exports.generateYamlTest = generateYamlTest; exports.AiLocateElement = AiLocateElement; exports.AiLocateSection = AiLocateSection; exports.AiExtractElementInfo = AiExtractElementInfo; exports.AiAssert = AiAssert; exports.plan = plan; exports.vlmPlanning = vlmPlanning; exports.resizeImageForUiTars = resizeImageForUiTars;
2580
+
2581
+ //# sourceMappingURL=chunk-EYIL4AHP.js.map