@midscene/core 1.10.6 → 1.10.7-beta-20260720030503.0

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/agent/utils.mjs +3 -15
  2. package/dist/es/agent/utils.mjs.map +1 -1
  3. package/dist/es/dump/report-action-dump.mjs +5 -2
  4. package/dist/es/dump/report-action-dump.mjs.map +1 -1
  5. package/dist/es/dump/screenshot-store.mjs +5 -2
  6. package/dist/es/dump/screenshot-store.mjs.map +1 -1
  7. package/dist/es/report-cli.mjs.map +1 -1
  8. package/dist/es/report-markdown.mjs +19 -19
  9. package/dist/es/report-markdown.mjs.map +1 -1
  10. package/dist/es/report.mjs +4 -3
  11. package/dist/es/report.mjs.map +1 -1
  12. package/dist/es/screenshot-item.mjs +13 -8
  13. package/dist/es/screenshot-item.mjs.map +1 -1
  14. package/dist/es/types.mjs.map +1 -1
  15. package/dist/es/utils.mjs +2 -2
  16. package/dist/lib/agent/utils.js +2 -14
  17. package/dist/lib/agent/utils.js.map +1 -1
  18. package/dist/lib/dump/report-action-dump.js +5 -2
  19. package/dist/lib/dump/report-action-dump.js.map +1 -1
  20. package/dist/lib/dump/screenshot-store.js +5 -2
  21. package/dist/lib/dump/screenshot-store.js.map +1 -1
  22. package/dist/lib/report-cli.js.map +1 -1
  23. package/dist/lib/report-markdown.js +19 -19
  24. package/dist/lib/report-markdown.js.map +1 -1
  25. package/dist/lib/report.js +4 -3
  26. package/dist/lib/report.js.map +1 -1
  27. package/dist/lib/screenshot-item.js +13 -8
  28. package/dist/lib/screenshot-item.js.map +1 -1
  29. package/dist/lib/types.js.map +1 -1
  30. package/dist/lib/utils.js +2 -2
  31. package/dist/types/dump/report-action-dump.d.ts +2 -2
  32. package/dist/types/dump/screenshot-store.d.ts +2 -1
  33. package/dist/types/report-markdown.d.ts +2 -1
  34. package/dist/types/screenshot-item.d.ts +6 -3
  35. package/dist/types/types.d.ts +2 -2
  36. package/package.json +2 -2
@@ -62,18 +62,6 @@ const external_dayjs_namespaceObject = require("dayjs");
62
62
  var external_dayjs_default = /*#__PURE__*/ __webpack_require__.n(external_dayjs_namespaceObject);
63
63
  const external_task_cache_js_namespaceObject = require("./task-cache.js");
64
64
  const agentDebug = (0, logger_namespaceObject.getDebug)('agent');
65
- const screenshotDataUrlPattern = /^data:image\/[a-zA-Z0-9.+-]+;base64,/i;
66
- const inferBase64ImageFormat = (base64Body)=>{
67
- if (base64Body.startsWith('iVBORw0KGgo')) return 'png';
68
- return 'jpeg';
69
- };
70
- const normalizeScreenshotBase64 = (screenshotBase64)=>{
71
- const trimmedBase64 = screenshotBase64.trim();
72
- if (screenshotDataUrlPattern.test(trimmedBase64)) return trimmedBase64;
73
- const base64Body = trimmedBase64.replace(/\s/g, '');
74
- (0, utils_namespaceObject.assert)(base64Body, 'screenshotBase64 must include image data');
75
- return (0, img_namespaceObject.createImgBase64ByFormat)(inferBase64ImageFormat(base64Body), base64Body);
76
- };
77
65
  const legacyScrollTypeMap = {
78
66
  once: 'singleAction',
79
67
  untilBottom: 'scrollToBottom',
@@ -163,7 +151,7 @@ async function commonContextParser(interfaceInstance, _opt) {
163
151
  }
164
152
  }
165
153
  async function createScreenshotBoundUIContext(screenshotBase64, opt) {
166
- const normalizedScreenshotBase64 = normalizeScreenshotBase64(screenshotBase64);
154
+ const normalizedScreenshotBase64 = (0, img_namespaceObject.normalizeBase64Image)(screenshotBase64);
167
155
  const actualScreenshotSize = await (0, img_namespaceObject.imageInfoOfBase64)(normalizedScreenshotBase64);
168
156
  if (opt.screenshotSize && (opt.screenshotSize.width !== actualScreenshotSize.width || opt.screenshotSize.height !== actualScreenshotSize.height)) agentDebug('describeElementAtPoint screenshotSize mismatch, use actual size', {
169
157
  provided: opt.screenshotSize,
@@ -232,7 +220,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
232
220
  return;
233
221
  }
234
222
  }
235
- const getMidsceneVersion = ()=>"1.10.6";
223
+ const getMidsceneVersion = ()=>"1.10.7-beta-20260720030503.0";
236
224
  const parsePrompt = (prompt)=>{
237
225
  if ('string' == typeof prompt) return {
238
226
  textPrompt: prompt,
@@ -1 +1 @@
1
- {"version":3,"file":"agent/utils.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/agent/utils.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pixelBboxToRect } from '@/ai-model/workflows/inspect/locate-result-rect';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ElementCacheFeature,\n LocateResultElement,\n PixelBbox,\n PlanningLocateParam,\n PlanningLocateParamWithLocatedPixelBbox,\n Rect,\n ScrollParam,\n Size,\n UIContext,\n} from '@/types';\nimport { uploadTestInfoToServer } from '@/utils';\nimport {\n MIDSCENE_REPORT_QUIET,\n MIDSCENE_REPORT_TAG_NAME,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { generateElementByRect } from '@midscene/shared/extractor';\nimport {\n convertImgBufferToJpeg,\n createImgBase64ByFormat,\n imageInfoOfBase64,\n parseBase64,\n resizeImgBase64,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { assert, ifInBrowser, logMsg, uuid } from '@midscene/shared/utils';\nimport dayjs from 'dayjs';\nimport type { TaskCache } from './task-cache';\nimport { debug as cacheDebug } from './task-cache';\n\nconst agentDebug = getDebug('agent');\nconst screenshotDataUrlPattern = /^data:image\\/[a-zA-Z0-9.+-]+;base64,/i;\n\nconst inferBase64ImageFormat = (base64Body: string) => {\n if (base64Body.startsWith('iVBORw0KGgo')) {\n return 'png';\n }\n return 'jpeg';\n};\n\nconst normalizeScreenshotBase64 = (screenshotBase64: string) => {\n const trimmedBase64 = screenshotBase64.trim();\n if (screenshotDataUrlPattern.test(trimmedBase64)) {\n return trimmedBase64;\n }\n\n const base64Body = trimmedBase64.replace(/\\s/g, '');\n assert(base64Body, 'screenshotBase64 must include image data');\n return createImgBase64ByFormat(\n inferBase64ImageFormat(base64Body),\n base64Body,\n );\n};\n\nconst legacyScrollTypeMap = {\n once: 'singleAction',\n untilBottom: 'scrollToBottom',\n untilTop: 'scrollToTop',\n untilRight: 'scrollToRight',\n untilLeft: 'scrollToLeft',\n} as const;\n\nexport const normalizeScrollType = (\n scrollType: string | undefined,\n): ScrollParam['scrollType'] | undefined => {\n if (!scrollType) {\n return undefined;\n }\n\n if (scrollType in legacyScrollTypeMap) {\n return legacyScrollTypeMap[scrollType as keyof typeof legacyScrollTypeMap];\n }\n\n return scrollType as ScrollParam['scrollType'];\n};\n\nexport async function commonContextParser(\n interfaceInstance: AbstractInterface,\n _opt: {\n uploadServerUrl?: string;\n screenshotShrinkFactor?: number;\n },\n): Promise<UIContext> {\n const debug = getDebug('commonContextParser');\n\n assert(interfaceInstance, 'interfaceInstance is required');\n\n debug('Getting interface description');\n const description = interfaceInstance.describe?.() || '';\n debug('Interface description end');\n\n debug('Uploading test info to server');\n uploadTestInfoToServer({\n testUrl: description,\n serverUrl: _opt.uploadServerUrl,\n });\n debug('UploadTestInfoToServer end');\n\n debug('will get size');\n const interfaceSize = await interfaceInstance.size();\n const { width: logicalWidth, height: logicalHeight } = interfaceSize;\n\n if ((interfaceSize as unknown as { dpr: number }).dpr) {\n console.warn(\n 'Warning: return value of interface.size() include a dpr property, which is not expected and ignored. ',\n );\n }\n\n if (!Number.isFinite(logicalWidth) || !Number.isFinite(logicalHeight)) {\n throw new Error(\n `Invalid interface size: width and height must be finite numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n if (logicalWidth <= 0 || logicalHeight <= 0) {\n throw new Error(\n `Invalid interface size: width and height must be positive numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n debug(`size: ${logicalWidth}x${logicalHeight}`);\n\n const screenshotBase64 = await interfaceInstance.screenshotBase64();\n const screenshotCapturedAt = Date.now();\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n // Get physical screenshot dimensions\n debug('will get screenshot dimensions');\n const { width: imgWidth, height: imgHeight } =\n await imageInfoOfBase64(screenshotBase64);\n\n if (!Number.isFinite(imgWidth) || !Number.isFinite(imgHeight)) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be finite numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n if (imgWidth <= 0 || imgHeight <= 0) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be positive numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n debug('screenshot dimensions', imgWidth, 'x', imgHeight);\n\n // Detect orientation mismatch between logical size and screenshot.\n // Some devices (e.g. OPPO) report wrong orientation via ADB, causing\n // size() to return portrait dimensions even when the device is landscape.\n // We detect this by comparing aspect ratios and swap if they disagree.\n const logicalIsPortrait = logicalWidth < logicalHeight;\n const screenshotIsPortrait = imgWidth < imgHeight;\n let finalLogicalWidth = logicalWidth;\n let finalLogicalHeight = logicalHeight;\n if (logicalIsPortrait !== screenshotIsPortrait) {\n debug(\n `Orientation mismatch detected: logical size ${logicalWidth}x${logicalHeight} (${logicalIsPortrait ? 'portrait' : 'landscape'}) vs screenshot ${imgWidth}x${imgHeight} (${screenshotIsPortrait ? 'portrait' : 'landscape'}). Swapping logical dimensions.`,\n );\n finalLogicalWidth = logicalHeight;\n finalLogicalHeight = logicalWidth;\n }\n\n const userShrinkFactor = _opt.screenshotShrinkFactor ?? 1;\n\n if (!Number.isFinite(userShrinkFactor) || userShrinkFactor < 1) {\n throw new Error(\n `Invalid screenshotShrinkFactor: must be a finite number >= 1. Received: ${userShrinkFactor}`,\n );\n }\n\n const dpr = imgWidth / finalLogicalWidth;\n\n debug('calculated dpr:', dpr);\n\n const shrunkShotToLogicalRatio = dpr / userShrinkFactor;\n\n debug('shrunkShotToLogicalRatio', shrunkShotToLogicalRatio);\n\n if (userShrinkFactor !== 1) {\n const targetWidth = Math.round(imgWidth / userShrinkFactor);\n const targetHeight = Math.round(imgHeight / userShrinkFactor);\n\n debug(\n `Applying screenshot shrink factor: ${userShrinkFactor} (physical: ${imgWidth}x${imgHeight} -> target: ${targetWidth}x${targetHeight})`,\n );\n\n const resizedBase64 = await resizeImgBase64(screenshotBase64, {\n width: targetWidth,\n height: targetHeight,\n });\n return {\n shotSize: {\n width: targetWidth,\n height: targetHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(resizedBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n } else {\n // For screenshots that do not need shrinking, convert PNG to JPEG to reduce the image payload in model requests and reports. (Shrunk images are already JPEG.)\n // This mainly covers Android's default screenshot path, which produces PNG screenshots.\n // Compared with conversion on Android, centralizing it here means each platform does not need to handle screenshot formats itself, and allows future output formats such as WebP.\n // Built-in paths that already output JPEG are unaffected, and custom devices that output JPEG will not be compressed again.\n // The Web platform already outputs JPEG, so it does not enter this branch. Other built-in device platforms run in Node, where Sharp conversion is fast enough that its extra cost is negligible.\n let outputScreenshotBase64 = screenshotBase64;\n const { mimeType, body } = parseBase64(screenshotBase64);\n if (mimeType.toLowerCase() === 'image/png') {\n const jpegBuffer = await convertImgBufferToJpeg(\n Buffer.from(body, 'base64'),\n 90,\n );\n outputScreenshotBase64 = createImgBase64ByFormat(\n 'jpeg',\n jpegBuffer.toString('base64'),\n );\n }\n\n return {\n shotSize: {\n width: imgWidth,\n height: imgHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(\n outputScreenshotBase64,\n screenshotCapturedAt,\n ),\n shrunkShotToLogicalRatio,\n };\n }\n}\n\nexport async function createScreenshotBoundUIContext(\n screenshotBase64: string,\n opt: {\n screenshotSize?: Size;\n },\n): Promise<UIContext> {\n const normalizedScreenshotBase64 =\n normalizeScreenshotBase64(screenshotBase64);\n const actualScreenshotSize = await imageInfoOfBase64(\n normalizedScreenshotBase64,\n );\n if (\n opt.screenshotSize &&\n (opt.screenshotSize.width !== actualScreenshotSize.width ||\n opt.screenshotSize.height !== actualScreenshotSize.height)\n ) {\n agentDebug(\n 'describeElementAtPoint screenshotSize mismatch, use actual size',\n {\n provided: opt.screenshotSize,\n actual: actualScreenshotSize,\n },\n );\n }\n\n return {\n screenshot: ScreenshotItem.create(normalizedScreenshotBase64, Date.now()),\n shotSize: actualScreenshotSize,\n shrunkShotToLogicalRatio: 1,\n _isFrozen: true,\n };\n}\n\nexport function getReportFileName(tag = 'web') {\n const reportTagName = globalConfigManager.getEnvConfigValue(\n MIDSCENE_REPORT_TAG_NAME,\n );\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 if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET)) {\n return;\n }\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\ntype NormalizeFilePathsOptions = {\n fileExists?: (path: string) => boolean;\n isInBrowser?: boolean;\n resolvePath?: (path: string) => string;\n wslDistroName?: string;\n cwd?: string;\n};\n\nexport function normalizeFilePaths(\n files: string[],\n options: NormalizeFilePathsOptions = {},\n): string[] {\n const {\n fileExists = existsSync,\n isInBrowser = ifInBrowser,\n resolvePath = resolve,\n wslDistroName = process.env.WSL_DISTRO_NAME,\n cwd = process.cwd(),\n } = options;\n\n if (isInBrowser) {\n throw new Error('File chooser is not supported in browser environment');\n }\n\n return files.map((file) => {\n const absolutePath = resolvePath(file);\n if (!fileExists(absolutePath)) {\n throw new Error(\n `File not found: ${file}. Resolved to: ${absolutePath}. Current working directory: ${cwd}`,\n );\n }\n\n if (!wslDistroName) {\n return absolutePath;\n }\n\n const wslMount = absolutePath.match(/^\\/mnt\\/([a-z])\\//);\n if (wslMount) {\n return `${wslMount[1].toUpperCase()}:\\\\${absolutePath.slice(7).replace(/\\//g, '\\\\')}`;\n }\n\n return `\\\\\\\\wsl$\\\\${wslDistroName}${absolutePath.replace(/\\//g, '\\\\')}`;\n });\n}\n\nexport function isPixelBbox(value: unknown): value is PixelBbox {\n return (\n Array.isArray(value) &&\n value.length === 4 &&\n value.every((item) => typeof item === 'number' && Number.isFinite(item))\n );\n}\n\ntype PlanningLocateParamWithMaybeLocatedPixelBbox = PlanningLocateParam & {\n locatedPixelBbox?: unknown;\n};\n\nexport function ifPlanLocateParamHasLocatedPixelBbox(\n planLocateParam: PlanningLocateParamWithMaybeLocatedPixelBbox,\n): planLocateParam is PlanningLocateParamWithLocatedPixelBbox {\n return isPixelBbox(planLocateParam.locatedPixelBbox);\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParamWithLocatedPixelBbox,\n): LocateResultElement | undefined {\n if (!planLocateParam) {\n return undefined;\n }\n\n const rect = pixelBboxToRect(planLocateParam.locatedPixelBbox);\n\n const element = generateElementByRect(\n rect,\n typeof planLocateParam.prompt === 'string'\n ? planLocateParam.prompt\n : planLocateParam.prompt?.prompt || '',\n );\n return element;\n}\n\nexport async function matchElementFromCache(\n context: {\n taskCache?: TaskCache;\n interfaceInstance: AbstractInterface;\n },\n cacheEntry: ElementCacheFeature | undefined,\n cachePrompt: TUserPrompt,\n cacheable: boolean | undefined,\n): Promise<LocateResultElement | undefined> {\n if (!cacheEntry) {\n return undefined;\n }\n\n if (cacheable === false) {\n cacheDebug('cache disabled for prompt: %s', cachePrompt);\n return undefined;\n }\n\n if (!context.taskCache?.isCacheResultUsed) {\n return undefined;\n }\n\n if (!context.interfaceInstance.rectMatchesCacheFeature) {\n cacheDebug(\n 'interface does not implement rectMatchesCacheFeature, skip cache',\n );\n return undefined;\n }\n\n try {\n const rect =\n await context.interfaceInstance.rectMatchesCacheFeature(cacheEntry);\n const element: LocateResultElement = {\n center: [\n Math.round(rect.left + rect.width / 2),\n Math.round(rect.top + rect.height / 2),\n ],\n rect,\n description:\n typeof cachePrompt === 'string'\n ? cachePrompt\n : cachePrompt.prompt || '',\n };\n\n cacheDebug('cache hit, prompt: %s', cachePrompt);\n return element;\n } catch (error) {\n cacheDebug('rectMatchesCacheFeature error: %s', error);\n return undefined;\n }\n}\n\ndeclare const __VERSION__: string | undefined;\n\nexport const getMidsceneVersion = (): string => {\n if (typeof __VERSION__ !== 'undefined') {\n return __VERSION__;\n } else if (\n process.env.__VERSION__ &&\n process.env.__VERSION__ !== 'undefined'\n ) {\n return process.env.__VERSION__;\n }\n throw new Error('__VERSION__ inject failed during build');\n};\n\nexport const parsePrompt = (\n prompt: TUserPrompt,\n): {\n textPrompt: string;\n multimodalPrompt?: TMultimodalPrompt;\n} => {\n if (typeof prompt === 'string') {\n return {\n textPrompt: prompt,\n multimodalPrompt: undefined,\n };\n }\n return {\n textPrompt: prompt.prompt,\n multimodalPrompt: prompt.images\n ? {\n images: prompt.images,\n convertHttpImage2Base64: !!prompt.convertHttpImage2Base64,\n }\n : undefined,\n };\n};\n\nexport const transformLogicalElementToScreenshot = (\n element: LocateResultElement,\n shrunkShotToLogicalRatio: number,\n): LocateResultElement => {\n if (shrunkShotToLogicalRatio === 1) {\n return element;\n }\n\n return {\n ...element,\n center: [\n Math.round(element.center[0] * shrunkShotToLogicalRatio),\n Math.round(element.center[1] * shrunkShotToLogicalRatio),\n ],\n rect: {\n ...element.rect,\n left: Math.round(element.rect.left * shrunkShotToLogicalRatio),\n top: Math.round(element.rect.top * shrunkShotToLogicalRatio),\n width: Math.round(element.rect.width * shrunkShotToLogicalRatio),\n height: Math.round(element.rect.height * shrunkShotToLogicalRatio),\n },\n };\n};\n\nexport const transformLogicalRectToScreenshotRect = (\n rect: Rect,\n shrunkShotToLogicalRatio: number,\n): Rect => {\n if (shrunkShotToLogicalRatio === 1) {\n return rect;\n }\n\n return {\n ...rect,\n left: Math.round(rect.left * shrunkShotToLogicalRatio),\n top: Math.round(rect.top * shrunkShotToLogicalRatio),\n width: Math.round(rect.width * shrunkShotToLogicalRatio),\n height: Math.round(rect.height * shrunkShotToLogicalRatio),\n };\n};\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","agentDebug","getDebug","screenshotDataUrlPattern","inferBase64ImageFormat","base64Body","normalizeScreenshotBase64","screenshotBase64","trimmedBase64","assert","createImgBase64ByFormat","legacyScrollTypeMap","normalizeScrollType","scrollType","commonContextParser","interfaceInstance","_opt","debug","description","uploadTestInfoToServer","interfaceSize","logicalWidth","logicalHeight","console","Number","Error","screenshotCapturedAt","Date","imgWidth","imgHeight","imageInfoOfBase64","logicalIsPortrait","screenshotIsPortrait","finalLogicalWidth","userShrinkFactor","dpr","shrunkShotToLogicalRatio","targetWidth","Math","targetHeight","resizedBase64","resizeImgBase64","ScreenshotItem","outputScreenshotBase64","mimeType","body","parseBase64","jpegBuffer","convertImgBufferToJpeg","Buffer","createScreenshotBoundUIContext","opt","normalizedScreenshotBase64","actualScreenshotSize","getReportFileName","tag","reportTagName","globalConfigManager","MIDSCENE_REPORT_TAG_NAME","dateTimeInFileName","dayjs","uniqueId","uuid","printReportMsg","filepath","MIDSCENE_REPORT_QUIET","logMsg","normalizeFilePaths","files","options","fileExists","existsSync","isInBrowser","ifInBrowser","resolvePath","resolve","wslDistroName","process","cwd","file","absolutePath","wslMount","isPixelBbox","value","Array","item","ifPlanLocateParamHasLocatedPixelBbox","planLocateParam","matchElementFromPlan","rect","pixelBboxToRect","element","generateElementByRect","matchElementFromCache","context","cacheEntry","cachePrompt","cacheable","cacheDebug","error","getMidsceneVersion","__VERSION__","parsePrompt","prompt","undefined","transformLogicalElementToScreenshot","transformLogicalRectToScreenshotRect"],"mappings":"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"}
1
+ {"version":3,"file":"agent/utils.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/agent/utils.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pixelBboxToRect } from '@/ai-model/workflows/inspect/locate-result-rect';\nimport type { TMultimodalPrompt, TUserPrompt } from '@/common';\nimport type { AbstractInterface } from '@/device';\nimport { ScreenshotItem } from '@/screenshot-item';\nimport type {\n ElementCacheFeature,\n LocateResultElement,\n PixelBbox,\n PlanningLocateParam,\n PlanningLocateParamWithLocatedPixelBbox,\n Rect,\n ScrollParam,\n Size,\n UIContext,\n} from '@/types';\nimport { uploadTestInfoToServer } from '@/utils';\nimport {\n MIDSCENE_REPORT_QUIET,\n MIDSCENE_REPORT_TAG_NAME,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { generateElementByRect } from '@midscene/shared/extractor';\nimport {\n convertImgBufferToJpeg,\n createImgBase64ByFormat,\n imageInfoOfBase64,\n normalizeBase64Image,\n parseBase64,\n resizeImgBase64,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { _keyDefinitions } from '@midscene/shared/us-keyboard-layout';\nimport { assert, ifInBrowser, logMsg, uuid } from '@midscene/shared/utils';\nimport dayjs from 'dayjs';\nimport type { TaskCache } from './task-cache';\nimport { debug as cacheDebug } from './task-cache';\n\nconst agentDebug = getDebug('agent');\n\nconst legacyScrollTypeMap = {\n once: 'singleAction',\n untilBottom: 'scrollToBottom',\n untilTop: 'scrollToTop',\n untilRight: 'scrollToRight',\n untilLeft: 'scrollToLeft',\n} as const;\n\nexport const normalizeScrollType = (\n scrollType: string | undefined,\n): ScrollParam['scrollType'] | undefined => {\n if (!scrollType) {\n return undefined;\n }\n\n if (scrollType in legacyScrollTypeMap) {\n return legacyScrollTypeMap[scrollType as keyof typeof legacyScrollTypeMap];\n }\n\n return scrollType as ScrollParam['scrollType'];\n};\n\nexport async function commonContextParser(\n interfaceInstance: AbstractInterface,\n _opt: {\n uploadServerUrl?: string;\n screenshotShrinkFactor?: number;\n },\n): Promise<UIContext> {\n const debug = getDebug('commonContextParser');\n\n assert(interfaceInstance, 'interfaceInstance is required');\n\n debug('Getting interface description');\n const description = interfaceInstance.describe?.() || '';\n debug('Interface description end');\n\n debug('Uploading test info to server');\n uploadTestInfoToServer({\n testUrl: description,\n serverUrl: _opt.uploadServerUrl,\n });\n debug('UploadTestInfoToServer end');\n\n debug('will get size');\n const interfaceSize = await interfaceInstance.size();\n const { width: logicalWidth, height: logicalHeight } = interfaceSize;\n\n if ((interfaceSize as unknown as { dpr: number }).dpr) {\n console.warn(\n 'Warning: return value of interface.size() include a dpr property, which is not expected and ignored. ',\n );\n }\n\n if (!Number.isFinite(logicalWidth) || !Number.isFinite(logicalHeight)) {\n throw new Error(\n `Invalid interface size: width and height must be finite numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n if (logicalWidth <= 0 || logicalHeight <= 0) {\n throw new Error(\n `Invalid interface size: width and height must be positive numbers. Received width: ${logicalWidth}, height: ${logicalHeight}`,\n );\n }\n\n debug(`size: ${logicalWidth}x${logicalHeight}`);\n\n const screenshotBase64 = await interfaceInstance.screenshotBase64();\n const screenshotCapturedAt = Date.now();\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n // Get physical screenshot dimensions\n debug('will get screenshot dimensions');\n const { width: imgWidth, height: imgHeight } =\n await imageInfoOfBase64(screenshotBase64);\n\n if (!Number.isFinite(imgWidth) || !Number.isFinite(imgHeight)) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be finite numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n if (imgWidth <= 0 || imgHeight <= 0) {\n throw new Error(\n `Invalid screenshot dimensions: width and height must be positive numbers. Received width: ${imgWidth}, height: ${imgHeight}`,\n );\n }\n debug('screenshot dimensions', imgWidth, 'x', imgHeight);\n\n // Detect orientation mismatch between logical size and screenshot.\n // Some devices (e.g. OPPO) report wrong orientation via ADB, causing\n // size() to return portrait dimensions even when the device is landscape.\n // We detect this by comparing aspect ratios and swap if they disagree.\n const logicalIsPortrait = logicalWidth < logicalHeight;\n const screenshotIsPortrait = imgWidth < imgHeight;\n let finalLogicalWidth = logicalWidth;\n let finalLogicalHeight = logicalHeight;\n if (logicalIsPortrait !== screenshotIsPortrait) {\n debug(\n `Orientation mismatch detected: logical size ${logicalWidth}x${logicalHeight} (${logicalIsPortrait ? 'portrait' : 'landscape'}) vs screenshot ${imgWidth}x${imgHeight} (${screenshotIsPortrait ? 'portrait' : 'landscape'}). Swapping logical dimensions.`,\n );\n finalLogicalWidth = logicalHeight;\n finalLogicalHeight = logicalWidth;\n }\n\n const userShrinkFactor = _opt.screenshotShrinkFactor ?? 1;\n\n if (!Number.isFinite(userShrinkFactor) || userShrinkFactor < 1) {\n throw new Error(\n `Invalid screenshotShrinkFactor: must be a finite number >= 1. Received: ${userShrinkFactor}`,\n );\n }\n\n const dpr = imgWidth / finalLogicalWidth;\n\n debug('calculated dpr:', dpr);\n\n const shrunkShotToLogicalRatio = dpr / userShrinkFactor;\n\n debug('shrunkShotToLogicalRatio', shrunkShotToLogicalRatio);\n\n if (userShrinkFactor !== 1) {\n const targetWidth = Math.round(imgWidth / userShrinkFactor);\n const targetHeight = Math.round(imgHeight / userShrinkFactor);\n\n debug(\n `Applying screenshot shrink factor: ${userShrinkFactor} (physical: ${imgWidth}x${imgHeight} -> target: ${targetWidth}x${targetHeight})`,\n );\n\n const resizedBase64 = await resizeImgBase64(screenshotBase64, {\n width: targetWidth,\n height: targetHeight,\n });\n return {\n shotSize: {\n width: targetWidth,\n height: targetHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(resizedBase64, screenshotCapturedAt),\n shrunkShotToLogicalRatio,\n };\n } else {\n // For screenshots that do not need shrinking, convert PNG to JPEG to reduce the image payload in model requests and reports. (Shrunk images are already JPEG.)\n // This mainly covers Android's default screenshot path, which produces PNG screenshots.\n // Compared with conversion on Android, centralizing it here means each platform does not need to handle screenshot formats itself, and allows future output formats such as WebP.\n // Built-in paths that already output JPEG are unaffected, and custom devices that output JPEG will not be compressed again.\n // Web platforms already output compressed formats, so they do not enter this branch. Other built-in device platforms run in Node, where Sharp conversion is fast enough that its extra cost is negligible.\n let outputScreenshotBase64 = screenshotBase64;\n const { mimeType, body } = parseBase64(screenshotBase64);\n if (mimeType.toLowerCase() === 'image/png') {\n const jpegBuffer = await convertImgBufferToJpeg(\n Buffer.from(body, 'base64'),\n 90,\n );\n outputScreenshotBase64 = createImgBase64ByFormat(\n 'jpeg',\n jpegBuffer.toString('base64'),\n );\n }\n\n return {\n shotSize: {\n width: imgWidth,\n height: imgHeight,\n },\n deprecatedDpr: dpr,\n screenshot: ScreenshotItem.create(\n outputScreenshotBase64,\n screenshotCapturedAt,\n ),\n shrunkShotToLogicalRatio,\n };\n }\n}\n\nexport async function createScreenshotBoundUIContext(\n screenshotBase64: string,\n opt: {\n screenshotSize?: Size;\n },\n): Promise<UIContext> {\n const normalizedScreenshotBase64 = normalizeBase64Image(screenshotBase64);\n const actualScreenshotSize = await imageInfoOfBase64(\n normalizedScreenshotBase64,\n );\n if (\n opt.screenshotSize &&\n (opt.screenshotSize.width !== actualScreenshotSize.width ||\n opt.screenshotSize.height !== actualScreenshotSize.height)\n ) {\n agentDebug(\n 'describeElementAtPoint screenshotSize mismatch, use actual size',\n {\n provided: opt.screenshotSize,\n actual: actualScreenshotSize,\n },\n );\n }\n\n return {\n screenshot: ScreenshotItem.create(normalizedScreenshotBase64, Date.now()),\n shotSize: actualScreenshotSize,\n shrunkShotToLogicalRatio: 1,\n _isFrozen: true,\n };\n}\n\nexport function getReportFileName(tag = 'web') {\n const reportTagName = globalConfigManager.getEnvConfigValue(\n MIDSCENE_REPORT_TAG_NAME,\n );\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 if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET)) {\n return;\n }\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\ntype NormalizeFilePathsOptions = {\n fileExists?: (path: string) => boolean;\n isInBrowser?: boolean;\n resolvePath?: (path: string) => string;\n wslDistroName?: string;\n cwd?: string;\n};\n\nexport function normalizeFilePaths(\n files: string[],\n options: NormalizeFilePathsOptions = {},\n): string[] {\n const {\n fileExists = existsSync,\n isInBrowser = ifInBrowser,\n resolvePath = resolve,\n wslDistroName = process.env.WSL_DISTRO_NAME,\n cwd = process.cwd(),\n } = options;\n\n if (isInBrowser) {\n throw new Error('File chooser is not supported in browser environment');\n }\n\n return files.map((file) => {\n const absolutePath = resolvePath(file);\n if (!fileExists(absolutePath)) {\n throw new Error(\n `File not found: ${file}. Resolved to: ${absolutePath}. Current working directory: ${cwd}`,\n );\n }\n\n if (!wslDistroName) {\n return absolutePath;\n }\n\n const wslMount = absolutePath.match(/^\\/mnt\\/([a-z])\\//);\n if (wslMount) {\n return `${wslMount[1].toUpperCase()}:\\\\${absolutePath.slice(7).replace(/\\//g, '\\\\')}`;\n }\n\n return `\\\\\\\\wsl$\\\\${wslDistroName}${absolutePath.replace(/\\//g, '\\\\')}`;\n });\n}\n\nexport function isPixelBbox(value: unknown): value is PixelBbox {\n return (\n Array.isArray(value) &&\n value.length === 4 &&\n value.every((item) => typeof item === 'number' && Number.isFinite(item))\n );\n}\n\ntype PlanningLocateParamWithMaybeLocatedPixelBbox = PlanningLocateParam & {\n locatedPixelBbox?: unknown;\n};\n\nexport function ifPlanLocateParamHasLocatedPixelBbox(\n planLocateParam: PlanningLocateParamWithMaybeLocatedPixelBbox,\n): planLocateParam is PlanningLocateParamWithLocatedPixelBbox {\n return isPixelBbox(planLocateParam.locatedPixelBbox);\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParamWithLocatedPixelBbox,\n): LocateResultElement | undefined {\n if (!planLocateParam) {\n return undefined;\n }\n\n const rect = pixelBboxToRect(planLocateParam.locatedPixelBbox);\n\n const element = generateElementByRect(\n rect,\n typeof planLocateParam.prompt === 'string'\n ? planLocateParam.prompt\n : planLocateParam.prompt?.prompt || '',\n );\n return element;\n}\n\nexport async function matchElementFromCache(\n context: {\n taskCache?: TaskCache;\n interfaceInstance: AbstractInterface;\n },\n cacheEntry: ElementCacheFeature | undefined,\n cachePrompt: TUserPrompt,\n cacheable: boolean | undefined,\n): Promise<LocateResultElement | undefined> {\n if (!cacheEntry) {\n return undefined;\n }\n\n if (cacheable === false) {\n cacheDebug('cache disabled for prompt: %s', cachePrompt);\n return undefined;\n }\n\n if (!context.taskCache?.isCacheResultUsed) {\n return undefined;\n }\n\n if (!context.interfaceInstance.rectMatchesCacheFeature) {\n cacheDebug(\n 'interface does not implement rectMatchesCacheFeature, skip cache',\n );\n return undefined;\n }\n\n try {\n const rect =\n await context.interfaceInstance.rectMatchesCacheFeature(cacheEntry);\n const element: LocateResultElement = {\n center: [\n Math.round(rect.left + rect.width / 2),\n Math.round(rect.top + rect.height / 2),\n ],\n rect,\n description:\n typeof cachePrompt === 'string'\n ? cachePrompt\n : cachePrompt.prompt || '',\n };\n\n cacheDebug('cache hit, prompt: %s', cachePrompt);\n return element;\n } catch (error) {\n cacheDebug('rectMatchesCacheFeature error: %s', error);\n return undefined;\n }\n}\n\ndeclare const __VERSION__: string | undefined;\n\nexport const getMidsceneVersion = (): string => {\n if (typeof __VERSION__ !== 'undefined') {\n return __VERSION__;\n } else if (\n process.env.__VERSION__ &&\n process.env.__VERSION__ !== 'undefined'\n ) {\n return process.env.__VERSION__;\n }\n throw new Error('__VERSION__ inject failed during build');\n};\n\nexport const parsePrompt = (\n prompt: TUserPrompt,\n): {\n textPrompt: string;\n multimodalPrompt?: TMultimodalPrompt;\n} => {\n if (typeof prompt === 'string') {\n return {\n textPrompt: prompt,\n multimodalPrompt: undefined,\n };\n }\n return {\n textPrompt: prompt.prompt,\n multimodalPrompt: prompt.images\n ? {\n images: prompt.images,\n convertHttpImage2Base64: !!prompt.convertHttpImage2Base64,\n }\n : undefined,\n };\n};\n\nexport const transformLogicalElementToScreenshot = (\n element: LocateResultElement,\n shrunkShotToLogicalRatio: number,\n): LocateResultElement => {\n if (shrunkShotToLogicalRatio === 1) {\n return element;\n }\n\n return {\n ...element,\n center: [\n Math.round(element.center[0] * shrunkShotToLogicalRatio),\n Math.round(element.center[1] * shrunkShotToLogicalRatio),\n ],\n rect: {\n ...element.rect,\n left: Math.round(element.rect.left * shrunkShotToLogicalRatio),\n top: Math.round(element.rect.top * shrunkShotToLogicalRatio),\n width: Math.round(element.rect.width * shrunkShotToLogicalRatio),\n height: Math.round(element.rect.height * shrunkShotToLogicalRatio),\n },\n };\n};\n\nexport const transformLogicalRectToScreenshotRect = (\n rect: Rect,\n shrunkShotToLogicalRatio: number,\n): Rect => {\n if (shrunkShotToLogicalRatio === 1) {\n return rect;\n }\n\n return {\n ...rect,\n left: Math.round(rect.left * shrunkShotToLogicalRatio),\n top: Math.round(rect.top * shrunkShotToLogicalRatio),\n width: Math.round(rect.width * shrunkShotToLogicalRatio),\n height: Math.round(rect.height * shrunkShotToLogicalRatio),\n };\n};\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","agentDebug","getDebug","legacyScrollTypeMap","normalizeScrollType","scrollType","commonContextParser","interfaceInstance","_opt","debug","assert","description","uploadTestInfoToServer","interfaceSize","logicalWidth","logicalHeight","console","Number","Error","screenshotBase64","screenshotCapturedAt","Date","imgWidth","imgHeight","imageInfoOfBase64","logicalIsPortrait","screenshotIsPortrait","finalLogicalWidth","userShrinkFactor","dpr","shrunkShotToLogicalRatio","targetWidth","Math","targetHeight","resizedBase64","resizeImgBase64","ScreenshotItem","outputScreenshotBase64","mimeType","body","parseBase64","jpegBuffer","convertImgBufferToJpeg","Buffer","createImgBase64ByFormat","createScreenshotBoundUIContext","opt","normalizedScreenshotBase64","normalizeBase64Image","actualScreenshotSize","getReportFileName","tag","reportTagName","globalConfigManager","MIDSCENE_REPORT_TAG_NAME","dateTimeInFileName","dayjs","uniqueId","uuid","printReportMsg","filepath","MIDSCENE_REPORT_QUIET","logMsg","normalizeFilePaths","files","options","fileExists","existsSync","isInBrowser","ifInBrowser","resolvePath","resolve","wslDistroName","process","cwd","file","absolutePath","wslMount","isPixelBbox","value","Array","item","ifPlanLocateParamHasLocatedPixelBbox","planLocateParam","matchElementFromPlan","rect","pixelBboxToRect","element","generateElementByRect","matchElementFromCache","context","cacheEntry","cachePrompt","cacheable","cacheDebug","error","getMidsceneVersion","__VERSION__","parsePrompt","prompt","undefined","transformLogicalElementToScreenshot","transformLogicalRectToScreenshotRect"],"mappings":"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"}
@@ -30,6 +30,7 @@ __webpack_require__.d(__webpack_exports__, {
30
30
  });
31
31
  const external_node_fs_namespaceObject = require("node:fs");
32
32
  const external_node_path_namespaceObject = require("node:path");
33
+ const img_namespaceObject = require("@midscene/shared/img");
33
34
  const external_screenshot_item_js_namespaceObject = require("../screenshot-item.js");
34
35
  const external_screenshot_restoration_js_namespaceObject = require("./screenshot-restoration.js");
35
36
  const external_screenshot_store_js_namespaceObject = require("./screenshot-store.js");
@@ -167,11 +168,13 @@ class ReportActionDump {
167
168
  const dumpString = (0, external_node_fs_namespaceObject.readFileSync)(basePath, 'utf-8');
168
169
  const screenshotsDir = `${basePath}.screenshots`;
169
170
  const loadFromExecutionScreenshotDir = (id, mimeType)=>{
170
- const ext = 'image/jpeg' === mimeType ? 'jpeg' : 'png';
171
+ const format = (0, img_namespaceObject.screenshotImageFormatFromMimeType)(mimeType);
172
+ if (!format) throw new Error(`Unsupported screenshot mime type: ${mimeType}`);
173
+ const ext = (0, img_namespaceObject.screenshotImageExtension)(format);
171
174
  const filePath = (0, external_node_path_namespaceObject.join)(screenshotsDir, `${id}.${ext}`);
172
175
  if (!(0, external_node_fs_namespaceObject.existsSync)(filePath)) return '';
173
176
  const data = (0, external_node_fs_namespaceObject.readFileSync)(filePath);
174
- return `data:image/${ext};base64,${data.toString('base64')}`;
177
+ return `data:${mimeType};base64,${data.toString('base64')}`;
175
178
  };
176
179
  const dumpData = JSON.parse(dumpString);
177
180
  const store = new external_screenshot_store_js_namespaceObject.ScreenshotStore({
@@ -1 +1 @@
1
- {"version":3,"file":"dump/report-action-dump.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/report-action-dump.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport { ScreenshotItem } from '../screenshot-item';\nimport type {\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n} from '../types';\nimport { restoreImageReferences } from './screenshot-restoration';\nimport { ScreenshotStore } from './screenshot-store';\n\n/**\n * Replacer function for JSON serialization that handles Page, Browser objects and ScreenshotItem\n */\nfunction replacerForDumpSerialization(_key: string, value: any): any {\n // screenshotSequence is a transient model input (multi-frame capture). Its\n // frames are not persisted by collectScreenshots, so serializing them would\n // emit dangling screenshot refs. The representative `screenshot` is kept.\n if (_key === 'screenshotSequence') {\n return undefined;\n }\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 // Handle ScreenshotItem serialization\n if (value && typeof value.toSerializable === 'function') {\n return value.toSerializable();\n }\n return value;\n}\n\n/**\n * Reviver function for JSON deserialization that keeps screenshot references\n * as plain objects. Resolution is handled lazily by restoreImageReferences.\n *\n * @param key - JSON key being processed\n * @param value - JSON value being processed\n * @returns Restored value\n */\nfunction reviverForDumpDeserialization(key: string, value: any): any {\n // Only process screenshot fields\n if (key !== 'screenshot' || typeof value !== 'object' || value === null) {\n return value;\n }\n\n if (ScreenshotItem.isSerialized(value)) {\n return value;\n }\n\n return value;\n}\n\n/**\n * ExecutionDump class for serializing and deserializing execution dumps\n */\nexport class ExecutionDump implements IExecutionDump {\n id?: string;\n logTime: number;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n\n constructor(data: IExecutionDump) {\n this.id = data.id;\n this.logTime = data.logTime;\n this.name = data.name;\n this.description = data.description;\n this.tasks = data.tasks;\n this.aiActContext = data.aiActContext;\n }\n\n /**\n * Serialize the ExecutionDump to a JSON string\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IExecutionDump {\n return {\n id: this.id,\n logTime: this.logTime,\n name: this.name,\n description: this.description,\n tasks: this.tasks.map((task) => ({\n ...task,\n recorder: task.recorder || [],\n })),\n aiActContext: this.aiActContext,\n };\n }\n\n /**\n * Create an ExecutionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ExecutionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IExecutionDump;\n return new ExecutionDump(parsed);\n }\n\n /**\n * Create an ExecutionDump instance from a plain object\n */\n static fromJSON(data: IExecutionDump): ExecutionDump {\n return new ExecutionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from tasks.\n * Scans through uiContext and recorder items to find screenshots.\n *\n * @returns Array of ScreenshotItem instances\n */\n collectScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n\n for (const task of this.tasks) {\n // Collect uiContext.screenshot if present\n if (task.uiContext?.screenshot instanceof ScreenshotItem) {\n screenshots.push(task.uiContext.screenshot);\n }\n\n // Collect recorder screenshots\n if (task.recorder) {\n for (const record of task.recorder) {\n if (record.screenshot instanceof ScreenshotItem) {\n screenshots.push(record.screenshot);\n }\n }\n }\n }\n\n return screenshots;\n }\n}\n\n/**\n * ReportActionDump class for serializing and deserializing report action dumps\n */\nexport class ReportActionDump implements IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: IReportActionDump['modelBriefs'];\n executions: ExecutionDump[];\n deviceType?: string;\n\n constructor(data: IReportActionDump) {\n this.sdkVersion = data.sdkVersion;\n this.groupName = data.groupName;\n this.groupDescription = data.groupDescription;\n this.modelBriefs = data.modelBriefs;\n this.executions = data.executions.map((exec) =>\n exec instanceof ExecutionDump ? exec : ExecutionDump.fromJSON(exec),\n );\n this.deviceType = data.deviceType;\n }\n\n /**\n * Serialize the ReportActionDump to a JSON string\n * Uses compact { $screenshot: id } format\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Serialize the ReportActionDump with inline screenshots to a JSON string.\n * Each ScreenshotItem is replaced with { base64: \"...\", capturedAt }.\n */\n serializeWithInlineScreenshots(indents?: number): string {\n const processValue = (obj: unknown): unknown => {\n if (obj instanceof ScreenshotItem) {\n return { base64: obj.base64, capturedAt: obj.capturedAt };\n }\n if (Array.isArray(obj)) {\n return obj.map(processValue);\n }\n if (obj && typeof obj === 'object') {\n const entries = Object.entries(obj)\n // screenshotSequence is a transient multi-frame model input whose\n // frames are not persisted; skip it to avoid inlining large base64.\n .filter(([key]) => key !== 'screenshotSequence')\n .map(([key, value]) => [key, processValue(value)]);\n return Object.fromEntries(entries);\n }\n return obj;\n };\n\n const data = processValue(this.toJSON());\n return JSON.stringify(data, null, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IReportActionDump {\n return {\n sdkVersion: this.sdkVersion,\n groupName: this.groupName,\n groupDescription: this.groupDescription,\n modelBriefs: this.modelBriefs,\n executions: this.executions.map((exec) => exec.toJSON()),\n deviceType: this.deviceType,\n };\n }\n\n /**\n * Create a ReportActionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ReportActionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IReportActionDump;\n return new ReportActionDump(parsed);\n }\n\n /**\n * Create a ReportActionDump instance from a plain object\n */\n static fromJSON(data: IReportActionDump): ReportActionDump {\n return new ReportActionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from all executions.\n *\n * @returns Array of all ScreenshotItem instances across all executions\n */\n collectAllScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n for (const execution of this.executions) {\n screenshots.push(...execution.collectScreenshots());\n }\n return screenshots;\n }\n\n /**\n * Serialize the dump to files with screenshots as separate PNG files.\n * Creates:\n * - {basePath} - dump JSON with { $screenshot: id } references\n * - {basePath}.screenshots/ - PNG files\n *\n * @param basePath - Base path for the dump file\n */\n serializeToFiles(basePath: string): void {\n const screenshotsDir = `${basePath}.screenshots`;\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const screenshots = this.collectAllScreenshots();\n\n for (const screenshot of screenshots) {\n const imagePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n if (existsSync(imagePath)) {\n continue;\n }\n\n const rawBase64 = screenshot.rawBase64;\n writeFileSync(imagePath, Buffer.from(rawBase64, 'base64'));\n }\n\n // Write dump JSON with references\n writeFileSync(basePath, this.serialize(), 'utf-8');\n }\n\n /**\n * Read dump from files and return JSON string with inline screenshots.\n * Reads the dump JSON and screenshot files, then inlines the base64 data.\n *\n * @param basePath - Base path for the dump file\n * @returns JSON string with inline screenshots ({ base64: \"...\" } format)\n */\n static fromFilesAsInlineJson(basePath: string): string {\n const dumpString = readFileSync(basePath, 'utf-8');\n const screenshotsDir = `${basePath}.screenshots`;\n\n const loadFromExecutionScreenshotDir = (id: string, mimeType: string) => {\n const ext = mimeType === 'image/jpeg' ? 'jpeg' : 'png';\n const filePath = join(screenshotsDir, `${id}.${ext}`);\n if (!existsSync(filePath)) {\n return '';\n }\n const data = readFileSync(filePath);\n return `data:image/${ext};base64,${data.toString('base64')}`;\n };\n\n // Restore image references\n const dumpData = JSON.parse(dumpString);\n const store = new ScreenshotStore({\n mode: 'directory',\n reportPath: basePath,\n });\n const processedData = restoreImageReferences(dumpData, (ref) => {\n const executionFileImage = loadFromExecutionScreenshotDir(\n ref.id,\n ref.mimeType,\n );\n if (executionFileImage) {\n return executionFileImage;\n }\n\n if (ref.storage === 'inline') {\n return '';\n }\n return store.loadBase64(ref);\n });\n return JSON.stringify(processedData);\n }\n\n /**\n * Clean up all files associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n */\n static cleanupFiles(basePath: string): void {\n const filesToClean = [basePath, `${basePath}.screenshots`];\n\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true, recursive: true });\n } catch {\n // Ignore errors - file may already be deleted\n }\n }\n }\n\n /**\n * Get all file paths associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n * @returns Array of all associated file paths\n */\n static getFilePaths(basePath: string): string[] {\n return [basePath, `${basePath}.screenshots`];\n }\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupedActionDump = ReportActionDump;\nexport const GroupedActionDump = ReportActionDump;\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","replacerForDumpSerialization","_key","value","reviverForDumpDeserialization","ScreenshotItem","ExecutionDump","indents","JSON","task","serialized","parsed","data","screenshots","record","ReportActionDump","processValue","Array","entries","exec","execution","basePath","screenshotsDir","existsSync","mkdirSync","screenshot","imagePath","join","rawBase64","writeFileSync","Buffer","dumpString","readFileSync","loadFromExecutionScreenshotDir","id","mimeType","ext","filePath","dumpData","store","ScreenshotStore","processedData","restoreImageReferences","ref","executionFileImage","filesToClean","rmSync","GroupedActionDump"],"mappings":"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"}
1
+ {"version":3,"file":"dump/report-action-dump.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/report-action-dump.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport {\n screenshotImageExtension,\n screenshotImageFormatFromMimeType,\n} from '@midscene/shared/img';\nimport { ScreenshotItem } from '../screenshot-item';\nimport type {\n ExecutionTask,\n IExecutionDump,\n IReportActionDump,\n} from '../types';\nimport { restoreImageReferences } from './screenshot-restoration';\nimport { type ScreenshotRef, ScreenshotStore } from './screenshot-store';\n\n/**\n * Replacer function for JSON serialization that handles Page, Browser objects and ScreenshotItem\n */\nfunction replacerForDumpSerialization(_key: string, value: any): any {\n // screenshotSequence is a transient model input (multi-frame capture). Its\n // frames are not persisted by collectScreenshots, so serializing them would\n // emit dangling screenshot refs. The representative `screenshot` is kept.\n if (_key === 'screenshotSequence') {\n return undefined;\n }\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 // Handle ScreenshotItem serialization\n if (value && typeof value.toSerializable === 'function') {\n return value.toSerializable();\n }\n return value;\n}\n\n/**\n * Reviver function for JSON deserialization that keeps screenshot references\n * as plain objects. Resolution is handled lazily by restoreImageReferences.\n *\n * @param key - JSON key being processed\n * @param value - JSON value being processed\n * @returns Restored value\n */\nfunction reviverForDumpDeserialization(key: string, value: any): any {\n // Only process screenshot fields\n if (key !== 'screenshot' || typeof value !== 'object' || value === null) {\n return value;\n }\n\n if (ScreenshotItem.isSerialized(value)) {\n return value;\n }\n\n return value;\n}\n\n/**\n * ExecutionDump class for serializing and deserializing execution dumps\n */\nexport class ExecutionDump implements IExecutionDump {\n id?: string;\n logTime: number;\n name: string;\n description?: string;\n tasks: ExecutionTask[];\n aiActContext?: string;\n\n constructor(data: IExecutionDump) {\n this.id = data.id;\n this.logTime = data.logTime;\n this.name = data.name;\n this.description = data.description;\n this.tasks = data.tasks;\n this.aiActContext = data.aiActContext;\n }\n\n /**\n * Serialize the ExecutionDump to a JSON string\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IExecutionDump {\n return {\n id: this.id,\n logTime: this.logTime,\n name: this.name,\n description: this.description,\n tasks: this.tasks.map((task) => ({\n ...task,\n recorder: task.recorder || [],\n })),\n aiActContext: this.aiActContext,\n };\n }\n\n /**\n * Create an ExecutionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ExecutionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IExecutionDump;\n return new ExecutionDump(parsed);\n }\n\n /**\n * Create an ExecutionDump instance from a plain object\n */\n static fromJSON(data: IExecutionDump): ExecutionDump {\n return new ExecutionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from tasks.\n * Scans through uiContext and recorder items to find screenshots.\n *\n * @returns Array of ScreenshotItem instances\n */\n collectScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n\n for (const task of this.tasks) {\n // Collect uiContext.screenshot if present\n if (task.uiContext?.screenshot instanceof ScreenshotItem) {\n screenshots.push(task.uiContext.screenshot);\n }\n\n // Collect recorder screenshots\n if (task.recorder) {\n for (const record of task.recorder) {\n if (record.screenshot instanceof ScreenshotItem) {\n screenshots.push(record.screenshot);\n }\n }\n }\n }\n\n return screenshots;\n }\n}\n\n/**\n * ReportActionDump class for serializing and deserializing report action dumps\n */\nexport class ReportActionDump implements IReportActionDump {\n sdkVersion: string;\n groupName: string;\n groupDescription?: string;\n modelBriefs: IReportActionDump['modelBriefs'];\n executions: ExecutionDump[];\n deviceType?: string;\n\n constructor(data: IReportActionDump) {\n this.sdkVersion = data.sdkVersion;\n this.groupName = data.groupName;\n this.groupDescription = data.groupDescription;\n this.modelBriefs = data.modelBriefs;\n this.executions = data.executions.map((exec) =>\n exec instanceof ExecutionDump ? exec : ExecutionDump.fromJSON(exec),\n );\n this.deviceType = data.deviceType;\n }\n\n /**\n * Serialize the ReportActionDump to a JSON string\n * Uses compact { $screenshot: id } format\n */\n serialize(indents?: number): string {\n return JSON.stringify(this.toJSON(), replacerForDumpSerialization, indents);\n }\n\n /**\n * Serialize the ReportActionDump with inline screenshots to a JSON string.\n * Each ScreenshotItem is replaced with { base64: \"...\", capturedAt }.\n */\n serializeWithInlineScreenshots(indents?: number): string {\n const processValue = (obj: unknown): unknown => {\n if (obj instanceof ScreenshotItem) {\n return { base64: obj.base64, capturedAt: obj.capturedAt };\n }\n if (Array.isArray(obj)) {\n return obj.map(processValue);\n }\n if (obj && typeof obj === 'object') {\n const entries = Object.entries(obj)\n // screenshotSequence is a transient multi-frame model input whose\n // frames are not persisted; skip it to avoid inlining large base64.\n .filter(([key]) => key !== 'screenshotSequence')\n .map(([key, value]) => [key, processValue(value)]);\n return Object.fromEntries(entries);\n }\n return obj;\n };\n\n const data = processValue(this.toJSON());\n return JSON.stringify(data, null, indents);\n }\n\n /**\n * Convert to a plain object for JSON serialization\n */\n toJSON(): IReportActionDump {\n return {\n sdkVersion: this.sdkVersion,\n groupName: this.groupName,\n groupDescription: this.groupDescription,\n modelBriefs: this.modelBriefs,\n executions: this.executions.map((exec) => exec.toJSON()),\n deviceType: this.deviceType,\n };\n }\n\n /**\n * Create a ReportActionDump instance from a serialized JSON string\n */\n static fromSerializedString(serialized: string): ReportActionDump {\n const parsed = JSON.parse(\n serialized,\n reviverForDumpDeserialization,\n ) as IReportActionDump;\n return new ReportActionDump(parsed);\n }\n\n /**\n * Create a ReportActionDump instance from a plain object\n */\n static fromJSON(data: IReportActionDump): ReportActionDump {\n return new ReportActionDump(data);\n }\n\n /**\n * Collect all ScreenshotItem instances from all executions.\n *\n * @returns Array of all ScreenshotItem instances across all executions\n */\n collectAllScreenshots(): ScreenshotItem[] {\n const screenshots: ScreenshotItem[] = [];\n for (const execution of this.executions) {\n screenshots.push(...execution.collectScreenshots());\n }\n return screenshots;\n }\n\n /**\n * Serialize the dump to files with screenshots as separate image files.\n * Creates:\n * - {basePath} - dump JSON with { $screenshot: id } references\n * - {basePath}.screenshots/ - screenshot image files\n *\n * @param basePath - Base path for the dump file\n */\n serializeToFiles(basePath: string): void {\n const screenshotsDir = `${basePath}.screenshots`;\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const screenshots = this.collectAllScreenshots();\n\n for (const screenshot of screenshots) {\n const imagePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n if (existsSync(imagePath)) {\n continue;\n }\n\n const rawBase64 = screenshot.rawBase64;\n writeFileSync(imagePath, Buffer.from(rawBase64, 'base64'));\n }\n\n // Write dump JSON with references\n writeFileSync(basePath, this.serialize(), 'utf-8');\n }\n\n /**\n * Read dump from files and return JSON string with inline screenshots.\n * Reads the dump JSON and screenshot files, then inlines the base64 data.\n *\n * @param basePath - Base path for the dump file\n * @returns JSON string with inline screenshots ({ base64: \"...\" } format)\n */\n static fromFilesAsInlineJson(basePath: string): string {\n const dumpString = readFileSync(basePath, 'utf-8');\n const screenshotsDir = `${basePath}.screenshots`;\n\n const loadFromExecutionScreenshotDir = (\n id: string,\n mimeType: ScreenshotRef['mimeType'],\n ) => {\n const format = screenshotImageFormatFromMimeType(mimeType);\n if (!format) {\n throw new Error(`Unsupported screenshot mime type: ${mimeType}`);\n }\n const ext = screenshotImageExtension(format);\n const filePath = join(screenshotsDir, `${id}.${ext}`);\n if (!existsSync(filePath)) {\n return '';\n }\n const data = readFileSync(filePath);\n return `data:${mimeType};base64,${data.toString('base64')}`;\n };\n\n // Restore image references\n const dumpData = JSON.parse(dumpString);\n const store = new ScreenshotStore({\n mode: 'directory',\n reportPath: basePath,\n });\n const processedData = restoreImageReferences(dumpData, (ref) => {\n const executionFileImage = loadFromExecutionScreenshotDir(\n ref.id,\n ref.mimeType,\n );\n if (executionFileImage) {\n return executionFileImage;\n }\n\n if (ref.storage === 'inline') {\n return '';\n }\n return store.loadBase64(ref);\n });\n return JSON.stringify(processedData);\n }\n\n /**\n * Clean up all files associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n */\n static cleanupFiles(basePath: string): void {\n const filesToClean = [basePath, `${basePath}.screenshots`];\n\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true, recursive: true });\n } catch {\n // Ignore errors - file may already be deleted\n }\n }\n }\n\n /**\n * Get all file paths associated with a serialized dump.\n *\n * @param basePath - Base path for the dump file\n * @returns Array of all associated file paths\n */\n static getFilePaths(basePath: string): string[] {\n return [basePath, `${basePath}.screenshots`];\n }\n}\n\n// Backward-compatible aliases for existing external consumers.\nexport type GroupedActionDump = ReportActionDump;\nexport const GroupedActionDump = ReportActionDump;\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","replacerForDumpSerialization","_key","value","reviverForDumpDeserialization","ScreenshotItem","ExecutionDump","indents","JSON","task","serialized","parsed","data","screenshots","record","ReportActionDump","processValue","Array","entries","exec","execution","basePath","screenshotsDir","existsSync","mkdirSync","screenshot","imagePath","join","rawBase64","writeFileSync","Buffer","dumpString","readFileSync","loadFromExecutionScreenshotDir","id","mimeType","format","screenshotImageFormatFromMimeType","Error","ext","screenshotImageExtension","filePath","dumpData","store","ScreenshotStore","processedData","restoreImageReferences","ref","executionFileImage","filesToClean","rmSync","GroupedActionDump"],"mappings":"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"}
@@ -31,6 +31,7 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  const external_node_fs_namespaceObject = require("node:fs");
32
32
  const promises_namespaceObject = require("node:fs/promises");
33
33
  const external_node_path_namespaceObject = require("node:path");
34
+ const img_namespaceObject = require("@midscene/shared/img");
34
35
  const external_html_utils_js_namespaceObject = require("./html-utils.js");
35
36
  function _define_property(obj, key, value) {
36
37
  if (key in obj) Object.defineProperty(obj, key, {
@@ -45,14 +46,16 @@ function _define_property(obj, key, value) {
45
46
  function normalizeScreenshotRef(value) {
46
47
  if ('object' != typeof value || null === value) return null;
47
48
  const record = value;
48
- if ('midscene_screenshot_ref' === record.type && 'string' == typeof record.id && 'number' == typeof record.capturedAt && ('inline' === record.storage || 'file' === record.storage) && ('image/png' === record.mimeType || 'image/jpeg' === record.mimeType)) {
49
+ if ('midscene_screenshot_ref' === record.type && 'string' == typeof record.id && 'number' == typeof record.capturedAt && ('inline' === record.storage || 'file' === record.storage) && (0, img_namespaceObject.isScreenshotImageMimeType)(record.mimeType)) {
49
50
  if ('file' === record.storage && 'string' != typeof record.path) return null;
50
51
  return record;
51
52
  }
52
53
  return null;
53
54
  }
54
55
  function extensionByMimeType(mimeType) {
55
- return 'image/jpeg' === mimeType ? 'jpeg' : 'png';
56
+ const format = (0, img_namespaceObject.screenshotImageFormatFromMimeType)(mimeType);
57
+ if (!format) throw new Error(`Unsupported screenshot mime type: ${mimeType}`);
58
+ return (0, img_namespaceObject.screenshotImageExtension)(format);
56
59
  }
57
60
  function resolveScreenshotSource(refInput, options) {
58
61
  const ref = normalizeScreenshotRef(refInput);
@@ -1 +1 @@
1
- {"version":3,"file":"dump/screenshot-store.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/screenshot-store.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';\nimport { writeFile as writeFileAsync } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport type { ScreenshotItem } from '../screenshot-item';\nimport { extractImageByIdSync } from './html-utils';\n\nexport interface ScreenshotRef {\n type: 'midscene_screenshot_ref';\n id: string;\n capturedAt: number;\n mimeType: 'image/png' | 'image/jpeg';\n storage: 'inline' | 'file';\n path?: string;\n}\n\nexport function normalizeScreenshotRef(value: unknown): ScreenshotRef | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n\n if (\n record.type === 'midscene_screenshot_ref' &&\n typeof record.id === 'string' &&\n typeof record.capturedAt === 'number' &&\n (record.storage === 'inline' || record.storage === 'file') &&\n (record.mimeType === 'image/png' || record.mimeType === 'image/jpeg')\n ) {\n if (record.storage === 'file' && typeof record.path !== 'string') {\n return null;\n }\n return record as unknown as ScreenshotRef;\n }\n\n return null;\n}\n\ntype ResolvedScreenshotSource =\n | {\n type: 'data-uri';\n id: string;\n mimeType: ScreenshotRef['mimeType'];\n dataUri: string;\n }\n | {\n type: 'file';\n id: string;\n mimeType: ScreenshotRef['mimeType'];\n filePath: string;\n };\n\nfunction extensionByMimeType(mimeType: ScreenshotRef['mimeType']): string {\n return mimeType === 'image/jpeg' ? 'jpeg' : 'png';\n}\n\nexport function resolveScreenshotSource(\n refInput: unknown,\n options: {\n reportPath: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedScreenshotSource {\n const ref = normalizeScreenshotRef(refInput);\n const id = ref?.id ?? options.fallbackId;\n const mimeType = ref?.mimeType ?? options.fallbackMimeType;\n\n if (!id || !mimeType) {\n throw new Error(\n 'ScreenshotStore: screenshot id and mimeType are required to resolve screenshot',\n );\n }\n\n const resolveReportRelativePath = (filePath: string): string =>\n isAbsolute(filePath)\n ? filePath\n : join(dirname(options.reportPath), filePath);\n\n if (ref?.storage === 'file') {\n if (!ref.path) {\n throw new Error(\n `ScreenshotStore: screenshot ref \"${ref.id}\" missing file path`,\n );\n }\n\n const explicitFilePath = resolveReportRelativePath(ref.path);\n if (existsSync(explicitFilePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: explicitFilePath,\n };\n }\n }\n\n const inlineDataUri = extractImageByIdSync(options.reportPath, id);\n if (inlineDataUri) {\n return {\n type: 'data-uri',\n id,\n mimeType,\n dataUri: inlineDataUri,\n };\n }\n\n const siblingScreenshotPath = join(\n dirname(options.reportPath),\n 'screenshots',\n `${id}.${extensionByMimeType(mimeType)}`,\n );\n if (existsSync(siblingScreenshotPath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: siblingScreenshotPath,\n };\n }\n\n throw new Error(\n `ScreenshotStore: cannot resolve screenshot \"${id}\" from ${options.reportPath}`,\n );\n}\n\nexport class ScreenshotStore {\n private readonly mode: 'inline' | 'directory';\n private readonly reportPath: string;\n private readonly screenshotsDir?: string;\n private readonly writeInlineImage?: (\n id: string,\n base64: string,\n ) => void | Promise<void>;\n private readonly alsoWriteFileCopy: boolean;\n private readonly writtenInlineIds = new Set<string>();\n private readonly writtenFileIds = new Set<string>();\n\n constructor(options: {\n mode: 'inline' | 'directory';\n reportPath: string;\n screenshotsDir?: string;\n writeInlineImage?: (id: string, base64: string) => void | Promise<void>;\n alsoWriteFileCopy?: boolean;\n /** @deprecated Use alsoWriteFileCopy instead. */\n ensureFileCopy?: boolean;\n }) {\n this.mode = options.mode;\n this.reportPath = options.reportPath;\n this.screenshotsDir = options.screenshotsDir;\n this.writeInlineImage = options.writeInlineImage;\n this.alsoWriteFileCopy =\n options.alsoWriteFileCopy ?? options.ensureFileCopy ?? false;\n }\n\n async persist(screenshot: ScreenshotItem): Promise<ScreenshotRef> {\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileRef = shouldWriteFileCopy\n ? await this.persistToSharedFileIfNeeded(screenshot, {\n markAsPersisted: this.mode === 'directory',\n })\n : null;\n\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ScreenshotStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(screenshot.id)) {\n await this.writeInlineImage(screenshot.id, screenshot.base64);\n this.writtenInlineIds.add(screenshot.id);\n }\n return screenshot.markPersistedInline(this.reportPath);\n }\n\n if (!fileRef) {\n throw new Error(\n 'ScreenshotStore: file persistence is required in directory mode',\n );\n }\n return fileRef;\n }\n\n private async persistToSharedFileIfNeeded(\n screenshot: ScreenshotItem,\n options: {\n markAsPersisted: boolean;\n },\n ): Promise<ScreenshotRef> {\n const screenshotsDir = this.screenshotsDir;\n if (!screenshotsDir) {\n throw new Error(\n 'ScreenshotStore: screenshotsDir is required when file persistence is enabled',\n );\n }\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const relativePath = `./screenshots/${screenshot.id}.${screenshot.extension}`;\n const absolutePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n\n if (!this.writtenFileIds.has(screenshot.id)) {\n const buffer = Buffer.from(screenshot.rawBase64, 'base64');\n await writeFileAsync(absolutePath, buffer);\n this.writtenFileIds.add(screenshot.id);\n }\n\n if (options.markAsPersisted) {\n return screenshot.markPersistedToPath(relativePath, absolutePath);\n }\n\n return screenshot.registerPersistedFileCopy(relativePath, absolutePath);\n }\n\n loadBase64(refInput: unknown): string {\n const ref = normalizeScreenshotRef(refInput);\n if (!ref) {\n throw new Error('ScreenshotStore: invalid screenshot reference');\n }\n\n const resolved = resolveScreenshotSource(ref, {\n reportPath: this.reportPath,\n });\n\n if (resolved.type === 'data-uri') {\n return resolved.dataUri;\n }\n\n const data = readFileSync(resolved.filePath);\n return `data:${resolved.mimeType};base64,${data.toString('base64')}`;\n }\n\n cleanup(): void {\n if (\n this.mode === 'directory' &&\n this.screenshotsDir &&\n existsSync(this.screenshotsDir)\n ) {\n rmSync(this.screenshotsDir, { recursive: true, force: true });\n }\n this.writtenInlineIds.clear();\n this.writtenFileIds.clear();\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","normalizeScreenshotRef","value","record","extensionByMimeType","mimeType","resolveScreenshotSource","refInput","options","ref","id","Error","resolveReportRelativePath","filePath","isAbsolute","join","dirname","explicitFilePath","existsSync","inlineDataUri","extractImageByIdSync","siblingScreenshotPath","ScreenshotStore","screenshot","shouldWriteFileCopy","fileRef","screenshotsDir","mkdirSync","relativePath","absolutePath","buffer","Buffer","writeFileAsync","resolved","data","readFileSync","rmSync","Set"],"mappings":"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"}
1
+ {"version":3,"file":"dump/screenshot-store.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/screenshot-store.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';\nimport { writeFile as writeFileAsync } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport {\n type ScreenshotImageMimeType,\n isScreenshotImageMimeType,\n screenshotImageExtension,\n screenshotImageFormatFromMimeType,\n} from '@midscene/shared/img';\nimport type { ScreenshotItem } from '../screenshot-item';\nimport { extractImageByIdSync } from './html-utils';\n\nexport interface ScreenshotRef {\n type: 'midscene_screenshot_ref';\n id: string;\n capturedAt: number;\n mimeType: ScreenshotImageMimeType;\n storage: 'inline' | 'file';\n path?: string;\n}\n\nexport function normalizeScreenshotRef(value: unknown): ScreenshotRef | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n\n if (\n record.type === 'midscene_screenshot_ref' &&\n typeof record.id === 'string' &&\n typeof record.capturedAt === 'number' &&\n (record.storage === 'inline' || record.storage === 'file') &&\n isScreenshotImageMimeType(record.mimeType)\n ) {\n if (record.storage === 'file' && typeof record.path !== 'string') {\n return null;\n }\n return record as unknown as ScreenshotRef;\n }\n\n return null;\n}\n\ntype ResolvedScreenshotSource =\n | {\n type: 'data-uri';\n id: string;\n mimeType: ScreenshotRef['mimeType'];\n dataUri: string;\n }\n | {\n type: 'file';\n id: string;\n mimeType: ScreenshotRef['mimeType'];\n filePath: string;\n };\n\nfunction extensionByMimeType(mimeType: ScreenshotRef['mimeType']): string {\n const format = screenshotImageFormatFromMimeType(mimeType);\n if (!format) {\n throw new Error(`Unsupported screenshot mime type: ${mimeType}`);\n }\n return screenshotImageExtension(format);\n}\n\nexport function resolveScreenshotSource(\n refInput: unknown,\n options: {\n reportPath: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedScreenshotSource {\n const ref = normalizeScreenshotRef(refInput);\n const id = ref?.id ?? options.fallbackId;\n const mimeType = ref?.mimeType ?? options.fallbackMimeType;\n\n if (!id || !mimeType) {\n throw new Error(\n 'ScreenshotStore: screenshot id and mimeType are required to resolve screenshot',\n );\n }\n\n const resolveReportRelativePath = (filePath: string): string =>\n isAbsolute(filePath)\n ? filePath\n : join(dirname(options.reportPath), filePath);\n\n if (ref?.storage === 'file') {\n if (!ref.path) {\n throw new Error(\n `ScreenshotStore: screenshot ref \"${ref.id}\" missing file path`,\n );\n }\n\n const explicitFilePath = resolveReportRelativePath(ref.path);\n if (existsSync(explicitFilePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: explicitFilePath,\n };\n }\n }\n\n const inlineDataUri = extractImageByIdSync(options.reportPath, id);\n if (inlineDataUri) {\n return {\n type: 'data-uri',\n id,\n mimeType,\n dataUri: inlineDataUri,\n };\n }\n\n const siblingScreenshotPath = join(\n dirname(options.reportPath),\n 'screenshots',\n `${id}.${extensionByMimeType(mimeType)}`,\n );\n if (existsSync(siblingScreenshotPath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: siblingScreenshotPath,\n };\n }\n\n throw new Error(\n `ScreenshotStore: cannot resolve screenshot \"${id}\" from ${options.reportPath}`,\n );\n}\n\nexport class ScreenshotStore {\n private readonly mode: 'inline' | 'directory';\n private readonly reportPath: string;\n private readonly screenshotsDir?: string;\n private readonly writeInlineImage?: (\n id: string,\n base64: string,\n ) => void | Promise<void>;\n private readonly alsoWriteFileCopy: boolean;\n private readonly writtenInlineIds = new Set<string>();\n private readonly writtenFileIds = new Set<string>();\n\n constructor(options: {\n mode: 'inline' | 'directory';\n reportPath: string;\n screenshotsDir?: string;\n writeInlineImage?: (id: string, base64: string) => void | Promise<void>;\n alsoWriteFileCopy?: boolean;\n /** @deprecated Use alsoWriteFileCopy instead. */\n ensureFileCopy?: boolean;\n }) {\n this.mode = options.mode;\n this.reportPath = options.reportPath;\n this.screenshotsDir = options.screenshotsDir;\n this.writeInlineImage = options.writeInlineImage;\n this.alsoWriteFileCopy =\n options.alsoWriteFileCopy ?? options.ensureFileCopy ?? false;\n }\n\n async persist(screenshot: ScreenshotItem): Promise<ScreenshotRef> {\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileRef = shouldWriteFileCopy\n ? await this.persistToSharedFileIfNeeded(screenshot, {\n markAsPersisted: this.mode === 'directory',\n })\n : null;\n\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ScreenshotStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(screenshot.id)) {\n await this.writeInlineImage(screenshot.id, screenshot.base64);\n this.writtenInlineIds.add(screenshot.id);\n }\n return screenshot.markPersistedInline(this.reportPath);\n }\n\n if (!fileRef) {\n throw new Error(\n 'ScreenshotStore: file persistence is required in directory mode',\n );\n }\n return fileRef;\n }\n\n private async persistToSharedFileIfNeeded(\n screenshot: ScreenshotItem,\n options: {\n markAsPersisted: boolean;\n },\n ): Promise<ScreenshotRef> {\n const screenshotsDir = this.screenshotsDir;\n if (!screenshotsDir) {\n throw new Error(\n 'ScreenshotStore: screenshotsDir is required when file persistence is enabled',\n );\n }\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const relativePath = `./screenshots/${screenshot.id}.${screenshot.extension}`;\n const absolutePath = join(\n screenshotsDir,\n `${screenshot.id}.${screenshot.extension}`,\n );\n\n if (!this.writtenFileIds.has(screenshot.id)) {\n const buffer = Buffer.from(screenshot.rawBase64, 'base64');\n await writeFileAsync(absolutePath, buffer);\n this.writtenFileIds.add(screenshot.id);\n }\n\n if (options.markAsPersisted) {\n return screenshot.markPersistedToPath(relativePath, absolutePath);\n }\n\n return screenshot.registerPersistedFileCopy(relativePath, absolutePath);\n }\n\n loadBase64(refInput: unknown): string {\n const ref = normalizeScreenshotRef(refInput);\n if (!ref) {\n throw new Error('ScreenshotStore: invalid screenshot reference');\n }\n\n const resolved = resolveScreenshotSource(ref, {\n reportPath: this.reportPath,\n });\n\n if (resolved.type === 'data-uri') {\n return resolved.dataUri;\n }\n\n const data = readFileSync(resolved.filePath);\n return `data:${resolved.mimeType};base64,${data.toString('base64')}`;\n }\n\n cleanup(): void {\n if (\n this.mode === 'directory' &&\n this.screenshotsDir &&\n existsSync(this.screenshotsDir)\n ) {\n rmSync(this.screenshotsDir, { recursive: true, force: true });\n }\n this.writtenInlineIds.clear();\n this.writtenFileIds.clear();\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","normalizeScreenshotRef","value","record","isScreenshotImageMimeType","extensionByMimeType","mimeType","format","screenshotImageFormatFromMimeType","Error","screenshotImageExtension","resolveScreenshotSource","refInput","options","ref","id","resolveReportRelativePath","filePath","isAbsolute","join","dirname","explicitFilePath","existsSync","inlineDataUri","extractImageByIdSync","siblingScreenshotPath","ScreenshotStore","screenshot","shouldWriteFileCopy","fileRef","screenshotsDir","mkdirSync","relativePath","absolutePath","buffer","Buffer","writeFileAsync","resolved","data","readFileSync","rmSync","Set"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"report-cli.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-cli.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n copyFileSync,\n existsSync,\n mkdirSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport { extractAllDumpScriptsSync } from './dump/html-utils';\nimport { resolveScreenshotSource } from './dump/screenshot-store';\nimport { deriveCaseStatus } from './dump/task-status';\nimport {\n ReportMergingTool,\n collectDedupedExecutions,\n splitReportHtmlByExecution,\n} from './report';\nimport { reportToMarkdown } from './report-markdown';\nimport type { MarkdownAttachment } from './report-markdown';\nimport type { ReportFileAttributes, TestStatus } from './types';\nimport { ReportActionDump } from './types';\n\ntype ReportCliToolResult = {\n isError: boolean;\n content: Array<{\n type: 'text';\n text: string;\n }>;\n};\n\ntype ReportCliSchema = Record<string, z.ZodTypeAny>;\n\nexport interface ReportCliCommandDefinition {\n name: string;\n description: string;\n schema: ReportCliSchema;\n handler: (args: Record<string, unknown>) => Promise<ReportCliToolResult>;\n}\n\nexport interface ReportCliCommandEntry {\n name: string;\n def: ReportCliCommandDefinition;\n}\n\nexport type ConsumeReportFileAction = 'split' | 'to-markdown' | 'merge-html';\n\nexport interface ConsumeReportFileOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport type SplitReportFileOptions = ConsumeReportFileOptions;\nexport type ReportFileToMarkdownOptions = ConsumeReportFileOptions;\n\nexport interface MergeReportFilesOptions {\n htmlPaths: string[];\n outputDir?: string;\n outputName?: string;\n overwrite?: boolean;\n}\n\nexport interface MergeReportFilesResult {\n mergedReportPath: string;\n}\n\nfunction writeAttachmentFromReport(\n attachment: MarkdownAttachment,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const { suggestedFileName, id, mimeType } = attachment;\n if (opts.writtenFiles.has(suggestedFileName)) return;\n\n const absolutePath = path.join(opts.screenshotsDir, suggestedFileName);\n\n const resolved = resolveScreenshotSource(attachment.sourceRef ?? null, {\n reportPath: opts.htmlPath,\n fallbackId: id,\n fallbackMimeType: (mimeType || 'image/png') as 'image/png' | 'image/jpeg',\n });\n\n if (resolved.type === 'data-uri') {\n const rawBase64 = resolved.dataUri.replace(\n /^data:image\\/[a-zA-Z+]+;base64,/,\n '',\n );\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n opts.writtenFiles.add(suggestedFileName);\n return;\n }\n\n if (!existsSync(resolved.filePath)) {\n throw new Error(\n `Cannot resolve screenshot \"${id}\" for markdown attachment from ${opts.htmlPath}`,\n );\n }\n\n copyFileSync(resolved.filePath, absolutePath);\n opts.writtenFiles.add(suggestedFileName);\n}\n\nasync function markdownFromReport(\n htmlPath: string,\n outputDir: string,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n const mergedReport = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n\n const result = reportToMarkdown(mergedReport);\n\n const markdownFiles: string[] = [];\n const writtenScreenshots = new Set<string>();\n\n const mdPath = path.join(outputDir, 'report.md');\n writeFileSync(mdPath, result.markdown, 'utf-8');\n markdownFiles.push(mdPath);\n\n for (const attachment of result.attachments) {\n writeAttachmentFromReport(attachment, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshots,\n });\n }\n\n return {\n markdownFiles,\n screenshotFiles: Array.from(writtenScreenshots)\n .sort()\n .map((f) => path.join(screenshotsDir, f)),\n };\n}\n\nfunction resolveReportHtmlPath(htmlPath: string): string {\n const normalizedPath = path.resolve(htmlPath);\n\n if (!existsSync(normalizedPath)) {\n throw new Error(`report-tool: --htmlPath does not exist: ${htmlPath}`);\n }\n\n const stats = statSync(normalizedPath);\n if (!stats.isDirectory()) {\n return normalizedPath;\n }\n\n const indexHtmlPath = path.join(normalizedPath, 'index.html');\n if (!existsSync(indexHtmlPath)) {\n throw new Error(\n `report-tool: \"${htmlPath}\" is not an HTML report file, and no index.html was found under this directory.`,\n );\n }\n\n return indexHtmlPath;\n}\n\nexport function splitReportFile(options: SplitReportFileOptions): {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n} {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('splitReportFile: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('splitReportFile: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return splitReportHtmlByExecution({\n htmlPath: resolvedHtmlPath,\n outputDir,\n });\n}\n\nexport async function reportFileToMarkdown(\n options: ReportFileToMarkdownOptions,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('reportFileToMarkdown: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('reportFileToMarkdown: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return markdownFromReport(resolvedHtmlPath, outputDir);\n}\n\nconst TEST_STATUS_VALUES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'passed',\n 'failed',\n 'timedOut',\n 'skipped',\n 'interrupted',\n]);\n\nconst FAILING_TEST_STATUSES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'failed',\n 'timedOut',\n 'interrupted',\n]);\n\n/**\n * Reuse a `playwright_test_status` already recorded on the source report's dump\n * scripts (e.g. a Playwright-generated report carries the precise\n * timedOut/skipped/interrupted status). Returns undefined when the source has\n * no such attribute, so the caller can fall back to deriving from the dump.\n *\n * A single source report may bundle several executions (e.g. an already-merged\n * report), so surface a failing status if any execution failed — mirroring\n * `deriveCaseStatus`'s any-failure-wins semantics rather than taking the first\n * script's status and masking later failures.\n */\nfunction readSourceTestStatus(htmlPath: string): TestStatus | undefined {\n const recorded: TestStatus[] = [];\n for (const { openTag } of extractAllDumpScriptsSync(htmlPath)) {\n const match = openTag.match(/playwright_test_status=\"([^\"]*)\"/);\n if (!match) continue;\n const status = decodeURIComponent(match[1]) as TestStatus;\n if (TEST_STATUS_VALUES.has(status)) {\n recorded.push(status);\n }\n }\n if (recorded.length === 0) return undefined;\n return recorded.find((s) => FAILING_TEST_STATUSES.has(s)) ?? recorded[0];\n}\n\nfunction deriveReportAttributesFromHtml(\n htmlPath: string,\n index: number,\n): ReportFileAttributes {\n const fallbackId = `${path.basename(path.dirname(htmlPath)) || path.basename(htmlPath, path.extname(htmlPath))}-${index + 1}`;\n try {\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n // Prefer a status the source report already recorded (keeps Playwright's\n // precise timedOut/skipped/interrupted); otherwise infer it from the dump\n // so a failing case is not silently merged in as passed.\n const testStatus =\n readSourceTestStatus(htmlPath) ?? deriveCaseStatus(executions);\n return {\n testId: fallbackId,\n testTitle: baseDump.groupName || fallbackId,\n testDescription: baseDump.groupDescription ?? '',\n testDuration: 0,\n testStatus,\n };\n } catch {\n return {\n testId: fallbackId,\n testTitle: fallbackId,\n testDescription: '',\n testDuration: 0,\n testStatus: 'passed' as TestStatus,\n };\n }\n}\n\nexport function mergeReportFiles(\n options: MergeReportFilesOptions,\n): MergeReportFilesResult {\n const { htmlPaths, outputDir, outputName, overwrite = false } = options;\n if (!htmlPaths || htmlPaths.length === 0) {\n throw new Error('mergeReportFiles: htmlPaths is required');\n }\n\n const resolvedPaths = htmlPaths.map((p) => resolveReportHtmlPath(p));\n\n const tool = new ReportMergingTool();\n resolvedPaths.forEach((htmlPath, index) => {\n tool.append({\n reportFilePath: htmlPath,\n reportAttributes: deriveReportAttributesFromHtml(htmlPath, index),\n });\n });\n\n const mergedReportPath = tool.mergeReports(outputName ?? 'AUTO', {\n overwrite,\n outputDir,\n });\n\n if (!mergedReportPath) {\n throw new Error('mergeReportFiles: failed to produce a merged report');\n }\n\n return { mergedReportPath };\n}\n\nfunction normalizeHtmlReportArg(raw: unknown): string[] | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (Array.isArray(raw)) {\n return raw.filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n );\n }\n if (typeof raw === 'string') {\n const trimmed = raw.trim();\n return trimmed ? [trimmed] : [];\n }\n return undefined;\n}\n\nconst reportCommandDefinition: ReportCliCommandDefinition = {\n name: 'report-tool',\n description:\n 'Transform Midscene report artifacts, including splitting executions, converting to markdown, and merging multiple reports.',\n schema: {\n action: z\n .enum(['split', 'to-markdown', 'merge-html'])\n .optional()\n .describe(\n 'Report action to run. Supports: split, to-markdown, merge-html. Defaults to split.',\n ),\n htmlPath: z\n .string()\n .optional()\n .describe(\n 'Input report HTML path (e.g. ./report/index.html). Used by split and to-markdown.',\n ),\n htmlReport: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Input report HTML path for the merge action. Repeat the flag to merge multiple reports (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n ),\n outputDir: z\n .string()\n .optional()\n .describe(\n 'Output directory for generated report artifacts. For merge, defaults to the Midscene report directory.',\n ),\n outputName: z\n .string()\n .optional()\n .describe(\n 'Output report file/directory name (without .html) for the merge action. Defaults to an auto-generated name.',\n ),\n overwrite: z\n .union([z.boolean(), z.string()])\n .optional()\n .describe(\n 'Overwrite the existing merged report file if present (merge action only).',\n ),\n },\n handler: async (args) => {\n const {\n action = 'split',\n htmlPath,\n htmlReport,\n outputDir,\n outputName,\n overwrite,\n } = args as {\n action?: string;\n htmlPath?: string;\n htmlReport?: unknown;\n outputDir?: string;\n outputName?: string;\n overwrite?: unknown;\n };\n if (\n action !== 'split' &&\n action !== 'to-markdown' &&\n action !== 'merge-html'\n ) {\n throw new Error(\n `report-tool: unsupported --action value \"${action}\". Currently supported: split, to-markdown, merge-html`,\n );\n }\n\n if (action === 'merge-html') {\n const paths = normalizeHtmlReportArg(htmlReport);\n if (!paths || paths.length === 0) {\n throw new Error(\n 'report-tool: --htmlReport is required for action \"merge-html\". Repeat --htmlReport for each report (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n );\n }\n\n const overwriteFlag =\n overwrite === true || overwrite === 'true' || overwrite === '1';\n\n const result = mergeReportFiles({\n htmlPaths: paths,\n outputDir,\n outputName,\n overwrite: overwriteFlag,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Merged ${paths.length} report(s) into ${result.mergedReportPath}`,\n },\n ],\n };\n }\n\n if (!htmlPath) {\n throw new Error('report-tool: --htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('report-tool: --outputDir is required');\n }\n\n if (action === 'to-markdown') {\n const result = await reportFileToMarkdown({\n htmlPath,\n outputDir,\n });\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Markdown export completed. Generated ${result.markdownFiles.length} markdown file(s) and ${result.screenshotFiles.length} screenshot(s). Output path: ${outputDir}`,\n },\n ],\n };\n }\n\n const result = splitReportFile({\n htmlPath,\n outputDir,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Report split completed. Generated ${result.executionJsonFiles.length} execution JSON files and ${result.screenshotFiles.length} screenshots. Output path: ${outputDir}`,\n },\n ],\n };\n },\n};\n\nexport function createReportCliCommands(): ReportCliCommandEntry[] {\n return [\n {\n name: 'report-tool',\n def: reportCommandDefinition,\n },\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","writeAttachmentFromReport","attachment","opts","suggestedFileName","id","mimeType","absolutePath","path","resolved","resolveScreenshotSource","rawBase64","writeFileSync","Buffer","existsSync","Error","copyFileSync","markdownFromReport","htmlPath","outputDir","screenshotsDir","mkdirSync","baseDump","executions","collectDedupedExecutions","mergedReport","ReportActionDump","result","reportToMarkdown","markdownFiles","writtenScreenshots","Set","mdPath","Array","f","resolveReportHtmlPath","normalizedPath","stats","statSync","indexHtmlPath","splitReportFile","options","resolvedHtmlPath","splitReportHtmlByExecution","reportFileToMarkdown","TEST_STATUS_VALUES","FAILING_TEST_STATUSES","readSourceTestStatus","recorded","openTag","extractAllDumpScriptsSync","match","status","decodeURIComponent","s","deriveReportAttributesFromHtml","index","fallbackId","testStatus","deriveCaseStatus","mergeReportFiles","htmlPaths","outputName","overwrite","resolvedPaths","p","tool","ReportMergingTool","mergedReportPath","normalizeHtmlReportArg","raw","trimmed","reportCommandDefinition","z","args","action","htmlReport","paths","overwriteFlag","createReportCliCommands"],"mappings":"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"}
1
+ {"version":3,"file":"report-cli.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-cli.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n copyFileSync,\n existsSync,\n mkdirSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport { extractAllDumpScriptsSync } from './dump/html-utils';\nimport { resolveScreenshotSource } from './dump/screenshot-store';\nimport { deriveCaseStatus } from './dump/task-status';\nimport {\n ReportMergingTool,\n collectDedupedExecutions,\n splitReportHtmlByExecution,\n} from './report';\nimport { reportToMarkdown } from './report-markdown';\nimport type { MarkdownAttachment } from './report-markdown';\nimport type { ReportFileAttributes, TestStatus } from './types';\nimport { ReportActionDump } from './types';\n\ntype ReportCliToolResult = {\n isError: boolean;\n content: Array<{\n type: 'text';\n text: string;\n }>;\n};\n\ntype ReportCliSchema = Record<string, z.ZodTypeAny>;\n\nexport interface ReportCliCommandDefinition {\n name: string;\n description: string;\n schema: ReportCliSchema;\n handler: (args: Record<string, unknown>) => Promise<ReportCliToolResult>;\n}\n\nexport interface ReportCliCommandEntry {\n name: string;\n def: ReportCliCommandDefinition;\n}\n\nexport type ConsumeReportFileAction = 'split' | 'to-markdown' | 'merge-html';\n\nexport interface ConsumeReportFileOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport type SplitReportFileOptions = ConsumeReportFileOptions;\nexport type ReportFileToMarkdownOptions = ConsumeReportFileOptions;\n\nexport interface MergeReportFilesOptions {\n htmlPaths: string[];\n outputDir?: string;\n outputName?: string;\n overwrite?: boolean;\n}\n\nexport interface MergeReportFilesResult {\n mergedReportPath: string;\n}\n\nfunction writeAttachmentFromReport(\n attachment: MarkdownAttachment,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const { suggestedFileName, id, mimeType } = attachment;\n if (opts.writtenFiles.has(suggestedFileName)) return;\n\n const absolutePath = path.join(opts.screenshotsDir, suggestedFileName);\n\n const resolved = resolveScreenshotSource(attachment.sourceRef ?? null, {\n reportPath: opts.htmlPath,\n fallbackId: id,\n fallbackMimeType: mimeType || 'image/png',\n });\n\n if (resolved.type === 'data-uri') {\n const rawBase64 = resolved.dataUri.replace(\n /^data:image\\/[a-zA-Z+]+;base64,/,\n '',\n );\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n opts.writtenFiles.add(suggestedFileName);\n return;\n }\n\n if (!existsSync(resolved.filePath)) {\n throw new Error(\n `Cannot resolve screenshot \"${id}\" for markdown attachment from ${opts.htmlPath}`,\n );\n }\n\n copyFileSync(resolved.filePath, absolutePath);\n opts.writtenFiles.add(suggestedFileName);\n}\n\nasync function markdownFromReport(\n htmlPath: string,\n outputDir: string,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n const mergedReport = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n\n const result = reportToMarkdown(mergedReport);\n\n const markdownFiles: string[] = [];\n const writtenScreenshots = new Set<string>();\n\n const mdPath = path.join(outputDir, 'report.md');\n writeFileSync(mdPath, result.markdown, 'utf-8');\n markdownFiles.push(mdPath);\n\n for (const attachment of result.attachments) {\n writeAttachmentFromReport(attachment, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshots,\n });\n }\n\n return {\n markdownFiles,\n screenshotFiles: Array.from(writtenScreenshots)\n .sort()\n .map((f) => path.join(screenshotsDir, f)),\n };\n}\n\nfunction resolveReportHtmlPath(htmlPath: string): string {\n const normalizedPath = path.resolve(htmlPath);\n\n if (!existsSync(normalizedPath)) {\n throw new Error(`report-tool: --htmlPath does not exist: ${htmlPath}`);\n }\n\n const stats = statSync(normalizedPath);\n if (!stats.isDirectory()) {\n return normalizedPath;\n }\n\n const indexHtmlPath = path.join(normalizedPath, 'index.html');\n if (!existsSync(indexHtmlPath)) {\n throw new Error(\n `report-tool: \"${htmlPath}\" is not an HTML report file, and no index.html was found under this directory.`,\n );\n }\n\n return indexHtmlPath;\n}\n\nexport function splitReportFile(options: SplitReportFileOptions): {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n} {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('splitReportFile: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('splitReportFile: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return splitReportHtmlByExecution({\n htmlPath: resolvedHtmlPath,\n outputDir,\n });\n}\n\nexport async function reportFileToMarkdown(\n options: ReportFileToMarkdownOptions,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('reportFileToMarkdown: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('reportFileToMarkdown: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return markdownFromReport(resolvedHtmlPath, outputDir);\n}\n\nconst TEST_STATUS_VALUES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'passed',\n 'failed',\n 'timedOut',\n 'skipped',\n 'interrupted',\n]);\n\nconst FAILING_TEST_STATUSES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'failed',\n 'timedOut',\n 'interrupted',\n]);\n\n/**\n * Reuse a `playwright_test_status` already recorded on the source report's dump\n * scripts (e.g. a Playwright-generated report carries the precise\n * timedOut/skipped/interrupted status). Returns undefined when the source has\n * no such attribute, so the caller can fall back to deriving from the dump.\n *\n * A single source report may bundle several executions (e.g. an already-merged\n * report), so surface a failing status if any execution failed — mirroring\n * `deriveCaseStatus`'s any-failure-wins semantics rather than taking the first\n * script's status and masking later failures.\n */\nfunction readSourceTestStatus(htmlPath: string): TestStatus | undefined {\n const recorded: TestStatus[] = [];\n for (const { openTag } of extractAllDumpScriptsSync(htmlPath)) {\n const match = openTag.match(/playwright_test_status=\"([^\"]*)\"/);\n if (!match) continue;\n const status = decodeURIComponent(match[1]) as TestStatus;\n if (TEST_STATUS_VALUES.has(status)) {\n recorded.push(status);\n }\n }\n if (recorded.length === 0) return undefined;\n return recorded.find((s) => FAILING_TEST_STATUSES.has(s)) ?? recorded[0];\n}\n\nfunction deriveReportAttributesFromHtml(\n htmlPath: string,\n index: number,\n): ReportFileAttributes {\n const fallbackId = `${path.basename(path.dirname(htmlPath)) || path.basename(htmlPath, path.extname(htmlPath))}-${index + 1}`;\n try {\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n // Prefer a status the source report already recorded (keeps Playwright's\n // precise timedOut/skipped/interrupted); otherwise infer it from the dump\n // so a failing case is not silently merged in as passed.\n const testStatus =\n readSourceTestStatus(htmlPath) ?? deriveCaseStatus(executions);\n return {\n testId: fallbackId,\n testTitle: baseDump.groupName || fallbackId,\n testDescription: baseDump.groupDescription ?? '',\n testDuration: 0,\n testStatus,\n };\n } catch {\n return {\n testId: fallbackId,\n testTitle: fallbackId,\n testDescription: '',\n testDuration: 0,\n testStatus: 'passed' as TestStatus,\n };\n }\n}\n\nexport function mergeReportFiles(\n options: MergeReportFilesOptions,\n): MergeReportFilesResult {\n const { htmlPaths, outputDir, outputName, overwrite = false } = options;\n if (!htmlPaths || htmlPaths.length === 0) {\n throw new Error('mergeReportFiles: htmlPaths is required');\n }\n\n const resolvedPaths = htmlPaths.map((p) => resolveReportHtmlPath(p));\n\n const tool = new ReportMergingTool();\n resolvedPaths.forEach((htmlPath, index) => {\n tool.append({\n reportFilePath: htmlPath,\n reportAttributes: deriveReportAttributesFromHtml(htmlPath, index),\n });\n });\n\n const mergedReportPath = tool.mergeReports(outputName ?? 'AUTO', {\n overwrite,\n outputDir,\n });\n\n if (!mergedReportPath) {\n throw new Error('mergeReportFiles: failed to produce a merged report');\n }\n\n return { mergedReportPath };\n}\n\nfunction normalizeHtmlReportArg(raw: unknown): string[] | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (Array.isArray(raw)) {\n return raw.filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n );\n }\n if (typeof raw === 'string') {\n const trimmed = raw.trim();\n return trimmed ? [trimmed] : [];\n }\n return undefined;\n}\n\nconst reportCommandDefinition: ReportCliCommandDefinition = {\n name: 'report-tool',\n description:\n 'Transform Midscene report artifacts, including splitting executions, converting to markdown, and merging multiple reports.',\n schema: {\n action: z\n .enum(['split', 'to-markdown', 'merge-html'])\n .optional()\n .describe(\n 'Report action to run. Supports: split, to-markdown, merge-html. Defaults to split.',\n ),\n htmlPath: z\n .string()\n .optional()\n .describe(\n 'Input report HTML path (e.g. ./report/index.html). Used by split and to-markdown.',\n ),\n htmlReport: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Input report HTML path for the merge action. Repeat the flag to merge multiple reports (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n ),\n outputDir: z\n .string()\n .optional()\n .describe(\n 'Output directory for generated report artifacts. For merge, defaults to the Midscene report directory.',\n ),\n outputName: z\n .string()\n .optional()\n .describe(\n 'Output report file/directory name (without .html) for the merge action. Defaults to an auto-generated name.',\n ),\n overwrite: z\n .union([z.boolean(), z.string()])\n .optional()\n .describe(\n 'Overwrite the existing merged report file if present (merge action only).',\n ),\n },\n handler: async (args) => {\n const {\n action = 'split',\n htmlPath,\n htmlReport,\n outputDir,\n outputName,\n overwrite,\n } = args as {\n action?: string;\n htmlPath?: string;\n htmlReport?: unknown;\n outputDir?: string;\n outputName?: string;\n overwrite?: unknown;\n };\n if (\n action !== 'split' &&\n action !== 'to-markdown' &&\n action !== 'merge-html'\n ) {\n throw new Error(\n `report-tool: unsupported --action value \"${action}\". Currently supported: split, to-markdown, merge-html`,\n );\n }\n\n if (action === 'merge-html') {\n const paths = normalizeHtmlReportArg(htmlReport);\n if (!paths || paths.length === 0) {\n throw new Error(\n 'report-tool: --htmlReport is required for action \"merge-html\". Repeat --htmlReport for each report (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n );\n }\n\n const overwriteFlag =\n overwrite === true || overwrite === 'true' || overwrite === '1';\n\n const result = mergeReportFiles({\n htmlPaths: paths,\n outputDir,\n outputName,\n overwrite: overwriteFlag,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Merged ${paths.length} report(s) into ${result.mergedReportPath}`,\n },\n ],\n };\n }\n\n if (!htmlPath) {\n throw new Error('report-tool: --htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('report-tool: --outputDir is required');\n }\n\n if (action === 'to-markdown') {\n const result = await reportFileToMarkdown({\n htmlPath,\n outputDir,\n });\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Markdown export completed. Generated ${result.markdownFiles.length} markdown file(s) and ${result.screenshotFiles.length} screenshot(s). Output path: ${outputDir}`,\n },\n ],\n };\n }\n\n const result = splitReportFile({\n htmlPath,\n outputDir,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Report split completed. Generated ${result.executionJsonFiles.length} execution JSON files and ${result.screenshotFiles.length} screenshots. Output path: ${outputDir}`,\n },\n ],\n };\n },\n};\n\nexport function createReportCliCommands(): ReportCliCommandEntry[] {\n return [\n {\n name: 'report-tool',\n def: reportCommandDefinition,\n },\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","writeAttachmentFromReport","attachment","opts","suggestedFileName","id","mimeType","absolutePath","path","resolved","resolveScreenshotSource","rawBase64","writeFileSync","Buffer","existsSync","Error","copyFileSync","markdownFromReport","htmlPath","outputDir","screenshotsDir","mkdirSync","baseDump","executions","collectDedupedExecutions","mergedReport","ReportActionDump","result","reportToMarkdown","markdownFiles","writtenScreenshots","Set","mdPath","Array","f","resolveReportHtmlPath","normalizedPath","stats","statSync","indexHtmlPath","splitReportFile","options","resolvedHtmlPath","splitReportHtmlByExecution","reportFileToMarkdown","TEST_STATUS_VALUES","FAILING_TEST_STATUSES","readSourceTestStatus","recorded","openTag","extractAllDumpScriptsSync","match","status","decodeURIComponent","s","deriveReportAttributesFromHtml","index","fallbackId","testStatus","deriveCaseStatus","mergeReportFiles","htmlPaths","outputName","overwrite","resolvedPaths","p","tool","ReportMergingTool","mergedReportPath","normalizeHtmlReportArg","raw","trimmed","reportCommandDefinition","z","args","action","htmlReport","paths","overwriteFlag","createReportCliCommands"],"mappings":"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"}
@@ -29,9 +29,9 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  });
30
30
  const ui_utils_js_namespaceObject = require("./agent/ui-utils.js");
31
31
  const external_screenshot_item_js_namespaceObject = require("./screenshot-item.js");
32
+ const img_namespaceObject = require("@midscene/shared/img");
32
33
  const screenshot_store_js_namespaceObject = require("./dump/screenshot-store.js");
33
- const screenshotDataUrlPattern = /^data:image\/(png|jpeg|jpg);base64,([\s\S]*)$/i;
34
- const rawBase64BodyPattern = /^[a-zA-Z0-9+/=\s]+$/;
34
+ const screenshotDataUrlPattern = /^data:image\/(?:png|jpe?g|webp);base64,/i;
35
35
  const jsonContextMaxStringLength = 12000;
36
36
  function toExecutionDump(execution) {
37
37
  if (!execution || 'object' != typeof execution) throw new Error('executionToMarkdown: execution is required');
@@ -247,23 +247,23 @@ function extractLocateCenter(task) {
247
247
  ];
248
248
  }
249
249
  function tryExtractBase64(screenshot) {
250
- if ('string' == typeof screenshot) {
251
- const trimmedScreenshot = screenshot.trim();
252
- const dataUrlMatch = trimmedScreenshot.match(screenshotDataUrlPattern);
253
- if (dataUrlMatch) {
254
- const format = 'jpg' === dataUrlMatch[1].toLowerCase() ? 'jpeg' : 'png';
255
- const base64Body = dataUrlMatch[2].replace(/\s/g, '');
256
- if (!base64Body) return;
257
- return `data:image/${format};base64,${base64Body}`;
258
- }
259
- if (trimmedScreenshot.startsWith('data:') || !rawBase64BodyPattern.test(trimmedScreenshot)) return;
260
- const base64Body = trimmedScreenshot.replace(/\s/g, '');
261
- return base64Body ? `data:image/png;base64,${base64Body}` : void 0;
250
+ if ('string' == typeof screenshot) try {
251
+ return (0, img_namespaceObject.normalizeScreenshotBase64)(screenshot);
252
+ } catch {
253
+ return;
262
254
  }
263
255
  if (!screenshot || 'object' != typeof screenshot) return;
264
256
  const s = screenshot;
265
257
  if ('string' == typeof s.base64 && s.base64.length > 0) return s.base64;
266
258
  }
259
+ function screenshotMetadataFromMimeType(mimeType) {
260
+ const format = (0, img_namespaceObject.screenshotImageFormatFromMimeType)(mimeType);
261
+ if (!format) throw new Error(`Unsupported screenshot mime type: ${String(mimeType)}`);
262
+ return {
263
+ extension: (0, img_namespaceObject.screenshotImageExtension)(format),
264
+ mimeType: (0, img_namespaceObject.screenshotImageMimeType)(format)
265
+ };
266
+ }
267
267
  function restoredSourceRef(screenshot) {
268
268
  if (!screenshot || 'object' != typeof screenshot) return;
269
269
  const sourceRef = screenshot.sourceRef;
@@ -279,7 +279,7 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
279
279
  attachment: {
280
280
  id: screenshot.id,
281
281
  suggestedFileName,
282
- mimeType: `image/${'jpeg' === ext ? 'jpeg' : 'png'}`,
282
+ mimeType: screenshot.mimeType,
283
283
  executionIndex,
284
284
  taskIndex,
285
285
  base64Data: tryExtractBase64(screenshot)
@@ -288,7 +288,7 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
288
288
  }
289
289
  const ref = (0, screenshot_store_js_namespaceObject.normalizeScreenshotRef)(screenshot);
290
290
  if (ref) {
291
- const ext = 'image/jpeg' === ref.mimeType ? 'jpeg' : 'png';
291
+ const { extension: ext } = screenshotMetadataFromMimeType(ref.mimeType);
292
292
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;
293
293
  return {
294
294
  markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
@@ -305,7 +305,7 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
305
305
  }
306
306
  const sourceRef = restoredSourceRef(screenshot);
307
307
  if (sourceRef) {
308
- const ext = 'image/jpeg' === sourceRef.mimeType ? 'jpeg' : 'png';
308
+ const { extension: ext } = screenshotMetadataFromMimeType(sourceRef.mimeType);
309
309
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${sourceRef.id}.${ext}`;
310
310
  return {
311
311
  markdown: `\n![${markdownLabel}](${screenshotBaseDir}/${suggestedFileName})`,
@@ -322,7 +322,7 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
322
322
  }
323
323
  const base64 = tryExtractBase64(screenshot);
324
324
  if (base64) {
325
- const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';
325
+ const { extension: ext, mimeType } = screenshotMetadataFromMimeType((0, img_namespaceObject.parseBase64)(base64).mimeType);
326
326
  const idSuffix = options?.fallbackIdSuffix ? `-${options.fallbackIdSuffix}` : '';
327
327
  const id = `restored-${executionIndex + 1}-${taskIndex + 1}${idSuffix}`;
328
328
  const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;
@@ -331,7 +331,7 @@ function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, tas
331
331
  attachment: {
332
332
  id,
333
333
  suggestedFileName,
334
- mimeType: `image/${ext}`,
334
+ mimeType,
335
335
  executionIndex,
336
336
  taskIndex,
337
337
  base64Data: base64