@midscene/core 1.17.4 → 1.17.5

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 (86) hide show
  1. package/dist/es/agent/agent-report-manager.mjs +28 -5
  2. package/dist/es/agent/agent-report-manager.mjs.map +1 -1
  3. package/dist/es/agent/agent.mjs +2 -2
  4. package/dist/es/agent/agent.mjs.map +1 -1
  5. package/dist/es/agent/utils.mjs +1 -1
  6. package/dist/es/dump/html-utils.mjs +51 -68
  7. package/dist/es/dump/html-utils.mjs.map +1 -1
  8. package/dist/es/dump/report-artifact-publication.mjs +52 -0
  9. package/dist/es/dump/report-artifact-publication.mjs.map +1 -0
  10. package/dist/es/dump/report-artifact-store.mjs +2 -2
  11. package/dist/es/dump/report-artifact-store.mjs.map +1 -1
  12. package/dist/es/dump/report-artifact-writer.mjs +129 -0
  13. package/dist/es/dump/report-artifact-writer.mjs.map +1 -0
  14. package/dist/es/dump/report-session.mjs +229 -0
  15. package/dist/es/dump/report-session.mjs.map +1 -0
  16. package/dist/es/dump/test-report-images.mjs +68 -42
  17. package/dist/es/dump/test-report-images.mjs.map +1 -1
  18. package/dist/es/index.mjs +2 -2
  19. package/dist/es/index.mjs.map +1 -1
  20. package/dist/es/report-cli.mjs +38 -11
  21. package/dist/es/report-cli.mjs.map +1 -1
  22. package/dist/es/report-generator.mjs +2 -1
  23. package/dist/es/report-generator.mjs.map +1 -1
  24. package/dist/es/report-html-template.mjs +1 -1
  25. package/dist/es/report.mjs +152 -159
  26. package/dist/es/report.mjs.map +1 -1
  27. package/dist/es/test-run-report.mjs.map +1 -1
  28. package/dist/es/utils.mjs +1 -1
  29. package/dist/es/yaml/player.mjs +1 -1
  30. package/dist/es/yaml/player.mjs.map +1 -1
  31. package/dist/lib/agent/agent-report-manager.js +27 -4
  32. package/dist/lib/agent/agent-report-manager.js.map +1 -1
  33. package/dist/lib/agent/agent.js +2 -2
  34. package/dist/lib/agent/agent.js.map +1 -1
  35. package/dist/lib/agent/utils.js +1 -1
  36. package/dist/lib/dump/html-utils.js +59 -70
  37. package/dist/lib/dump/html-utils.js.map +1 -1
  38. package/dist/lib/dump/report-artifact-publication.js +89 -0
  39. package/dist/lib/dump/report-artifact-publication.js.map +1 -0
  40. package/dist/lib/dump/report-artifact-store.js +2 -2
  41. package/dist/lib/dump/report-artifact-store.js.map +1 -1
  42. package/dist/lib/dump/report-artifact-writer.js +163 -0
  43. package/dist/lib/dump/report-artifact-writer.js.map +1 -0
  44. package/dist/lib/dump/report-session.js +266 -0
  45. package/dist/lib/dump/report-session.js.map +1 -0
  46. package/dist/lib/dump/test-report-images.js +67 -41
  47. package/dist/lib/dump/test-report-images.js.map +1 -1
  48. package/dist/lib/index.js +3 -0
  49. package/dist/lib/index.js.map +1 -1
  50. package/dist/lib/report-cli.js +37 -10
  51. package/dist/lib/report-cli.js.map +1 -1
  52. package/dist/lib/report-generator.js +2 -1
  53. package/dist/lib/report-generator.js.map +1 -1
  54. package/dist/lib/report-html-template.js +1 -1
  55. package/dist/lib/report.js +152 -153
  56. package/dist/lib/report.js.map +1 -1
  57. package/dist/lib/test-run-report.js.map +1 -1
  58. package/dist/lib/utils.js +1 -1
  59. package/dist/lib/yaml/player.js +1 -1
  60. package/dist/lib/yaml/player.js.map +1 -1
  61. package/dist/types/agent/agent-report-manager.d.ts +4 -1
  62. package/dist/types/agent/agent-report-manager.d.ts.map +1 -1
  63. package/dist/types/agent/agent.d.ts +1 -1
  64. package/dist/types/agent/agent.d.ts.map +1 -1
  65. package/dist/types/dump/html-utils.d.ts +7 -1
  66. package/dist/types/dump/html-utils.d.ts.map +1 -1
  67. package/dist/types/dump/report-artifact-publication.d.ts +5 -0
  68. package/dist/types/dump/report-artifact-publication.d.ts.map +1 -0
  69. package/dist/types/dump/report-artifact-store.d.ts +1 -1
  70. package/dist/types/dump/report-artifact-store.d.ts.map +1 -1
  71. package/dist/types/dump/report-artifact-writer.d.ts +21 -0
  72. package/dist/types/dump/report-artifact-writer.d.ts.map +1 -0
  73. package/dist/types/dump/report-session.d.ts +48 -0
  74. package/dist/types/dump/report-session.d.ts.map +1 -0
  75. package/dist/types/dump/test-report-images.d.ts +5 -2
  76. package/dist/types/dump/test-report-images.d.ts.map +1 -1
  77. package/dist/types/index.d.ts +1 -1
  78. package/dist/types/index.d.ts.map +1 -1
  79. package/dist/types/report-cli.d.ts +2 -1
  80. package/dist/types/report-cli.d.ts.map +1 -1
  81. package/dist/types/report-generator.d.ts.map +1 -1
  82. package/dist/types/report.d.ts +11 -0
  83. package/dist/types/report.d.ts.map +1 -1
  84. package/dist/types/test-run-report.d.ts +2 -2
  85. package/dist/types/test-run-report.d.ts.map +1 -1
  86. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport * as fsAsync from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg, uuid } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent/report-file-name';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n extractLastDumpScriptSync,\n generateAgentReportComment,\n getBaseUrlFixScript,\n generateTestRunReportScriptTag as runnerDumpScript,\n streamDumpScriptsSync,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport {\n imageRefFileExtension,\n normalizeStoredImageRef,\n} from './dump/image-reference';\nimport {\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nimport {\n parseBase64ImageDataUrl,\n resolveImageSource,\n} from './dump/screenshot-store';\nexport {\n readReportActionDump,\n readReportArtifactManifestAsync,\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nexport { ExecutionRecorder } from './dump/execution-recorder';\nexport { ReportArtifactStore } from './dump/report-artifact-store';\nimport { TestReportImages } from './dump/test-report-images';\nimport { collectReportSummary } from './report-stats';\nimport type {\n AssembleTestRunReportOptions,\n IndexedTestRunReportSource,\n TestRunReportMetrics,\n TestRunReportSource,\n TestRunReportSourceIndex,\n} from './test-run-report';\nimport {\n type MergeTestRunReportSource,\n mergeTestRunReportDumps,\n} from './test-runner/reporting/merge-report';\nimport { collectTestRunReportMetrics } from './test-runner/reporting/metrics';\nimport {\n type ExecutionDump,\n type IExecutionDump,\n ReportActionDump,\n type ScreenshotMode,\n} from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, getVersion, reportHTMLContent } from './utils';\n\nexport {\n calculateTestRunHealth,\n classifyTestRunCase,\n} from './test-run-health';\n\n/**\n * Read the screenshot storage mode a report declared at generation time.\n *\n * Reports written by current versions stamp `data-screenshot-mode` onto every\n * dump script tag, so we only need to peek at the first real dump script (the\n * template's bundled JS also references the dump type string, hence the\n * `data-group-id` filter) and can stop streaming immediately.\n *\n * Returns undefined for legacy reports that predate the attribute or for\n * unreadable files, letting the caller fall back to a filesystem heuristic.\n */\nconst screenshotModeAttrRegExp = new RegExp(\n `${DATA_SCREENSHOT_MODE_ATTR}=\"(inline|directory)\"`,\n);\n\nfunction readDeclaredScreenshotMode(\n reportFilePath: string,\n): ScreenshotMode | undefined {\n let mode: ScreenshotMode | undefined;\n try {\n streamDumpScriptsSync(reportFilePath, ({ openTag }) => {\n // Skip false matches from the template's bundled JS code.\n if (!openTag.includes('data-group-id')) return false;\n const match = openTag.match(screenshotModeAttrRegExp);\n mode = match?.[1] as ScreenshotMode | undefined;\n return true; // the first real dump script decides the mode\n });\n } catch {\n // Unreadable / non-existent file — let the caller fall back.\n }\n return mode;\n}\n\n/**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n *\n * The mode is read from the report's own `data-screenshot-mode` metadata, which\n * is authoritative regardless of whether the run happened to capture any\n * screenshots. For legacy reports without the attribute we fall back to the old\n * filesystem heuristic (an `index.html` that has a sibling `screenshots/` dir).\n */\nexport function isDirectoryModeReport(reportFilePath: string): boolean {\n if (readReportArtifactManifest(reportFilePath)) return true;\n // Agent directory reports use index.html by default. Reports without the\n // versioned manifest use the existing metadata and layout heuristic.\n if (path.basename(reportFilePath) !== 'index.html') return false;\n\n const declared = readDeclaredScreenshotMode(reportFilePath);\n if (declared) return declared === 'directory';\n\n // Legacy fallback for reports generated before screenshotMode was embedded.\n return existsSync(path.join(path.dirname(reportFilePath), 'screenshots'));\n}\n\n/**\n * Whether a report lives in its own dedicated directory (`{name}/index.html`)\n * rather than being a single standalone file (`{name}.html`).\n *\n * This is a structural fact about *where the report file sits*, independent of\n * how it stores screenshots: a directory report can keep screenshots external\n * (a `screenshots/` sibling) OR inline them into `index.html`. Deletion needs\n * this — not the screenshot mode — to decide whether to remove the whole\n * directory or just unlink a file, so that an inline-screenshot report nested in\n * its own folder still has the folder removed instead of being orphaned.\n */\nfunction isDirectoryBasedReport(reportFilePath: string): boolean {\n return path.basename(reportFilePath) === 'index.html';\n}\n\n/**\n * Remove a complete report artifact from disk.\n *\n * Standalone reports own only their HTML file, while directory-based reports\n * own the directory containing `index.html`. Callers must decide whether they\n * own the source artifact and should only invoke this after its replacement\n * has been produced successfully.\n *\n * @param reportFilePath Path to the standalone report or directory report's\n * `index.html` entry point.\n * @throws When the owned report artifact cannot be removed.\n */\nexport function removeReportArtifact(reportFilePath: string): void {\n if (isDirectoryBasedReport(reportFilePath)) {\n rmSync(path.dirname(reportFilePath), { recursive: true, force: true });\n return;\n }\n unlinkSync(reportFilePath);\n}\n\n/**\n * Deduplicate executions by stable id, keeping only the last occurrence.\n * Old-format executions without id are always preserved.\n */\nexport function dedupeExecutionsKeepLatest<T extends Pick<ExecutionDump, 'id'>>(\n executions: T[],\n): T[] {\n let noIdCounter = 0;\n const deduped = new Map<string, T>();\n for (const exec of executions) {\n const key = exec.id || `__no_id_${noIdCounter++}`;\n deduped.set(key, exec);\n }\n return Array.from(deduped.values());\n}\n/**\n * Peek at the first `sdkVersion` field embedded in a midscene_web_dump\n * script tag inside the given report file. Returns undefined if no\n * recognizable tag or sdkVersion is present.\n */\nfunction peekReportSdkVersion(reportFilePath: string): string | undefined {\n try {\n let dump: string | undefined;\n streamReportDumpScripts(reportFilePath, ({ content }) => {\n dump = content;\n return true;\n });\n dump ??= extractLastDumpScriptSync(reportFilePath) ?? undefined;\n if (!dump) return undefined;\n const match = dump.match(/\"sdkVersion\"\\s*:\\s*\"([^\"]+)\"/);\n return match?.[1];\n } catch {\n return undefined;\n }\n}\n\nconst warnedMismatchedVersions = new Set<string>();\n\nfunction tryParseAgentReportDump(dumpString: string): ReportActionDump | null {\n const trimmed = dumpString.trimStart();\n if (!trimmed.startsWith('{') || !trimmed.includes('\"executions\"')) {\n return null;\n }\n\n try {\n return ReportActionDump.fromSerializedString(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction mergedAgentReportComment(reports: ReportActionDump[]): string {\n if (reports.length === 0) {\n return '';\n }\n if (reports.length === 1) {\n return generateAgentReportComment(reports[0]);\n }\n\n const deviceTypes = Array.from(\n new Set(reports.map((report) => report.deviceType).filter(Boolean)),\n );\n const mergedReport = new ReportActionDump({\n sdkVersion: reports[0].sdkVersion || getVersion(),\n groupName: 'Merged Midscene Report',\n groupDescription: 'Agent-readable summary for merged report HTML',\n modelBriefs: reports.flatMap((report) => report.modelBriefs ?? []),\n deviceType: deviceTypes.length === 1 ? deviceTypes[0] : 'mixed',\n executions: dedupeExecutionsKeepLatest(\n reports.flatMap((report) => report.executions ?? []),\n ),\n });\n return generateAgentReportComment(mergedReport);\n}\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n\n private createEmptyDumpString(groupName: string, groupDescription?: string) {\n return new ReportActionDump({\n sdkVersion: '',\n groupName,\n groupDescription,\n modelBriefs: [],\n executions: [],\n }).serialize();\n }\n\n public append(reportInfo: ReportFileWithAttributes) {\n if (\n !reportInfo.reportFilePath &&\n reportInfo.reportAttributes.testStatus !== 'skipped'\n ) {\n throw new Error(\n 'reportFilePath is required unless reportAttributes.testStatus is \"skipped\"',\n );\n }\n\n if (reportInfo.reportFilePath) {\n const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] ReportMergingTool version mismatch: source report was written by @midscene/core@${sourceVersion} but the merger is @midscene/core@${currentVersion}. This commonly means @midscene/core and the device package (e.g. @midscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`,\n );\n }\n }\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Merge multiple dump script contents (from the same source report)\n * into a single serialized ReportActionDump string.\n * If there's only one dump, return it as-is. If multiple, merge\n * all executions into the first dump's group structure.\n */\n private mergeDumpScripts(contents: string[]): string {\n const unescaped = contents\n .map((c) => antiEscapeScriptTag(c))\n .filter((c) => c.length > 0);\n if (unescaped.length === 0) return '';\n if (unescaped.length === 1) return unescaped[0];\n\n // Parse all dumps and collect executions, deduplicating by id (keep last).\n // Only executions with a stable id are deduped; old-format entries without\n // id are always kept (they may be distinct despite sharing the same name).\n const base = ReportActionDump.fromSerializedString(unescaped[0]);\n const allExecutions = [...base.executions];\n for (let i = 1; i < unescaped.length; i++) {\n const other = ReportActionDump.fromSerializedString(unescaped[i]);\n allExecutions.push(...other.executions);\n }\n base.executions = dedupeExecutionsKeepLatest(allExecutions);\n return base.serialize();\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts?: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n outputDir?: string;\n },\n ): string | null {\n const {\n rmOriginalReports = false,\n overwrite = false,\n outputDir,\n } = opts ?? {};\n\n if (this.reportInfos.length === 0) {\n logMsg('No reports to merge');\n return null;\n }\n\n const runnerDumps = this.reportInfos.map((info) =>\n info.reportFilePath\n ? readReportRunnerDump(info.reportFilePath)\n : undefined,\n );\n const hasRunnerDump = runnerDumps.some(Boolean);\n\n const targetDir = outputDir\n ? path.resolve(outputDir)\n : getMidsceneRunSubDir('report');\n if (outputDir) {\n mkdirSync(targetDir, { recursive: true });\n }\n\n // Resolve each report's screenshot mode exactly once. isDirectoryModeReport\n // reads the file to find the authoritative metadata, so recomputing it for\n // both the output-path decision and the per-report merge loop would re-scan\n // every report twice.\n const isDirModeByIndex = this.reportInfos.map((info) =>\n Boolean(\n info.reportFilePath && isDirectoryModeReport(info.reportFilePath),\n ),\n );\n const hasDirectoryModeReport = isDirModeByIndex.some(Boolean);\n\n const resolvedName =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n\n // Directory mode: output as {name}/index.html to keep relative paths working\n // Inline mode: output as {name}.html (single file)\n const outputFilePath = hasDirectoryModeReport\n ? path.resolve(targetDir, resolvedName, 'index.html')\n : path.resolve(targetDir, `${resolvedName}.html`);\n\n if (reportFileName !== 'AUTO' && existsSync(outputFilePath)) {\n if (!overwrite) {\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n }\n if (hasDirectoryModeReport) {\n rmSync(path.dirname(outputFilePath), { recursive: true, force: true });\n } else {\n unlinkSync(outputFilePath);\n }\n }\n\n if (hasDirectoryModeReport) {\n mkdirSync(path.dirname(outputFilePath), { recursive: true });\n }\n\n logMsg(\n `Start merging ${this.reportInfos.length} reports...\\nCreating template file...`,\n );\n\n try {\n // Write template without closing </html> tag so we can append\n // dump scripts before it. The closing tag is added at the end.\n const htmlEndTag = '</html>';\n const tpl = getReportTpl();\n const htmlEndIdx = tpl.lastIndexOf(htmlEndTag);\n const tplWithoutClose =\n htmlEndIdx !== -1 ? tpl.slice(0, htmlEndIdx) : tpl;\n appendFileSync(outputFilePath, tplWithoutClose);\n\n // For directory-mode output, inject base URL fix script\n if (hasDirectoryModeReport) {\n appendFileSync(outputFilePath, getBaseUrlFixScript());\n }\n\n const agentReports: ReportActionDump[] = [];\n const runnerSources: MergeTestRunReportSource[] = [];\n const writtenInlineImageIds = new Set<string>();\n\n // Process all reports one by one\n for (let i = 0; i < this.reportInfos.length; i++) {\n const reportInfo = this.reportInfos[i];\n logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);\n\n const { reportAttributes } = reportInfo;\n let dumpString = this.createEmptyDumpString(\n reportAttributes.testTitle,\n reportAttributes.testDescription,\n );\n let mergedGroupId = `merged-group-${i}`;\n\n if (reportInfo.reportFilePath) {\n if (isDirModeByIndex[i]) {\n // Directory mode: copy external screenshot files. A directory-mode\n // report can legitimately have no screenshots/ dir (a run that\n // captured nothing), so only copy when the source dir exists.\n const reportDir = path.dirname(reportInfo.reportFilePath);\n const screenshotsDir = path.join(reportDir, 'screenshots');\n if (existsSync(screenshotsDir)) {\n const mergedScreenshotsDir = path.join(\n path.dirname(outputFilePath),\n 'screenshots',\n );\n mkdirSync(mergedScreenshotsDir, { recursive: true });\n for (const file of readdirSync(screenshotsDir)) {\n const src = path.join(screenshotsDir, file);\n const dest = path.join(mergedScreenshotsDir, file);\n copyFileSync(src, dest);\n }\n }\n } else {\n // Inline mode: stream image scripts to output file\n streamImageScriptsToFile(\n reportInfo.reportFilePath,\n outputFilePath,\n writtenInlineImageIds,\n );\n }\n\n // Extract all dump scripts from the source report.\n // After the per-execution append refactor, a single source report\n // may contain multiple <script type=\"midscene_web_dump\"> tags\n // (one per execution). We merge them into a single ReportActionDump.\n // Filter by data-group-id to exclude false matches from the template's\n // bundled JS code, which also references the midscene_web_dump type string.\n const allDumps = readReportDumpScripts(\n reportInfo.reportFilePath,\n ).filter((d) => d.openTag.includes('data-group-id'));\n const groupIdMatch = allDumps[0]?.openTag.match(\n /data-group-id=\"([^\"]+)\"/,\n );\n if (groupIdMatch && !hasRunnerDump) {\n mergedGroupId = decodeURIComponent(groupIdMatch[1]);\n }\n const extractedDumpString =\n allDumps.length > 0\n ? this.mergeDumpScripts(allDumps.map((d) => d.content))\n : extractLastDumpScriptSync(reportInfo.reportFilePath);\n if (extractedDumpString) {\n dumpString = extractedDumpString;\n }\n }\n\n const agentReport = tryParseAgentReportDump(dumpString);\n if (agentReport) {\n agentReports.push(agentReport);\n }\n if (hasRunnerDump)\n runnerSources.push({\n reportId: mergedGroupId,\n sourcePath: reportInfo.reportFilePath ?? '<skipped-agent-report>',\n attributes: reportAttributes,\n dump: runnerDumps[i],\n timing: agentReport\n ? agentReportTiming(agentReport, reportAttributes.testDuration)\n : undefined,\n });\n\n const reportHtmlStr = `${reportHTMLContent(\n {\n dumpString,\n attributes: {\n 'data-group-id': mergedGroupId,\n ...(hasRunnerDump ? { 'data-report-id': mergedGroupId } : {}),\n [DATA_SCREENSHOT_MODE_ATTR]: hasDirectoryModeReport\n ? 'directory'\n : 'inline',\n playwright_test_duration: reportAttributes.testDuration,\n playwright_test_status: reportAttributes.testStatus,\n playwright_test_title: reportAttributes.testTitle,\n playwright_test_id: reportAttributes.testId,\n playwright_test_description: reportAttributes.testDescription,\n is_merged: true,\n },\n },\n undefined,\n undefined,\n false,\n )}\\n`;\n\n appendFileSync(outputFilePath, reportHtmlStr);\n }\n\n if (hasRunnerDump) {\n const executions = dedupeExecutionsKeepLatest(\n agentReports.flatMap((report) => report.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n appendFileSync(\n outputFilePath,\n runnerDumpScript(\n mergeTestRunReportDumps(\n runnerSources,\n metrics,\n uuid(),\n executionMetrics,\n ),\n ),\n );\n }\n\n const agentComment = mergedAgentReportComment(agentReports);\n if (agentComment) {\n appendFileSync(outputFilePath, agentComment);\n }\n\n // Close the HTML document\n appendFileSync(outputFilePath, `${htmlEndTag}\\n`);\n\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n\n // Remove original reports if needed\n if (rmOriginalReports) {\n let removedReportCount = 0;\n let reportCountWithFilePath = 0;\n for (const info of this.reportInfos) {\n if (!info.reportFilePath) continue;\n reportCountWithFilePath += 1;\n try {\n removeReportArtifact(info.reportFilePath);\n removedReportCount += 1;\n } catch (error) {\n logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);\n }\n }\n logMsg(\n `Removed ${removedReportCount}/${reportCountWithFilePath} original reports`,\n );\n }\n return outputFilePath;\n } catch (error) {\n logMsg(`Error in mergeReports: ${error}`);\n throw error;\n }\n }\n}\n\nfunction agentReportTiming(\n report: ReportActionDump,\n durationMs: number,\n): MergeTestRunReportSource['timing'] {\n const timing = collectReportSummary(report).timing;\n const executionTimes = report.executions\n .map((execution) => execution.logTime)\n .filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value),\n );\n const started =\n timing.wallTimeStart ??\n (executionTimes.length ? Math.min(...executionTimes) : undefined);\n if (started === undefined) return undefined;\n const ended =\n timing.wallTimeEnd ??\n Math.max(\n ...executionTimes,\n started + (Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0),\n );\n return {\n startedAt: new Date(started).toISOString(),\n endedAt: new Date(ended).toISOString(),\n durationMs: Math.max(0, ended - started),\n };\n}\n\ninterface PreparedTestRunReportSource extends IndexedTestRunReportSource {\n dump: ReportActionDump;\n dumpPath?: string;\n}\n\nconst validateTestRunReportFileName = (fileName: string): void => {\n if (\n !fileName ||\n fileName === '.' ||\n fileName === '..' ||\n path.basename(fileName) !== fileName ||\n fileName.endsWith('.html')\n ) {\n throw new Error(\n `Midscene Test reportFileName must be one path segment without an extension: ${fileName}`,\n );\n }\n};\n\nconst uniqueTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n): TestRunReportSource[] => {\n const unique = new Map<string, TestRunReportSource>();\n\n for (const source of sources) {\n const scopeId = source.scopeId.trim();\n const sourcePath = path.resolve(source.sourcePath);\n if (!scopeId) {\n throw new Error('Midscene Test report source scopeId must not be empty.');\n }\n\n unique.set(`${scopeId}\\u0000${source.dumpPath ?? sourcePath}`, {\n ...source,\n scopeId,\n sourcePath,\n });\n }\n\n return [...unique.values()];\n};\n\nconst prepareTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n loadedDumps?: ReadonlyMap<string, string | Error>,\n): {\n sources: PreparedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n capturedMetrics: TestRunReportMetrics;\n executionMetrics: Record<string, TestRunReportMetrics>;\n diagnostics: import('./test-run-report').TestRunReportDiagnostic[];\n} => {\n const prepared: PreparedTestRunReportSource[] = [];\n const diagnostics: import('./test-run-report').TestRunReportDiagnostic[] = [];\n const ownerByScopedExecution = new Map<string, string>();\n const preparedByPath = new Map<string, PreparedTestRunReportSource>();\n\n for (const [sourceIndex, source] of uniqueTestRunReportSources(\n sources,\n ).entries()) {\n let baseDump: ReportActionDump;\n let executions: IExecutionDump[];\n try {\n if (source.dumpPath) {\n const contents =\n loadedDumps?.get(source.dumpPath) ??\n readFileSync(source.dumpPath, 'utf8');\n if (contents instanceof Error) throw contents;\n baseDump = ReportActionDump.fromSerializedString(contents);\n executions = baseDump.executions;\n } else {\n if (!existsSync(source.sourcePath))\n throw new Error(`Agent report does not exist: ${source.sourcePath}`);\n const sourceVersion = peekReportSdkVersion(source.sourcePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] TestRunReportAssembler version mismatch: source report was written by @midscene/core@${sourceVersion} but the assembler is @midscene/core@${currentVersion}. Align the workspace package versions before generating the Midscene Test report.`,\n );\n }\n ({ baseDump, executions } = collectDedupedExecutions(\n source.sourcePath,\n ));\n }\n } catch (error) {\n diagnostics.push({\n level: 'warning',\n code: 'agent-source-unavailable',\n scopeId: source.scopeId,\n sourcePaths: [source.sourcePath],\n message: `Agent source could not be collected: ${String(error)}`,\n });\n continue;\n }\n // A borrowed Agent can serve several sequential test scopes. Reuse its\n // report group and metrics while indexing each scope's execution links.\n const sourceKey = source.sourcePath;\n const shared = preparedByPath.get(sourceKey);\n if (shared) {\n if (source.dumpPath) {\n shared.dump.executions = dedupeExecutionsKeepLatest([\n ...shared.dump.executions,\n ...baseDump.executions,\n ]);\n }\n const scoped = prepared.find(\n (item) =>\n item.reportId === shared.reportId && item.scopeId === source.scopeId,\n );\n const executionIds = [\n ...new Set([\n ...(scoped?.executionIds ?? []),\n ...executions.flatMap((execution) =>\n execution.id ? [execution.id] : [],\n ),\n ]),\n ];\n for (const executionId of executionIds) {\n const key = `${source.scopeId}\\u0000${executionId}`;\n const owner = ownerByScopedExecution.get(key);\n if (owner && owner !== shared.reportId)\n throw new Error(\n `Agent execution ${executionId} is present in multiple reports for test scope ${source.scopeId}: ${owner}, ${shared.reportId}`,\n );\n ownerByScopedExecution.set(key, shared.reportId);\n }\n if (scoped) {\n scoped.executionIds = executionIds;\n const partial = [scoped.collection, source.collection].find(\n (collection) => collection?.status === 'partial',\n );\n scoped.collection = partial ?? source.collection ?? scoped.collection;\n } else\n prepared.push({\n ...shared,\n scopeId: source.scopeId,\n executionIds,\n collection: source.collection,\n });\n continue;\n }\n const reportId = `runner-report-${sourceIndex + 1}`;\n const executionIds: string[] = [];\n for (const execution of executions) {\n if (!execution.id) continue;\n const scopedExecution = `${source.scopeId}\\u0000${execution.id}`;\n const previousOwner = ownerByScopedExecution.get(scopedExecution);\n if (previousOwner && previousOwner !== reportId) {\n throw new Error(\n `Agent execution ${execution.id} is present in multiple reports for test scope ${source.scopeId}: ${previousOwner}, ${reportId}`,\n );\n }\n ownerByScopedExecution.set(scopedExecution, reportId);\n executionIds.push(execution.id);\n }\n\n const dump = 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 const preparedSource = {\n reportId,\n scopeId: source.scopeId,\n sourcePath: source.sourcePath,\n executionIds,\n collection: source.collection,\n dump,\n ...(source.dumpPath ? { dumpPath: source.dumpPath } : {}),\n };\n prepared.push(preparedSource);\n preparedByPath.set(sourceKey, preparedSource);\n }\n\n // A legacy invocation can preserve a snapshot of an Agent also used by\n // native scopes. Count stable executions once across both paths.\n const executions = dedupeExecutionsKeepLatest(\n [...preparedByPath.values()].flatMap((source) => source.dump.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n return {\n sources: prepared,\n diagnostics,\n capturedMetrics: metrics,\n executionMetrics,\n metrics: {\n ...metrics,\n collection: {\n ...metrics.collection!,\n fields: Object.fromEntries(\n Object.entries(metrics.collection!.fields!).map(([field, status]) => [\n field,\n diagnostics.length\n ? prepared.length === 0\n ? 'unknown'\n : status === 'complete'\n ? 'partial'\n : status\n : status,\n ]),\n ) as NonNullable<\n NonNullable<TestRunReportMetrics['collection']>['fields']\n >,\n status: diagnostics.length\n ? prepared.length\n ? 'partial'\n : 'unknown'\n : metrics.collection!.status,\n expectedExecutionCount: 0,\n capturedExecutionCount: 0,\n reasons: [\n ...metrics.collection!.reasons,\n ...diagnostics.map((diagnostic) => diagnostic.message),\n ],\n },\n },\n };\n};\n\n/**\n * Build a self-contained Midscene Test report while preserving the existing\n * Agent dump format consumed by the Report App.\n *\n * Test hierarchy and status live in `midscene_test_run_dump`; Agent reports\n * remain independent `midscene_web_dump` groups addressed by `data-report-id`.\n */\nexport class TestRunReportAssembler {\n /** Runtime publisher: current Agent snapshots avoid parsing HTML or copying unused images. */\n public async assembleAsync(\n options: AssembleTestRunReportOptions,\n ): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const loaded = new Map<string, string | Error>();\n await Promise.all(\n options.sources.map(async (source) => {\n if (source.dumpPath) {\n try {\n loaded.set(\n source.dumpPath,\n await fsAsync.readFile(source.dumpPath, 'utf8'),\n );\n } catch (error) {\n loaded.set(\n source.dumpPath,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }),\n );\n const prepared = prepareTestRunReportSources(options.sources, loaded);\n const dump = options.buildRunnerDump({\n sources: prepared.sources,\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n });\n if (dump.kind !== 'test-runner' || dump.schemaVersion !== 1)\n throw new Error('Invalid Runner report dump');\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const outputDir = path.resolve(options.outputDir);\n await fsAsync.mkdir(outputDir, { recursive: true });\n const artifact = path.join(outputDir, options.reportFileName);\n const finalPath = directoryMode\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (\n !options.overwrite &&\n (existsSync(artifact) || existsSync(`${artifact}.html`))\n )\n throw new Error(`Report artifact already exists: ${artifact}`);\n const temporary = await fsAsync.mkdtemp(\n path.join(outputDir, '.test-run-report-'),\n );\n const htmlPath = path.join(temporary, 'index.html');\n try {\n await fsAsync.writeFile(htmlPath, getReportTpl());\n if (directoryMode)\n await fsAsync.appendFile(htmlPath, getBaseUrlFixScript());\n const images = new TestReportImages(htmlPath, directoryMode);\n const reports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n const serialized = await images.rewriteAsync(\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n );\n reports.push(source.dump);\n await fsAsync.appendFile(\n htmlPath,\n reportHTMLContent(\n {\n dumpString: serialized,\n attributes: {\n 'data-group-id': source.reportId,\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n [DATA_SCREENSHOT_MODE_ATTR]: directoryMode\n ? 'directory'\n : 'inline',\n },\n },\n undefined,\n undefined,\n false,\n ),\n );\n }\n await fsAsync.appendFile(\n htmlPath,\n runnerDumpScript(dump) + mergedAgentReportComment(reports),\n );\n if (options.overwrite) {\n await fsAsync.rm(artifact, { recursive: true, force: true });\n await fsAsync.rm(`${artifact}.html`, { force: true });\n }\n await fsAsync.rename(\n directoryMode ? temporary : htmlPath,\n directoryMode ? artifact : finalPath,\n );\n return finalPath;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n }\n\n public assemble(options: AssembleTestRunReportOptions): string {\n validateTestRunReportFileName(options.reportFileName);\n const outputDir = path.resolve(options.outputDir);\n mkdirSync(outputDir, { recursive: true });\n\n const prepared = prepareTestRunReportSources(options.sources);\n const index: TestRunReportSourceIndex = {\n sources: prepared.sources.map(\n ({ reportId, scopeId, sourcePath, executionIds, collection }) => ({\n reportId,\n scopeId,\n sourcePath,\n executionIds,\n collection,\n }),\n ),\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n };\n const runnerDump = options.buildRunnerDump(index);\n if (runnerDump.schemaVersion !== 1 || runnerDump.kind !== 'test-runner') {\n throw new Error(\n 'buildRunnerDump must return a Midscene Test report dump with schemaVersion 1 and kind \"test-runner\".',\n );\n }\n // Validate serializability before creating any output artifact.\n JSON.stringify(runnerDump);\n\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const finalArtifactPath = path.join(outputDir, options.reportFileName);\n const directoryReportPath = path.join(finalArtifactPath, 'index.html');\n const standaloneReportPath = `${finalArtifactPath}.html`;\n const finalReportPath = directoryMode\n ? directoryReportPath\n : standaloneReportPath;\n const existingArtifacts = [finalArtifactPath, standaloneReportPath].filter(\n existsSync,\n );\n if (existingArtifacts.length > 0 && !options.overwrite) {\n throw new Error(\n `Report artifact already exists: ${existingArtifacts[0]}\\nSet overwrite to true to overwrite this report.`,\n );\n }\n\n const temporaryRoot = mkdtempSync(\n path.join(outputDir, '.test-run-report-'),\n );\n const temporaryArtifactPath = path.join(\n temporaryRoot,\n options.reportFileName,\n );\n const temporaryReportPath = directoryMode\n ? path.join(temporaryArtifactPath, 'index.html')\n : `${temporaryArtifactPath}.html`;\n\n try {\n if (directoryMode) {\n mkdirSync(temporaryArtifactPath, { recursive: true });\n }\n\n const htmlEndTag = '</html>';\n const template = getReportTpl();\n const htmlEndIndex = template.lastIndexOf(htmlEndTag);\n appendFileSync(\n temporaryReportPath,\n htmlEndIndex === -1 ? template : template.slice(0, htmlEndIndex),\n );\n if (directoryMode) {\n appendFileSync(temporaryReportPath, getBaseUrlFixScript());\n }\n\n const images = new TestReportImages(temporaryReportPath, directoryMode);\n const agentReports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n const serialized = images.rewriteSync(\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n );\n\n agentReports.push(source.dump);\n appendFileSync(\n temporaryReportPath,\n `${reportHTMLContent(\n {\n dumpString: serialized,\n attributes: {\n 'data-group-id': source.reportId,\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n [DATA_SCREENSHOT_MODE_ATTR]: directoryMode\n ? 'directory'\n : 'inline',\n },\n },\n undefined,\n undefined,\n false,\n )}\\n`,\n );\n }\n\n appendFileSync(temporaryReportPath, runnerDumpScript(runnerDump));\n const agentComment = mergedAgentReportComment(agentReports);\n if (agentComment) {\n appendFileSync(temporaryReportPath, agentComment);\n }\n appendFileSync(temporaryReportPath, `${htmlEndTag}\\n`);\n\n if (existsSync(finalArtifactPath)) {\n rmSync(finalArtifactPath, { recursive: true, force: true });\n }\n if (existsSync(standaloneReportPath)) {\n unlinkSync(standaloneReportPath);\n }\n renameSync(\n directoryMode ? temporaryArtifactPath : temporaryReportPath,\n directoryMode ? finalArtifactPath : standaloneReportPath,\n );\n rmSync(temporaryRoot, { recursive: true, force: true });\n return finalReportPath;\n } catch (error) {\n rmSync(temporaryRoot, { recursive: true, force: true });\n throw error;\n }\n }\n}\n\nexport interface SplitReportHtmlOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport interface SplitReportHtmlResult {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n}\n\nexport interface CollectedReportExecutions {\n baseDump: ReportActionDump;\n executions: IExecutionDump[];\n}\n\n/**\n * Collect executions from a report HTML, deduplicating by stable id while\n * keeping only the latest occurrence. Old-format executions without id are\n * always preserved.\n */\nexport function collectDedupedExecutions(\n htmlPath: string,\n): CollectedReportExecutions {\n let baseDump: ReportActionDump | null = null;\n let executionSerial = 0;\n const latestSerialByExecutionId = new Map<string, number>();\n\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (execution.id) {\n latestSerialByExecutionId.set(execution.id, executionSerial);\n }\n }\n return false;\n });\n\n const executions: IExecutionDump[] = [];\n executionSerial = 0;\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n if (!baseDump) {\n baseDump = groupedDump;\n }\n\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (\n execution.id &&\n latestSerialByExecutionId.get(execution.id) !== executionSerial\n ) {\n continue;\n }\n executions.push(execution);\n }\n\n return false;\n });\n\n if (!baseDump) {\n throw new Error(`No report dump scripts found in ${htmlPath}`);\n }\n\n return {\n baseDump,\n executions,\n };\n}\n\nfunction externalizeImagesInExecution(\n execution: IExecutionDump,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const visit = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) {\n visit(item);\n }\n return;\n }\n\n if (typeof node !== 'object' || node === null) return;\n\n const ref = normalizeStoredImageRef(node);\n if (ref) {\n const ext = imageRefFileExtension(ref);\n const fileName = `${ref.id}.${ext}`;\n const relativePath = `./screenshots/${fileName}`;\n const absolutePath = path.join(opts.screenshotsDir, fileName);\n\n if (!opts.writtenFiles.has(fileName)) {\n const resolved = resolveImageSource(ref, {\n reportPath: opts.htmlPath,\n });\n if (resolved.type === 'data-uri') {\n const { rawBase64 } = parseBase64ImageDataUrl(resolved.dataUri);\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n } else {\n copyFileSync(resolved.filePath, absolutePath);\n }\n opts.writtenFiles.add(fileName);\n }\n\n ref.storage = 'file';\n ref.path = relativePath;\n return;\n }\n\n for (const value of Object.values(node)) {\n visit(value);\n }\n };\n\n visit(execution);\n}\n\n/**\n * Reverse parse a Midscene report HTML into per-execution JSON files and\n * externalized screenshots.\n */\nexport function splitReportHtmlByExecution(\n options: SplitReportHtmlOptions,\n): SplitReportHtmlResult {\n const { htmlPath, outputDir } = options;\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const executionJsonFiles: string[] = [];\n const writtenScreenshotFiles = new Set<string>();\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n let fileIndex = 0;\n for (const execution of executions) {\n fileIndex += 1;\n externalizeImagesInExecution(execution, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshotFiles,\n });\n const singleExecutionDump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions: [execution],\n });\n\n const jsonFilePath = path.join(outputDir, `${fileIndex}.execution.json`);\n writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');\n executionJsonFiles.push(jsonFilePath);\n }\n\n return {\n executionJsonFiles,\n screenshotFiles: Array.from(writtenScreenshotFiles)\n .sort()\n .map((fileName) => path.join(screenshotsDir, fileName)),\n };\n}\n"],"names":["screenshotModeAttrRegExp","RegExp","DATA_SCREENSHOT_MODE_ATTR","readDeclaredScreenshotMode","reportFilePath","mode","streamDumpScriptsSync","openTag","match","isDirectoryModeReport","readReportArtifactManifest","path","declared","existsSync","isDirectoryBasedReport","removeReportArtifact","rmSync","unlinkSync","dedupeExecutionsKeepLatest","executions","noIdCounter","deduped","Map","exec","key","Array","peekReportSdkVersion","dump","streamReportDumpScripts","content","extractLastDumpScriptSync","undefined","warnedMismatchedVersions","Set","tryParseAgentReportDump","dumpString","trimmed","ReportActionDump","mergedAgentReportComment","reports","generateAgentReportComment","deviceTypes","report","Boolean","mergedReport","getVersion","ReportMergingTool","groupName","groupDescription","reportInfo","Error","sourceVersion","currentVersion","logMsg","contents","unescaped","c","antiEscapeScriptTag","base","allExecutions","i","other","reportFileName","opts","rmOriginalReports","overwrite","outputDir","runnerDumps","info","readReportRunnerDump","hasRunnerDump","targetDir","getMidsceneRunSubDir","mkdirSync","isDirModeByIndex","hasDirectoryModeReport","resolvedName","getReportFileName","outputFilePath","htmlEndTag","tpl","getReportTpl","htmlEndIdx","tplWithoutClose","appendFileSync","getBaseUrlFixScript","agentReports","runnerSources","writtenInlineImageIds","reportAttributes","mergedGroupId","reportDir","screenshotsDir","mergedScreenshotsDir","file","readdirSync","src","dest","copyFileSync","streamImageScriptsToFile","allDumps","readReportDumpScripts","d","groupIdMatch","decodeURIComponent","extractedDumpString","agentReport","agentReportTiming","reportHtmlStr","reportHTMLContent","metrics","collectTestRunReportMetrics","executionMetrics","Object","execution","runnerDumpScript","mergeTestRunReportDumps","uuid","agentComment","removedReportCount","reportCountWithFilePath","error","durationMs","timing","collectReportSummary","executionTimes","value","Number","started","Math","ended","Date","validateTestRunReportFileName","fileName","uniqueTestRunReportSources","sources","unique","source","scopeId","sourcePath","prepareTestRunReportSources","loadedDumps","prepared","diagnostics","ownerByScopedExecution","preparedByPath","sourceIndex","baseDump","readFileSync","collectDedupedExecutions","String","sourceKey","shared","scoped","item","executionIds","executionId","owner","partial","collection","reportId","scopedExecution","previousOwner","preparedSource","field","status","diagnostic","TestRunReportAssembler","options","loaded","Promise","fsAsync","directoryMode","artifact","finalPath","temporary","htmlPath","images","TestReportImages","serialized","index","runnerDump","JSON","finalArtifactPath","directoryReportPath","standaloneReportPath","finalReportPath","existingArtifacts","temporaryRoot","mkdtempSync","temporaryArtifactPath","temporaryReportPath","template","htmlEndIndex","renameSync","executionSerial","latestSerialByExecutionId","dumpScript","groupedDump","externalizeImagesInExecution","visit","node","ref","normalizeStoredImageRef","ext","imageRefFileExtension","relativePath","absolutePath","resolved","resolveImageSource","rawBase64","parseBase64ImageDataUrl","writeFileSync","Buffer","splitReportHtmlByExecution","executionJsonFiles","writtenScreenshotFiles","fileIndex","singleExecutionDump","jsonFilePath"],"mappings":"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"}
1
+ {"version":3,"file":"report.mjs","sources":["../../src/report.ts"],"sourcesContent":["import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport * as fsAsync from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg, uuid } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent/report-file-name';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n extractLastDumpScriptSync,\n generateAgentReportComment,\n getBaseUrlFixScript,\n generateTestRunReportScriptTag as runnerDumpScript,\n streamDumpScriptsSync,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport {\n imageRefFileExtension,\n normalizeStoredImageRef,\n} from './dump/image-reference';\nimport {\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nimport {\n parseBase64ImageDataUrl,\n resolveImageSource,\n} from './dump/screenshot-store';\nexport {\n readReportActionDump,\n readReportArtifactManifestAsync,\n readReportArtifactManifest,\n readReportDumpScripts,\n readReportRunnerDump,\n streamReportDumpScripts,\n} from './dump/report-artifact-reader';\nexport { ExecutionRecorder } from './dump/execution-recorder';\nexport { ReportArtifactStore } from './dump/report-artifact-store';\nexport { ReportSession } from './dump/report-session';\nimport {\n publishReportArtifact,\n publishReportArtifactSync,\n} from './dump/report-artifact-publication';\nimport {\n ReportArtifactWriter,\n type ReportGroupAttributes,\n} from './dump/report-artifact-writer';\nimport { collectReportSummary } from './report-stats';\nimport type {\n AssembleTestRunReportOptions,\n IndexedTestRunReportSource,\n TestRunReportMetrics,\n TestRunReportSource,\n TestRunReportSourceIndex,\n} from './test-run-report';\nimport {\n type MergeTestRunReportSource,\n mergeTestRunReportDumps,\n} from './test-runner/reporting/merge-report';\nimport { collectTestRunReportMetrics } from './test-runner/reporting/metrics';\nimport {\n type ExecutionDump,\n type IExecutionDump,\n ReportActionDump,\n type ScreenshotMode,\n} from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, getVersion, reportHTMLContent } from './utils';\n\nexport {\n calculateTestRunHealth,\n classifyTestRunCase,\n} from './test-run-health';\n\nexport interface ExportReportArtifactOptions {\n reportPath: string;\n outputDir: string;\n reportFileName: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n overwrite?: boolean;\n}\n\n/** Export only referenced facts and images, validating them before publishing. */\nexport async function exportReportArtifact(\n options: ExportReportArtifactOptions,\n): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const input = path.resolve(options.reportPath);\n const output = path.resolve(options.outputDir);\n const external = options.outputFormat === 'html-and-external-assets';\n const artifact = path.join(output, options.reportFileName);\n const destination = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (\n input === destination ||\n (external && input.startsWith(`${artifact}${path.sep}`))\n )\n throw new Error('Export destination must differ from the input report.');\n if (!options.overwrite && existsSync(external ? artifact : destination))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const manifest = readReportArtifactManifest(input);\n const runner = readReportRunnerDump(input);\n const groups = new Map<\n string,\n { dump: ReportActionDump; attributes: ReportGroupAttributes }\n >();\n for (const source of readReportDumpScripts(input)) {\n const attributes = Object.fromEntries(\n [...source.openTag.matchAll(/([^\\s=]+)=\"([^\"]*)\"/g)]\n .filter(([, key]) => key !== 'type')\n .map(([, key, value]) => [key, decodeURIComponent(value)]),\n );\n const id = attributes['data-group-id'];\n if (!id) throw new Error('Report execution group is missing its ID.');\n const dump = ReportActionDump.fromSerializedString(\n manifest ? source.content : antiEscapeScriptTag(source.content),\n );\n const previous = groups.get(id);\n dump.executions = dedupeExecutionsKeepLatest([\n ...(previous?.dump.executions ?? []),\n ...dump.executions,\n ]);\n groups.set(id, { dump, attributes });\n }\n if (!groups.size && !runner)\n throw new Error('No report execution data was found in the input.');\n const hint = [...groups.values()][0]?.attributes['data-report-kind'];\n const kind =\n manifest?.kind ??\n (hint === 'agent' || hint === 'test' ? hint : runner ? 'test' : 'agent');\n await fsAsync.mkdir(output, { recursive: true });\n const temporary = await fsAsync.mkdtemp(path.join(output, '.report-export-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n kind,\n manifest?.reportId ?? runner?.runId,\n );\n await writer.initialize();\n for (const [id, group] of groups)\n await writer.addGroup(\n id,\n group.dump.serialize(),\n input,\n group.attributes,\n );\n await writer.finish(\n runner,\n mergedAgentReportComment([...groups.values()].map((group) => group.dump)),\n );\n await publishReportArtifact(\n external ? temporary : entry,\n external ? artifact : destination,\n external,\n options.overwrite,\n );\n return destination;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n}\n\n/**\n * Read the screenshot storage mode a report declared at generation time.\n *\n * Reports written by current versions stamp `data-screenshot-mode` onto every\n * dump script tag, so we only need to peek at the first real dump script (the\n * template's bundled JS also references the dump type string, hence the\n * `data-group-id` filter) and can stop streaming immediately.\n *\n * Returns undefined for legacy reports that predate the attribute or for\n * unreadable files, letting the caller fall back to a filesystem heuristic.\n */\nconst screenshotModeAttrRegExp = new RegExp(\n `${DATA_SCREENSHOT_MODE_ATTR}=\"(inline|directory)\"`,\n);\n\nfunction readDeclaredScreenshotMode(\n reportFilePath: string,\n): ScreenshotMode | undefined {\n let mode: ScreenshotMode | undefined;\n try {\n streamDumpScriptsSync(reportFilePath, ({ openTag }) => {\n // Skip false matches from the template's bundled JS code.\n if (!openTag.includes('data-group-id')) return false;\n const match = openTag.match(screenshotModeAttrRegExp);\n mode = match?.[1] as ScreenshotMode | undefined;\n return true; // the first real dump script decides the mode\n });\n } catch {\n // Unreadable / non-existent file — let the caller fall back.\n }\n return mode;\n}\n\n/**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n *\n * The mode is read from the report's own `data-screenshot-mode` metadata, which\n * is authoritative regardless of whether the run happened to capture any\n * screenshots. For legacy reports without the attribute we fall back to the old\n * filesystem heuristic (an `index.html` that has a sibling `screenshots/` dir).\n */\nexport function isDirectoryModeReport(reportFilePath: string): boolean {\n if (readReportArtifactManifest(reportFilePath)) return true;\n // Agent directory reports use index.html by default. Reports without the\n // versioned manifest use the existing metadata and layout heuristic.\n if (path.basename(reportFilePath) !== 'index.html') return false;\n\n const declared = readDeclaredScreenshotMode(reportFilePath);\n if (declared) return declared === 'directory';\n\n // Legacy fallback for reports generated before screenshotMode was embedded.\n return existsSync(path.join(path.dirname(reportFilePath), 'screenshots'));\n}\n\n/**\n * Whether a report lives in its own dedicated directory (`{name}/index.html`)\n * rather than being a single standalone file (`{name}.html`).\n *\n * This is a structural fact about *where the report file sits*, independent of\n * how it stores screenshots: a directory report can keep screenshots external\n * (a `screenshots/` sibling) OR inline them into `index.html`. Deletion needs\n * this — not the screenshot mode — to decide whether to remove the whole\n * directory or just unlink a file, so that an inline-screenshot report nested in\n * its own folder still has the folder removed instead of being orphaned.\n */\nfunction isDirectoryBasedReport(reportFilePath: string): boolean {\n return path.basename(reportFilePath) === 'index.html';\n}\n\n/**\n * Remove a complete report artifact from disk.\n *\n * Standalone reports own only their HTML file, while directory-based reports\n * own the directory containing `index.html`. Callers must decide whether they\n * own the source artifact and should only invoke this after its replacement\n * has been produced successfully.\n *\n * @param reportFilePath Path to the standalone report or directory report's\n * `index.html` entry point.\n * @throws When the owned report artifact cannot be removed.\n */\nexport function removeReportArtifact(reportFilePath: string): void {\n if (isDirectoryBasedReport(reportFilePath)) {\n rmSync(path.dirname(reportFilePath), { recursive: true, force: true });\n return;\n }\n unlinkSync(reportFilePath);\n}\n\n/**\n * Deduplicate executions by stable id, keeping only the last occurrence.\n * Old-format executions without id are always preserved.\n */\nexport function dedupeExecutionsKeepLatest<T extends Pick<ExecutionDump, 'id'>>(\n executions: T[],\n): T[] {\n let noIdCounter = 0;\n const deduped = new Map<string, T>();\n for (const exec of executions) {\n const key = exec.id || `__no_id_${noIdCounter++}`;\n deduped.set(key, exec);\n }\n return Array.from(deduped.values());\n}\n/**\n * Peek at the first `sdkVersion` field embedded in a midscene_web_dump\n * script tag inside the given report file. Returns undefined if no\n * recognizable tag or sdkVersion is present.\n */\nfunction peekReportSdkVersion(reportFilePath: string): string | undefined {\n try {\n let dump: string | undefined;\n streamReportDumpScripts(reportFilePath, ({ content }) => {\n dump = content;\n return true;\n });\n dump ??= extractLastDumpScriptSync(reportFilePath) ?? undefined;\n if (!dump) return undefined;\n const match = dump.match(/\"sdkVersion\"\\s*:\\s*\"([^\"]+)\"/);\n return match?.[1];\n } catch {\n return undefined;\n }\n}\n\nconst warnedMismatchedVersions = new Set<string>();\n\nfunction tryParseAgentReportDump(dumpString: string): ReportActionDump | null {\n const trimmed = dumpString.trimStart();\n if (!trimmed.startsWith('{') || !trimmed.includes('\"executions\"')) {\n return null;\n }\n\n try {\n return ReportActionDump.fromSerializedString(trimmed);\n } catch {\n return null;\n }\n}\n\nfunction mergedAgentReportComment(reports: ReportActionDump[]): string {\n if (reports.length === 0) {\n return '';\n }\n if (reports.length === 1) {\n return generateAgentReportComment(reports[0]);\n }\n\n const deviceTypes = Array.from(\n new Set(reports.map((report) => report.deviceType).filter(Boolean)),\n );\n const mergedReport = new ReportActionDump({\n sdkVersion: reports[0].sdkVersion || getVersion(),\n groupName: 'Merged Midscene Report',\n groupDescription: 'Agent-readable summary for merged report HTML',\n modelBriefs: reports.flatMap((report) => report.modelBriefs ?? []),\n deviceType: deviceTypes.length === 1 ? deviceTypes[0] : 'mixed',\n executions: dedupeExecutionsKeepLatest(\n reports.flatMap((report) => report.executions ?? []),\n ),\n });\n return generateAgentReportComment(mergedReport);\n}\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n\n private createEmptyDumpString(groupName: string, groupDescription?: string) {\n return new ReportActionDump({\n sdkVersion: '',\n groupName,\n groupDescription,\n modelBriefs: [],\n executions: [],\n }).serialize();\n }\n\n public append(reportInfo: ReportFileWithAttributes) {\n if (\n !reportInfo.reportFilePath &&\n reportInfo.reportAttributes.testStatus !== 'skipped'\n ) {\n throw new Error(\n 'reportFilePath is required unless reportAttributes.testStatus is \"skipped\"',\n );\n }\n\n if (reportInfo.reportFilePath) {\n const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] ReportMergingTool version mismatch: source report was written by @midscene/core@${sourceVersion} but the merger is @midscene/core@${currentVersion}. This commonly means @midscene/core and the device package (e.g. @midscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`,\n );\n }\n }\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Merge multiple dump script contents (from the same source report)\n * into a single serialized ReportActionDump string.\n * If there's only one dump, return it as-is. If multiple, merge\n * all executions into the first dump's group structure.\n */\n private mergeDumpScripts(contents: string[], external: boolean): string {\n const unescaped = contents\n .map((c) => (external ? c : antiEscapeScriptTag(c)))\n .filter((c) => c.length > 0);\n if (unescaped.length === 0) return '';\n if (unescaped.length === 1) return unescaped[0];\n\n // Parse all dumps and collect executions, deduplicating by id (keep last).\n // Only executions with a stable id are deduped; old-format entries without\n // id are always kept (they may be distinct despite sharing the same name).\n const base = ReportActionDump.fromSerializedString(unescaped[0]);\n const allExecutions = [...base.executions];\n for (let i = 1; i < unescaped.length; i++) {\n const other = ReportActionDump.fromSerializedString(unescaped[i]);\n allExecutions.push(...other.executions);\n }\n base.executions = dedupeExecutionsKeepLatest(allExecutions);\n return base.serialize();\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n outputDir?: string;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n } = {},\n ): string | null {\n const {\n rmOriginalReports = false,\n overwrite = false,\n outputDir,\n outputFormat,\n } = opts;\n if (!this.reportInfos.length) {\n logMsg('No reports to merge');\n return null;\n }\n const runnerDumps = this.reportInfos.map((info) =>\n info.reportFilePath\n ? readReportRunnerDump(info.reportFilePath)\n : undefined,\n );\n const hasRunnerDump = runnerDumps.some(Boolean);\n const external = outputFormat === 'html-and-external-assets';\n const name =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n validateTestRunReportFileName(name);\n const targetDir = outputDir\n ? path.resolve(outputDir)\n : getMidsceneRunSubDir('report');\n mkdirSync(targetDir, { recursive: true });\n const artifact = path.join(targetDir, name);\n const outputFilePath = external\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!overwrite && existsSync(external ? artifact : outputFilePath))\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n if (\n rmOriginalReports &&\n this.reportInfos.some(\n (info) =>\n info.reportFilePath &&\n isDirectoryBasedReport(info.reportFilePath) &&\n outputFilePath.startsWith(\n `${path.dirname(path.resolve(info.reportFilePath))}${path.sep}`,\n ),\n )\n )\n throw new Error(\n 'Merged output must be outside source report directories when removing originals.',\n );\n const temporary = mkdtempSync(path.join(targetDir, '.merged-report-'));\n const entry = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n entry,\n external,\n hasRunnerDump ? 'test' : 'agent',\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n const runnerSources: MergeTestRunReportSource[] = [];\n const groupIds = new Set<string>();\n for (const [index, info] of this.reportInfos.entries()) {\n const attributes = info.reportAttributes;\n let serialized = this.createEmptyDumpString(\n attributes.testTitle,\n attributes.testDescription,\n );\n let groupId = `merged-group-${index}`;\n if (info.reportFilePath) {\n const dumps = readReportDumpScripts(info.reportFilePath);\n const originalId = dumps[0]?.openTag.match(\n /data-group-id=\"([^\"]+)\"/,\n )?.[1];\n if (\n originalId &&\n !hasRunnerDump &&\n !groupIds.has(decodeURIComponent(originalId))\n )\n groupId = decodeURIComponent(originalId);\n serialized = dumps.length\n ? this.mergeDumpScripts(\n dumps.map((dump) => dump.content),\n Boolean(readReportArtifactManifest(info.reportFilePath)),\n )\n : (extractLastDumpScriptSync(info.reportFilePath) ?? serialized);\n }\n if (groupIds.has(groupId)) groupId = `merged-group-${uuid()}`;\n groupIds.add(groupId);\n const agentReport = tryParseAgentReportDump(serialized);\n if (agentReport) agentReports.push(agentReport);\n if (hasRunnerDump)\n runnerSources.push({\n reportId: groupId,\n sourcePath: info.reportFilePath ?? '<skipped-agent-report>',\n attributes,\n dump: runnerDumps[index],\n timing: agentReport\n ? agentReportTiming(agentReport, attributes.testDuration)\n : undefined,\n });\n writer.addGroupSync(groupId, serialized, info.reportFilePath ?? entry, {\n ...(hasRunnerDump ? { 'data-report-id': groupId } : {}),\n playwright_test_duration: attributes.testDuration,\n playwright_test_status: attributes.testStatus,\n playwright_test_title: attributes.testTitle,\n playwright_test_id: attributes.testId,\n playwright_test_description: attributes.testDescription,\n is_merged: true,\n });\n }\n let runner;\n if (hasRunnerDump) {\n const executions = dedupeExecutionsKeepLatest(\n agentReports.flatMap((report) => report.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n runner = mergeTestRunReportDumps(\n runnerSources,\n metrics,\n uuid(),\n executionMetrics,\n );\n }\n writer.finishSync(runner, mergedAgentReportComment(agentReports));\n publishReportArtifactSync(\n external ? temporary : entry,\n external ? artifact : outputFilePath,\n external,\n overwrite,\n );\n if (rmOriginalReports)\n for (const sourcePath of new Set(\n this.reportInfos.flatMap((info) =>\n info.reportFilePath ? [path.resolve(info.reportFilePath)] : [],\n ),\n )) {\n if (sourcePath !== outputFilePath) removeReportArtifact(sourcePath);\n }\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n return outputFilePath;\n } finally {\n rmSync(temporary, { recursive: true, force: true });\n }\n }\n}\n\nfunction agentReportTiming(\n report: ReportActionDump,\n durationMs: number,\n): MergeTestRunReportSource['timing'] {\n const timing = collectReportSummary(report).timing;\n const executionTimes = report.executions\n .map((execution) => execution.logTime)\n .filter(\n (value): value is number =>\n typeof value === 'number' && Number.isFinite(value),\n );\n const started =\n timing.wallTimeStart ??\n (executionTimes.length ? Math.min(...executionTimes) : undefined);\n if (started === undefined) return undefined;\n const ended =\n timing.wallTimeEnd ??\n Math.max(\n ...executionTimes,\n started + (Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0),\n );\n return {\n startedAt: new Date(started).toISOString(),\n endedAt: new Date(ended).toISOString(),\n durationMs: Math.max(0, ended - started),\n };\n}\n\ninterface PreparedTestRunReportSource extends IndexedTestRunReportSource {\n dump: ReportActionDump;\n dumpPath?: string;\n imageOrigins: Map<string, string>;\n}\n\nconst validateTestRunReportFileName = (fileName: string): void => {\n if (\n !fileName ||\n fileName === '.' ||\n fileName === '..' ||\n path.basename(fileName) !== fileName ||\n fileName.endsWith('.html')\n ) {\n throw new Error(\n `Midscene Test reportFileName must be one path segment without an extension: ${fileName}`,\n );\n }\n};\n\nconst uniqueTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n): TestRunReportSource[] => {\n const unique = new Map<string, TestRunReportSource>();\n\n for (const source of sources) {\n const scopeId = source.scopeId.trim();\n const sourcePath = path.resolve(source.sourcePath);\n if (!scopeId) {\n throw new Error('Midscene Test report source scopeId must not be empty.');\n }\n\n unique.set(`${scopeId}\\u0000${source.dumpPath ?? sourcePath}`, {\n ...source,\n scopeId,\n sourcePath,\n });\n }\n\n return [...unique.values()];\n};\n\nconst prepareTestRunReportSources = (\n sources: readonly TestRunReportSource[],\n loadedDumps?: ReadonlyMap<string, string | Error>,\n): {\n sources: PreparedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n capturedMetrics: TestRunReportMetrics;\n executionMetrics: Record<string, TestRunReportMetrics>;\n diagnostics: import('./test-run-report').TestRunReportDiagnostic[];\n} => {\n const prepared: PreparedTestRunReportSource[] = [];\n const diagnostics: import('./test-run-report').TestRunReportDiagnostic[] = [];\n const ownerByScopedExecution = new Map<string, string>();\n const preparedByPath = new Map<string, PreparedTestRunReportSource>();\n\n for (const [sourceIndex, source] of uniqueTestRunReportSources(\n sources,\n ).entries()) {\n let baseDump: ReportActionDump;\n let executions: IExecutionDump[];\n try {\n if (source.dumpPath) {\n const contents =\n loadedDumps?.get(source.dumpPath) ??\n readFileSync(source.dumpPath, 'utf8');\n if (contents instanceof Error) throw contents;\n baseDump = ReportActionDump.fromSerializedString(contents);\n executions = baseDump.executions;\n } else {\n if (!existsSync(source.sourcePath))\n throw new Error(`Agent report does not exist: ${source.sourcePath}`);\n const sourceVersion = peekReportSdkVersion(source.sourcePath);\n const currentVersion = getVersion();\n if (\n sourceVersion &&\n currentVersion &&\n sourceVersion !== currentVersion &&\n !warnedMismatchedVersions.has(sourceVersion)\n ) {\n warnedMismatchedVersions.add(sourceVersion);\n logMsg(\n `[@midscene/core] TestRunReportAssembler version mismatch: source report was written by @midscene/core@${sourceVersion} but the assembler is @midscene/core@${currentVersion}. Align the workspace package versions before generating the Midscene Test report.`,\n );\n }\n ({ baseDump, executions } = collectDedupedExecutions(\n source.sourcePath,\n ));\n }\n } catch (error) {\n diagnostics.push({\n level: 'warning',\n code: 'agent-source-unavailable',\n scopeId: source.scopeId,\n sourcePaths: [source.sourcePath],\n message: `Agent source could not be collected: ${String(error)}`,\n });\n continue;\n }\n // A borrowed Agent can serve several sequential test scopes. Reuse its\n // report group and metrics while indexing each scope's execution links.\n const sourceKey = source.sourcePath;\n const imageOrigins = new Map<string, string>();\n JSON.parse(baseDump.serialize(), (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n if (ref)\n imageOrigins.set(\n ref.id,\n ref.storage === 'file'\n ? (source.dumpPath ?? source.sourcePath)\n : source.sourcePath,\n );\n return value;\n });\n const shared = preparedByPath.get(sourceKey);\n if (shared) {\n for (const [id, origin] of imageOrigins)\n shared.imageOrigins.set(id, origin);\n if (source.dumpPath) {\n shared.dump.executions = dedupeExecutionsKeepLatest([\n ...shared.dump.executions,\n ...baseDump.executions,\n ]);\n }\n const scoped = prepared.find(\n (item) =>\n item.reportId === shared.reportId && item.scopeId === source.scopeId,\n );\n const executionIds = [\n ...new Set([\n ...(scoped?.executionIds ?? []),\n ...executions.flatMap((execution) =>\n execution.id ? [execution.id] : [],\n ),\n ]),\n ];\n for (const executionId of executionIds) {\n const key = `${source.scopeId}\\u0000${executionId}`;\n const owner = ownerByScopedExecution.get(key);\n if (owner && owner !== shared.reportId)\n throw new Error(\n `Agent execution ${executionId} is present in multiple reports for test scope ${source.scopeId}: ${owner}, ${shared.reportId}`,\n );\n ownerByScopedExecution.set(key, shared.reportId);\n }\n if (scoped) {\n scoped.executionIds = executionIds;\n const partial = [scoped.collection, source.collection].find(\n (collection) => collection?.status === 'partial',\n );\n scoped.collection = partial ?? source.collection ?? scoped.collection;\n } else\n prepared.push({\n ...shared,\n scopeId: source.scopeId,\n executionIds,\n collection: source.collection,\n });\n continue;\n }\n const reportId = `runner-report-${sourceIndex + 1}`;\n const executionIds: string[] = [];\n for (const execution of executions) {\n if (!execution.id) continue;\n const scopedExecution = `${source.scopeId}\\u0000${execution.id}`;\n const previousOwner = ownerByScopedExecution.get(scopedExecution);\n if (previousOwner && previousOwner !== reportId) {\n throw new Error(\n `Agent execution ${execution.id} is present in multiple reports for test scope ${source.scopeId}: ${previousOwner}, ${reportId}`,\n );\n }\n ownerByScopedExecution.set(scopedExecution, reportId);\n executionIds.push(execution.id);\n }\n\n const dump = 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 const preparedSource = {\n reportId,\n scopeId: source.scopeId,\n sourcePath: source.sourcePath,\n executionIds,\n collection: source.collection,\n dump,\n imageOrigins,\n ...(source.dumpPath ? { dumpPath: source.dumpPath } : {}),\n };\n prepared.push(preparedSource);\n preparedByPath.set(sourceKey, preparedSource);\n }\n\n // A legacy invocation can preserve a snapshot of an Agent also used by\n // native scopes. Count stable executions once across both paths.\n const executions = dedupeExecutionsKeepLatest(\n [...preparedByPath.values()].flatMap((source) => source.dump.executions),\n );\n const metrics = collectTestRunReportMetrics({ executions });\n const executionMetrics = Object.fromEntries(\n executions.flatMap((execution) =>\n execution.id\n ? [\n [\n execution.id,\n collectTestRunReportMetrics({ executions: [execution] }),\n ],\n ]\n : [],\n ),\n );\n return {\n sources: prepared,\n diagnostics,\n capturedMetrics: metrics,\n executionMetrics,\n metrics: {\n ...metrics,\n collection: {\n ...metrics.collection!,\n fields: Object.fromEntries(\n Object.entries(metrics.collection!.fields!).map(([field, status]) => [\n field,\n diagnostics.length\n ? prepared.length === 0\n ? 'unknown'\n : status === 'complete'\n ? 'partial'\n : status\n : status,\n ]),\n ) as NonNullable<\n NonNullable<TestRunReportMetrics['collection']>['fields']\n >,\n status: diagnostics.length\n ? prepared.length\n ? 'partial'\n : 'unknown'\n : metrics.collection!.status,\n expectedExecutionCount: 0,\n capturedExecutionCount: 0,\n reasons: [\n ...metrics.collection!.reasons,\n ...diagnostics.map((diagnostic) => diagnostic.message),\n ],\n },\n },\n };\n};\n\n/**\n * Build a self-contained Midscene Test report while preserving the existing\n * Agent dump format consumed by the Report App.\n *\n * Test hierarchy and status live in `midscene_test_run_dump`; Agent reports\n * remain independent `midscene_web_dump` groups addressed by `data-report-id`.\n */\nexport class TestRunReportAssembler {\n /** Runtime publisher: current Agent snapshots avoid parsing HTML or copying unused images. */\n public async assembleAsync(\n options: AssembleTestRunReportOptions,\n ): Promise<string> {\n validateTestRunReportFileName(options.reportFileName);\n const loaded = new Map<string, string | Error>();\n await Promise.all(\n options.sources.map(async (source) => {\n if (source.dumpPath) {\n try {\n loaded.set(\n source.dumpPath,\n await fsAsync.readFile(source.dumpPath, 'utf8'),\n );\n } catch (error) {\n loaded.set(\n source.dumpPath,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }),\n );\n const prepared = prepareTestRunReportSources(options.sources, loaded);\n const dump = options.buildRunnerDump({\n sources: prepared.sources,\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n });\n if (dump.kind !== 'test-runner' || dump.schemaVersion !== 1)\n throw new Error('Invalid Runner report dump');\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const outputDir = path.resolve(options.outputDir);\n await fsAsync.mkdir(outputDir, { recursive: true });\n const artifact = path.join(outputDir, options.reportFileName);\n const finalPath = directoryMode\n ? path.join(artifact, 'index.html')\n : `${artifact}.html`;\n if (!options.overwrite && existsSync(directoryMode ? artifact : finalPath))\n throw new Error(`Report artifact already exists: ${artifact}`);\n const temporary = await fsAsync.mkdtemp(\n path.join(outputDir, '.test-run-report-'),\n );\n const htmlPath = path.join(temporary, 'index.html');\n try {\n const writer = new ReportArtifactWriter(\n htmlPath,\n directoryMode,\n 'test',\n dump.runId,\n );\n await writer.initialize();\n const reports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n reports.push(source.dump);\n await writer.addGroup(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n await writer.finish(dump, mergedAgentReportComment(reports));\n await publishReportArtifact(\n directoryMode ? temporary : htmlPath,\n directoryMode ? artifact : finalPath,\n directoryMode,\n options.overwrite,\n );\n return finalPath;\n } finally {\n await fsAsync.rm(temporary, { recursive: true, force: true });\n }\n }\n\n public assemble(options: AssembleTestRunReportOptions): string {\n validateTestRunReportFileName(options.reportFileName);\n const outputDir = path.resolve(options.outputDir);\n mkdirSync(outputDir, { recursive: true });\n\n const prepared = prepareTestRunReportSources(options.sources);\n const index: TestRunReportSourceIndex = {\n sources: prepared.sources.map(\n ({ reportId, scopeId, sourcePath, executionIds, collection }) => ({\n reportId,\n scopeId,\n sourcePath,\n executionIds,\n collection,\n }),\n ),\n metrics: prepared.metrics,\n capturedMetrics: prepared.capturedMetrics,\n executionMetrics: prepared.executionMetrics,\n diagnostics: prepared.diagnostics,\n };\n const runnerDump = options.buildRunnerDump(index);\n if (runnerDump.schemaVersion !== 1 || runnerDump.kind !== 'test-runner') {\n throw new Error(\n 'buildRunnerDump must return a Midscene Test report dump with schemaVersion 1 and kind \"test-runner\".',\n );\n }\n // Validate serializability before creating any output artifact.\n JSON.stringify(runnerDump);\n\n const directoryMode = options.outputFormat === 'html-and-external-assets';\n const finalArtifactPath = path.join(outputDir, options.reportFileName);\n const directoryReportPath = path.join(finalArtifactPath, 'index.html');\n const standaloneReportPath = `${finalArtifactPath}.html`;\n const finalReportPath = directoryMode\n ? directoryReportPath\n : standaloneReportPath;\n const existingArtifacts = [\n directoryMode ? finalArtifactPath : standaloneReportPath,\n ].filter(existsSync);\n if (existingArtifacts.length > 0 && !options.overwrite) {\n throw new Error(\n `Report artifact already exists: ${existingArtifacts[0]}\\nSet overwrite to true to overwrite this report.`,\n );\n }\n\n const temporaryRoot = mkdtempSync(\n path.join(outputDir, '.test-run-report-'),\n );\n const temporaryArtifactPath = path.join(\n temporaryRoot,\n options.reportFileName,\n );\n const temporaryReportPath = directoryMode\n ? path.join(temporaryArtifactPath, 'index.html')\n : `${temporaryArtifactPath}.html`;\n\n try {\n if (directoryMode) {\n mkdirSync(temporaryArtifactPath, { recursive: true });\n }\n\n const writer = new ReportArtifactWriter(\n temporaryReportPath,\n directoryMode,\n 'test',\n runnerDump.runId,\n );\n writer.initializeSync();\n const agentReports: ReportActionDump[] = [];\n for (const source of new Map(\n prepared.sources.map((source) => [source.reportId, source]),\n ).values()) {\n agentReports.push(source.dump);\n writer.addGroupSync(\n source.reportId,\n source.dump.serialize(),\n source.dumpPath ?? source.sourcePath,\n {\n 'data-report-id': source.reportId,\n 'data-runner-scope-id': source.scopeId,\n },\n source.imageOrigins,\n );\n }\n writer.finishSync(runnerDump, mergedAgentReportComment(agentReports));\n\n publishReportArtifactSync(\n directoryMode ? temporaryArtifactPath : temporaryReportPath,\n directoryMode ? finalArtifactPath : standaloneReportPath,\n directoryMode,\n options.overwrite,\n );\n rmSync(temporaryRoot, { recursive: true, force: true });\n return finalReportPath;\n } catch (error) {\n rmSync(temporaryRoot, { recursive: true, force: true });\n throw error;\n }\n }\n}\n\nexport interface SplitReportHtmlOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport interface SplitReportHtmlResult {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n}\n\nexport interface CollectedReportExecutions {\n baseDump: ReportActionDump;\n executions: IExecutionDump[];\n}\n\n/**\n * Collect executions from a report HTML, deduplicating by stable id while\n * keeping only the latest occurrence. Old-format executions without id are\n * always preserved.\n */\nexport function collectDedupedExecutions(\n htmlPath: string,\n): CollectedReportExecutions {\n let baseDump: ReportActionDump | null = null;\n let executionSerial = 0;\n const latestSerialByExecutionId = new Map<string, number>();\n\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (execution.id) {\n latestSerialByExecutionId.set(execution.id, executionSerial);\n }\n }\n return false;\n });\n\n const executions: IExecutionDump[] = [];\n executionSerial = 0;\n streamReportDumpScripts(htmlPath, (dumpScript) => {\n if (!dumpScript.openTag.includes('data-group-id')) {\n return false;\n }\n\n const groupedDump = ReportActionDump.fromSerializedString(\n antiEscapeScriptTag(dumpScript.content),\n );\n if (!baseDump) {\n baseDump = groupedDump;\n }\n\n for (const execution of groupedDump.executions) {\n executionSerial += 1;\n if (\n execution.id &&\n latestSerialByExecutionId.get(execution.id) !== executionSerial\n ) {\n continue;\n }\n executions.push(execution);\n }\n\n return false;\n });\n\n if (!baseDump) {\n throw new Error(`No report dump scripts found in ${htmlPath}`);\n }\n\n return {\n baseDump,\n executions,\n };\n}\n\nfunction externalizeImagesInExecution(\n execution: IExecutionDump,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const visit = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const item of node) {\n visit(item);\n }\n return;\n }\n\n if (typeof node !== 'object' || node === null) return;\n\n const ref = normalizeStoredImageRef(node);\n if (ref) {\n const ext = imageRefFileExtension(ref);\n const fileName = `${ref.id}.${ext}`;\n const relativePath = `./screenshots/${fileName}`;\n const absolutePath = path.join(opts.screenshotsDir, fileName);\n\n if (!opts.writtenFiles.has(fileName)) {\n const resolved = resolveImageSource(ref, {\n reportPath: opts.htmlPath,\n });\n if (resolved.type === 'data-uri') {\n const { rawBase64 } = parseBase64ImageDataUrl(resolved.dataUri);\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n } else {\n copyFileSync(resolved.filePath, absolutePath);\n }\n opts.writtenFiles.add(fileName);\n }\n\n ref.storage = 'file';\n ref.path = relativePath;\n return;\n }\n\n for (const value of Object.values(node)) {\n visit(value);\n }\n };\n\n visit(execution);\n}\n\n/**\n * Reverse parse a Midscene report HTML into per-execution JSON files and\n * externalized screenshots.\n */\nexport function splitReportHtmlByExecution(\n options: SplitReportHtmlOptions,\n): SplitReportHtmlResult {\n const { htmlPath, outputDir } = options;\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const executionJsonFiles: string[] = [];\n const writtenScreenshotFiles = new Set<string>();\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n let fileIndex = 0;\n for (const execution of executions) {\n fileIndex += 1;\n externalizeImagesInExecution(execution, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshotFiles,\n });\n const singleExecutionDump = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions: [execution],\n });\n\n const jsonFilePath = path.join(outputDir, `${fileIndex}.execution.json`);\n writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');\n executionJsonFiles.push(jsonFilePath);\n }\n\n return {\n executionJsonFiles,\n screenshotFiles: Array.from(writtenScreenshotFiles)\n .sort()\n .map((fileName) => path.join(screenshotsDir, fileName)),\n };\n}\n"],"names":["exportReportArtifact","options","validateTestRunReportFileName","input","path","output","external","artifact","destination","Error","existsSync","manifest","readReportArtifactManifest","runner","readReportRunnerDump","groups","Map","source","readReportDumpScripts","attributes","Object","key","value","decodeURIComponent","id","dump","ReportActionDump","antiEscapeScriptTag","previous","dedupeExecutionsKeepLatest","hint","kind","fsAsync","temporary","entry","writer","ReportArtifactWriter","group","mergedAgentReportComment","publishReportArtifact","screenshotModeAttrRegExp","RegExp","DATA_SCREENSHOT_MODE_ATTR","readDeclaredScreenshotMode","reportFilePath","mode","streamDumpScriptsSync","openTag","match","isDirectoryModeReport","declared","isDirectoryBasedReport","removeReportArtifact","rmSync","unlinkSync","executions","noIdCounter","deduped","exec","Array","peekReportSdkVersion","streamReportDumpScripts","content","extractLastDumpScriptSync","undefined","warnedMismatchedVersions","Set","tryParseAgentReportDump","dumpString","trimmed","reports","generateAgentReportComment","deviceTypes","report","Boolean","mergedReport","getVersion","ReportMergingTool","groupName","groupDescription","reportInfo","sourceVersion","currentVersion","logMsg","contents","unescaped","c","base","allExecutions","i","other","reportFileName","opts","rmOriginalReports","overwrite","outputDir","outputFormat","runnerDumps","info","hasRunnerDump","name","getReportFileName","targetDir","getMidsceneRunSubDir","mkdirSync","outputFilePath","mkdtempSync","agentReports","runnerSources","groupIds","index","serialized","groupId","dumps","originalId","uuid","agentReport","agentReportTiming","metrics","collectTestRunReportMetrics","executionMetrics","execution","mergeTestRunReportDumps","publishReportArtifactSync","sourcePath","durationMs","timing","collectReportSummary","executionTimes","Number","started","Math","ended","Date","fileName","uniqueTestRunReportSources","sources","unique","scopeId","prepareTestRunReportSources","loadedDumps","prepared","diagnostics","ownerByScopedExecution","preparedByPath","sourceIndex","baseDump","readFileSync","collectDedupedExecutions","error","String","sourceKey","imageOrigins","JSON","_key","ref","normalizeStoredImageRef","shared","origin","scoped","item","executionIds","executionId","owner","partial","collection","reportId","scopedExecution","previousOwner","preparedSource","field","status","diagnostic","TestRunReportAssembler","loaded","Promise","directoryMode","finalPath","htmlPath","runnerDump","finalArtifactPath","directoryReportPath","standaloneReportPath","finalReportPath","existingArtifacts","temporaryRoot","temporaryArtifactPath","temporaryReportPath","executionSerial","latestSerialByExecutionId","dumpScript","groupedDump","externalizeImagesInExecution","visit","node","ext","imageRefFileExtension","relativePath","absolutePath","resolved","resolveImageSource","rawBase64","parseBase64ImageDataUrl","writeFileSync","Buffer","copyFileSync","splitReportHtmlByExecution","screenshotsDir","executionJsonFiles","writtenScreenshotFiles","fileIndex","singleExecutionDump","jsonFilePath"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["import type { ReportModelUsageSummary } from './report-stats';\n\nexport const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n /** Actual models observed in usage records, independent of configured defaults. */\n models?: ReportModelUsageSummary[];\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n > &\n Partial<\n Record<\n 'promptTokens' | 'completionTokens',\n 'complete' | 'partial' | 'unknown'\n >\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying the Midscene Agent report. */\n sourcePath: string;\n /** Current Agent dump JSON; when present, HTML is not parsed for execution data. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** Final delivery format; defaults to a self-contained, offline-readable HTML. */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAEO,MAAMA,8BAA8B"}
1
+ {"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["import type { ReportModelUsageSummary } from './report-stats';\n\nexport const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n /** Actual models observed in usage records, independent of configured defaults. */\n models?: ReportModelUsageSummary[];\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n > &\n Partial<\n Record<\n 'promptTokens' | 'completionTokens',\n 'complete' | 'partial' | 'unknown'\n >\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying structured Agent records or an external Agent HTML report. */\n sourcePath: string;\n /** Immutable execution snapshot JSON; when present, execution data is read from it. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** Final delivery format; defaults to a self-contained, offline-readable HTML. */\n outputFormat?: 'single-html' | 'html-and-external-assets';\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAEO,MAAMA,8BAA8B"}
package/dist/es/utils.mjs CHANGED
@@ -184,7 +184,7 @@ function stringifyDumpData(data, indents) {
184
184
  return JSON.stringify(data, replacerForPageObject, indents);
185
185
  }
186
186
  function getVersion() {
187
- return "1.17.4";
187
+ return "1.17.5";
188
188
  }
189
189
  function debugLog(...message) {
190
190
  const debugMode = process.env[MIDSCENE_DEBUG_MODE];
@@ -145,7 +145,7 @@ class ScriptPlayer {
145
145
  freeFn = [
146
146
  ...newFreeFn || []
147
147
  ];
148
- agent._prepareForTestRunner?.();
148
+ await agent._prepareForTestRunner?.();
149
149
  signal = this.abortSignal ?? currentYamlSignal(agent);
150
150
  signal?.throwIfAborted();
151
151
  this.actionSpace = await agent.getActionSpace();
@@ -1 +1 @@
1
- {"version":3,"file":"yaml/player.mjs","sources":["../../../src/yaml/player.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { isActionReadinessError } from '@/agent/action-readiness';\nimport type { Agent } from '@/agent/agent';\nimport type {\n DeviceAction,\n FreeFn,\n MidsceneYamlScript,\n MidsceneYamlScriptEnv,\n ScriptPlayerStatusValue,\n ScriptPlayerTaskStatus,\n} from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, ifInWorker, uuid } from '@midscene/shared/utils';\nimport type { WorkflowDocumentExecutionResult } from '../test-runner';\nimport {\n WorkflowExecutionFailure,\n WorkflowPublicationError,\n} from '../test-runner/engine/execution-failure';\nimport { cleanupResources } from '../test-runner/engine/resource-operations';\nimport { runFreeFnCleanup } from './cleanup';\nimport {\n YamlExecutionOwnershipError,\n currentYamlSignal,\n enterYamlExecution,\n hasActiveYamlExecution,\n runInYamlExecutionContext,\n} from './execution-session';\nimport { createLegacyYamlExecutionRecord } from './legacy-yaml-execution-record';\nimport { publishLegacyYamlReport } from './legacy-yaml-report';\nimport {\n createLegacyYamlRuntime,\n legacyYamlOutcomeError,\n} from './legacy-yaml-runtime';\nimport {\n getLegacyYamlPlayerState,\n initializeLegacyYamlPlayerState,\n} from './player-state';\nimport { collectLegacyYamlDocument } from './test-runner-compat';\nimport { resolveWebTarget, resolveYamlOutputConfig } from './utils';\n\nconst debug = getDebug('yaml-player');\n\n/**\n * Compatibility facade for the legacy YAML API.\n *\n * Execution is delegated to the shared Runner kernel through\n * createLegacyYamlRuntime(). This class keeps the mutable status, result,\n * progress, output, and setup/cleanup behavior exposed by the old API.\n */\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\n public currentTaskIndex?: number;\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\n public status: ScriptPlayerStatusValue = 'init';\n public reportFile?: string | null;\n public result: Record<string, any>;\n private unnamedResultIndex = 0;\n private publicationErrors: WorkflowPublicationError[] = [];\n public output?: string | null;\n public unstableLogContent?: string | null;\n public errorInSetup?: Error;\n private interfaceAgent: Agent | null = null;\n public agentStatusTip?: string;\n public target?: MidsceneYamlScriptEnv;\n private actionSpace: DeviceAction[] = [];\n private scriptPath?: string;\n\n constructor(\n private script: MidsceneYamlScript,\n private setupAgent: (platform: T) => Promise<{\n agent: Agent;\n freeFn: FreeFn[];\n }>,\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\n scriptPath?: string,\n private readonly abortSignal?: AbortSignal,\n ) {\n this.scriptPath = scriptPath;\n // Collection validates syntax but defers old task/Step errors until execution.\n collectLegacyYamlDocument(script, scriptPath);\n initializeLegacyYamlPlayerState(this);\n this.result = {};\n\n this.target =\n resolveWebTarget(script)?.target ||\n script.android ||\n script.ios ||\n script.harmony ||\n script.computer ||\n script.config;\n const outputConfig = resolveYamlOutputConfig(script);\n\n if (ifInBrowser || ifInWorker) {\n this.output = undefined;\n debug('output is undefined in browser or worker');\n } else if (outputConfig.output) {\n this.output = resolve(process.cwd(), outputConfig.output);\n debug('setting output by config.output', this.output);\n } else {\n const scriptName = this.scriptPath\n ? basename(this.scriptPath, '.yaml').replace(/\\.(ya?ml)$/i, '')\n : 'script';\n this.output = join(\n getMidsceneRunSubDir('output'),\n `${scriptName}-${uuid()}.json`,\n );\n debug('setting output by script path', this.output);\n }\n\n if (ifInBrowser || ifInWorker) {\n this.unstableLogContent = undefined;\n } else if (typeof outputConfig.unstableLogContent === 'string') {\n this.unstableLogContent = resolve(\n process.cwd(),\n outputConfig.unstableLogContent,\n );\n } else if (outputConfig.unstableLogContent === true) {\n this.unstableLogContent = join(\n getMidsceneRunSubDir('output'),\n 'unstableLogContent.json',\n );\n }\n\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\n ...task,\n index: taskIndex,\n status: 'init',\n totalSteps: task.flow?.length || 0,\n }));\n }\n\n private setResult(key: string | undefined, value: any) {\n const keyToUse = key || this.unnamedResultIndex++;\n if (this.result[keyToUse]) {\n getDebug('yaml-player', { console: true })(\n `result key ${keyToUse} already exists, will overwrite`,\n );\n }\n this.result[keyToUse] = value;\n\n return this.flushResult();\n }\n\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\n this.status = status;\n this.errorInSetup = error;\n }\n\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\n const taskIndexToNotify =\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\n\n if (typeof taskIndexToNotify !== 'number') return;\n\n const taskStatus = this.taskStatusList[taskIndexToNotify];\n this.onTaskStatusChange?.(taskStatus);\n }\n\n private async setTaskStatus(\n index: number,\n statusValue: ScriptPlayerStatusValue,\n error?: Error,\n ) {\n this.taskStatusList[index].status = statusValue;\n if (error) this.taskStatusList[index].error = error;\n this.notifyCurrentTaskStatusChange(index);\n }\n\n private setTaskIndex(taskIndex: number) {\n this.currentTaskIndex = taskIndex;\n }\n\n private async flushResult() {\n if (this.output) {\n const output = resolve(process.cwd(), this.output);\n await this.publish(output, async () => {\n const outputDir = dirname(output);\n await mkdir(outputDir, { recursive: true });\n await writeFile(\n output,\n JSON.stringify(this.result || {}, undefined, 2),\n );\n });\n }\n }\n\n private async flushUnstableLogContent() {\n if (this.unstableLogContent) {\n const content = this.interfaceAgent?._unstableLogContent();\n const filePath = resolve(process.cwd(), this.unstableLogContent);\n await this.publish(filePath, async () => {\n const outputDir = dirname(filePath);\n await mkdir(outputDir, { recursive: true });\n await writeFile(filePath, JSON.stringify(content, null, 2));\n });\n }\n }\n\n private async publish(path: string, write: () => Promise<void>) {\n try {\n await write();\n } catch (cause) {\n const error = new WorkflowPublicationError('write-result', path, cause);\n this.publicationErrors.push(error);\n throw error;\n }\n }\n\n private createRuntime(agent: Agent) {\n return createLegacyYamlRuntime({\n agent,\n actionSpace: this.actionSpace,\n sourcePath: this.scriptPath,\n setResult: (key, value) => this.setResult(key, value),\n });\n }\n\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: Agent) {\n const { flow } = taskStatus;\n assert(flow, 'missing flow in task');\n const statusIndex = this.taskStatusList.indexOf(taskStatus);\n const declaredIndex = (\n taskStatus as ScriptPlayerTaskStatus & {\n index?: unknown;\n }\n ).index;\n const taskIndex =\n statusIndex >= 0\n ? statusIndex\n : typeof declaredIndex === 'number' && Number.isInteger(declaredIndex)\n ? declaredIndex\n : 0;\n\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = await this.createRuntime(agent).runTask(\n taskStatus,\n taskIndex,\n {\n signal: this.abortSignal ?? currentYamlSignal(agent),\n onStepStart: (info) => {\n if (info.scope === 'case' && info.case.phase === 'steps') {\n taskStatus.currentStep = info.case.stepIndex;\n }\n },\n },\n );\n\n const outcome = state.executionResult.cases[0];\n if (!outcome || outcome.status === 'not-run') {\n throw new Error(`Task \"${taskStatus.name}\" did not run.`);\n }\n if (outcome.status === 'failed') throw legacyYamlOutcomeError(outcome);\n\n this.reportFile = agent.reportFile;\n await this.flushUnstableLogContent();\n }\n\n async run() {\n return runInYamlExecutionContext(() => this.runInContext());\n }\n\n private async runInContext() {\n const startedAt = new Date();\n const runId = uuid();\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = undefined;\n state.executionRecord = undefined;\n this.publicationErrors = [];\n const { android, ios, harmony, computer } = this.script;\n const webEnv = resolveWebTarget(this.script)?.target;\n const androidEnv = android;\n const iosEnv = ios;\n const harmonyEnv = harmony;\n const computerEnv = computer;\n const platform =\n webEnv || androidEnv || iosEnv || harmonyEnv || computerEnv;\n\n this.setPlayerStatus('running');\n\n let agent: Agent | null = null;\n let freeFn: FreeFn[] = [];\n let setupError: unknown;\n let executionError: unknown;\n let cleanupError: unknown;\n let signal: AbortSignal | undefined;\n let session: ReturnType<typeof enterYamlExecution> | undefined;\n try {\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\n platform as T,\n );\n // Register ownership immediately: discovering actions can fail too.\n agent = newAgent;\n session = enterYamlExecution(agent);\n freeFn = [...(newFreeFn || [])];\n agent._prepareForTestRunner?.();\n signal = this.abortSignal ?? currentYamlSignal(agent);\n signal?.throwIfAborted();\n this.actionSpace = await agent.getActionSpace();\n const originalOnTaskStartTip = agent.onTaskStartTip;\n agent.onTaskStartTip = (tip) => {\n if (this.status === 'running') this.agentStatusTip = tip;\n originalOnTaskStartTip?.(tip);\n };\n freeFn.push({\n name: 'restore-agent-onTaskStartTip',\n fn: () => {\n if (agent) agent.onTaskStartTip = originalOnTaskStartTip;\n },\n });\n } catch (error) {\n // A rejected borrower must not close or publish into another run's Agent.\n if (error instanceof YamlExecutionOwnershipError) throw error;\n setupError = error;\n this.setPlayerStatus('error', error as Error);\n }\n this.interfaceAgent = agent;\n\n const document = collectLegacyYamlDocument(\n this.script,\n this.scriptPath ?? '<inline-yaml>',\n this.actionSpace,\n );\n const runtime =\n agent && !setupError ? this.createRuntime(agent) : undefined;\n try {\n if (runtime && agent) {\n state.executionResult = await runtime.runScript(this.script, {\n document,\n signal,\n createDocumentRunId: () => runId,\n onCaseStart: (collectedCase) => {\n const taskIndex = collectedCase.caseIndex;\n this.setTaskIndex(taskIndex);\n return this.setTaskStatus(taskIndex, 'running');\n },\n onStepStart: (info) => {\n if (info.scope !== 'case' || info.case.phase !== 'steps') return;\n const taskStatus = this.taskStatusList[info.case.caseIndex];\n if (taskStatus) taskStatus.currentStep = info.case.stepIndex;\n },\n onCaseOutcome: async (outcome) => {\n if (outcome.status === 'not-run') return;\n const taskIndex = outcome.caseIndex;\n const taskStatus = this.taskStatusList[taskIndex];\n assert(taskStatus, `missing YAML task at index ${taskIndex}`);\n\n this.reportFile = agent?.reportFile;\n if (outcome.status === 'success') {\n await this.setTaskStatus(taskIndex, 'done');\n await this.flushUnstableLogContent();\n return;\n }\n\n const error = legacyYamlOutcomeError(outcome);\n await this.setTaskStatus(taskIndex, 'error', error);\n },\n });\n const stoppedOnFailure = state.executionResult.cases.some(\n (outcome) =>\n outcome.status === 'failed' &&\n (!this.taskStatusList[outcome.caseIndex]?.continueOnError ||\n isActionReadinessError(legacyYamlOutcomeError(outcome))),\n );\n this.setPlayerStatus(\n stoppedOnFailure || signal?.aborted ? 'error' : 'done',\n );\n }\n } catch (error) {\n executionError = error;\n if (error instanceof WorkflowExecutionFailure)\n state.executionResult = error.result as WorkflowDocumentExecutionResult;\n this.setPlayerStatus('error', error as Error);\n }\n\n this.reportFile = agent?.reportFile;\n this.agentStatusTip = '';\n\n try {\n // A timeout settles the Runner before an uncooperative device action.\n // Never dispose its Agent while that action is still using it.\n if (session?.isRoot && agent) {\n await cleanupResources([agent], () => runFreeFnCleanup(freeFn), {\n owner: this,\n signal,\n onDeferredError: (error) =>\n getDebug('yaml-player', { console: true })(\n `Deferred YAML cleanup failed: ${String(error)}`,\n ),\n });\n } else await runFreeFnCleanup(freeFn);\n } catch (error) {\n cleanupError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? error) as Error,\n );\n }\n\n const endedAt = new Date();\n const cleanupErrors =\n cleanupError instanceof AggregateError\n ? cleanupError.errors\n : cleanupError === undefined\n ? []\n : [cleanupError];\n state.executionRecord = createLegacyYamlExecutionRecord({\n runId,\n attemptIndex: 0,\n script: this.script,\n document,\n startedAt,\n endedAt,\n execution: state.executionResult,\n setupError,\n executionError,\n cleanupErrors,\n aborted: signal?.aborted ?? false,\n publicationErrors: this.publicationErrors,\n outputs: this.result,\n reportFile: agent?.reportFile,\n children: session?.children,\n });\n let reportError: unknown;\n if (!session || session.isRoot) {\n try {\n const published = await publishLegacyYamlReport({\n agent,\n record: state.executionRecord,\n runId,\n script: this.script,\n scriptPath: this.scriptPath,\n fallbackReportFileName: state.fallbackReportFileName,\n });\n this.reportFile = published.reportFile;\n state.executionRecord = published.record;\n } catch (error) {\n reportError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? cleanupError ?? error) as Error,\n );\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError: error,\n });\n }\n }\n session?.finish(state.executionRecord);\n // destroy() defers report finalization while YAML owns the Agent. Publish\n // cleanup outcomes and source snapshots before releasing its executions.\n if (agent?.destroyed && !hasActiveYamlExecution(agent)) {\n try {\n await agent.finalizeReport();\n } catch (error) {\n reportError = reportError\n ? new AggregateError(\n [reportError, error],\n 'YAML report publication failed',\n )\n : error;\n this.setPlayerStatus('error', reportError as Error);\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError,\n });\n }\n }\n if (reportError) {\n const errors = [\n setupError,\n executionError,\n ...cleanupErrors,\n reportError,\n ].filter((error) => error !== undefined);\n throw errors.length === 1\n ? reportError\n : new AggregateError(\n errors,\n 'YAML execution/report finalization failed',\n );\n }\n if (executionError && cleanupError) {\n throw new AggregateError(\n [executionError, ...cleanupErrors],\n 'YAML execution and cleanup failed',\n );\n }\n if (executionError) throw executionError;\n if (cleanupError) throw cleanupError;\n }\n}\n"],"names":["debug","getDebug","ScriptPlayer","key","value","keyToUse","status","error","taskIndex","taskIndexToNotify","taskStatus","index","statusValue","output","resolve","process","outputDir","dirname","mkdir","writeFile","JSON","undefined","content","filePath","path","write","cause","WorkflowPublicationError","agent","createLegacyYamlRuntime","flow","assert","statusIndex","declaredIndex","Number","state","getLegacyYamlPlayerState","currentYamlSignal","info","outcome","Error","legacyYamlOutcomeError","runInYamlExecutionContext","startedAt","Date","runId","uuid","android","ios","harmony","computer","webEnv","resolveWebTarget","androidEnv","iosEnv","harmonyEnv","computerEnv","platform","freeFn","setupError","executionError","cleanupError","signal","session","newAgent","newFreeFn","enterYamlExecution","originalOnTaskStartTip","tip","YamlExecutionOwnershipError","document","collectLegacyYamlDocument","runtime","collectedCase","stoppedOnFailure","isActionReadinessError","WorkflowExecutionFailure","cleanupResources","runFreeFnCleanup","String","endedAt","cleanupErrors","AggregateError","createLegacyYamlExecutionRecord","reportError","published","publishLegacyYamlReport","Object","hasActiveYamlExecution","errors","script","setupAgent","onTaskStatusChange","scriptPath","abortSignal","initializeLegacyYamlPlayerState","outputConfig","resolveYamlOutputConfig","ifInBrowser","ifInWorker","scriptName","basename","join","getMidsceneRunSubDir","task"],"mappings":"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"}
1
+ {"version":3,"file":"yaml/player.mjs","sources":["../../../src/yaml/player.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { isActionReadinessError } from '@/agent/action-readiness';\nimport type { Agent } from '@/agent/agent';\nimport type {\n DeviceAction,\n FreeFn,\n MidsceneYamlScript,\n MidsceneYamlScriptEnv,\n ScriptPlayerStatusValue,\n ScriptPlayerTaskStatus,\n} from '@/types';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, ifInWorker, uuid } from '@midscene/shared/utils';\nimport type { WorkflowDocumentExecutionResult } from '../test-runner';\nimport {\n WorkflowExecutionFailure,\n WorkflowPublicationError,\n} from '../test-runner/engine/execution-failure';\nimport { cleanupResources } from '../test-runner/engine/resource-operations';\nimport { runFreeFnCleanup } from './cleanup';\nimport {\n YamlExecutionOwnershipError,\n currentYamlSignal,\n enterYamlExecution,\n hasActiveYamlExecution,\n runInYamlExecutionContext,\n} from './execution-session';\nimport { createLegacyYamlExecutionRecord } from './legacy-yaml-execution-record';\nimport { publishLegacyYamlReport } from './legacy-yaml-report';\nimport {\n createLegacyYamlRuntime,\n legacyYamlOutcomeError,\n} from './legacy-yaml-runtime';\nimport {\n getLegacyYamlPlayerState,\n initializeLegacyYamlPlayerState,\n} from './player-state';\nimport { collectLegacyYamlDocument } from './test-runner-compat';\nimport { resolveWebTarget, resolveYamlOutputConfig } from './utils';\n\nconst debug = getDebug('yaml-player');\n\n/**\n * Compatibility facade for the legacy YAML API.\n *\n * Execution is delegated to the shared Runner kernel through\n * createLegacyYamlRuntime(). This class keeps the mutable status, result,\n * progress, output, and setup/cleanup behavior exposed by the old API.\n */\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\n public currentTaskIndex?: number;\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\n public status: ScriptPlayerStatusValue = 'init';\n public reportFile?: string | null;\n public result: Record<string, any>;\n private unnamedResultIndex = 0;\n private publicationErrors: WorkflowPublicationError[] = [];\n public output?: string | null;\n public unstableLogContent?: string | null;\n public errorInSetup?: Error;\n private interfaceAgent: Agent | null = null;\n public agentStatusTip?: string;\n public target?: MidsceneYamlScriptEnv;\n private actionSpace: DeviceAction[] = [];\n private scriptPath?: string;\n\n constructor(\n private script: MidsceneYamlScript,\n private setupAgent: (platform: T) => Promise<{\n agent: Agent;\n freeFn: FreeFn[];\n }>,\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\n scriptPath?: string,\n private readonly abortSignal?: AbortSignal,\n ) {\n this.scriptPath = scriptPath;\n // Collection validates syntax but defers old task/Step errors until execution.\n collectLegacyYamlDocument(script, scriptPath);\n initializeLegacyYamlPlayerState(this);\n this.result = {};\n\n this.target =\n resolveWebTarget(script)?.target ||\n script.android ||\n script.ios ||\n script.harmony ||\n script.computer ||\n script.config;\n const outputConfig = resolveYamlOutputConfig(script);\n\n if (ifInBrowser || ifInWorker) {\n this.output = undefined;\n debug('output is undefined in browser or worker');\n } else if (outputConfig.output) {\n this.output = resolve(process.cwd(), outputConfig.output);\n debug('setting output by config.output', this.output);\n } else {\n const scriptName = this.scriptPath\n ? basename(this.scriptPath, '.yaml').replace(/\\.(ya?ml)$/i, '')\n : 'script';\n this.output = join(\n getMidsceneRunSubDir('output'),\n `${scriptName}-${uuid()}.json`,\n );\n debug('setting output by script path', this.output);\n }\n\n if (ifInBrowser || ifInWorker) {\n this.unstableLogContent = undefined;\n } else if (typeof outputConfig.unstableLogContent === 'string') {\n this.unstableLogContent = resolve(\n process.cwd(),\n outputConfig.unstableLogContent,\n );\n } else if (outputConfig.unstableLogContent === true) {\n this.unstableLogContent = join(\n getMidsceneRunSubDir('output'),\n 'unstableLogContent.json',\n );\n }\n\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\n ...task,\n index: taskIndex,\n status: 'init',\n totalSteps: task.flow?.length || 0,\n }));\n }\n\n private setResult(key: string | undefined, value: any) {\n const keyToUse = key || this.unnamedResultIndex++;\n if (this.result[keyToUse]) {\n getDebug('yaml-player', { console: true })(\n `result key ${keyToUse} already exists, will overwrite`,\n );\n }\n this.result[keyToUse] = value;\n\n return this.flushResult();\n }\n\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\n this.status = status;\n this.errorInSetup = error;\n }\n\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\n const taskIndexToNotify =\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\n\n if (typeof taskIndexToNotify !== 'number') return;\n\n const taskStatus = this.taskStatusList[taskIndexToNotify];\n this.onTaskStatusChange?.(taskStatus);\n }\n\n private async setTaskStatus(\n index: number,\n statusValue: ScriptPlayerStatusValue,\n error?: Error,\n ) {\n this.taskStatusList[index].status = statusValue;\n if (error) this.taskStatusList[index].error = error;\n this.notifyCurrentTaskStatusChange(index);\n }\n\n private setTaskIndex(taskIndex: number) {\n this.currentTaskIndex = taskIndex;\n }\n\n private async flushResult() {\n if (this.output) {\n const output = resolve(process.cwd(), this.output);\n await this.publish(output, async () => {\n const outputDir = dirname(output);\n await mkdir(outputDir, { recursive: true });\n await writeFile(\n output,\n JSON.stringify(this.result || {}, undefined, 2),\n );\n });\n }\n }\n\n private async flushUnstableLogContent() {\n if (this.unstableLogContent) {\n const content = this.interfaceAgent?._unstableLogContent();\n const filePath = resolve(process.cwd(), this.unstableLogContent);\n await this.publish(filePath, async () => {\n const outputDir = dirname(filePath);\n await mkdir(outputDir, { recursive: true });\n await writeFile(filePath, JSON.stringify(content, null, 2));\n });\n }\n }\n\n private async publish(path: string, write: () => Promise<void>) {\n try {\n await write();\n } catch (cause) {\n const error = new WorkflowPublicationError('write-result', path, cause);\n this.publicationErrors.push(error);\n throw error;\n }\n }\n\n private createRuntime(agent: Agent) {\n return createLegacyYamlRuntime({\n agent,\n actionSpace: this.actionSpace,\n sourcePath: this.scriptPath,\n setResult: (key, value) => this.setResult(key, value),\n });\n }\n\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: Agent) {\n const { flow } = taskStatus;\n assert(flow, 'missing flow in task');\n const statusIndex = this.taskStatusList.indexOf(taskStatus);\n const declaredIndex = (\n taskStatus as ScriptPlayerTaskStatus & {\n index?: unknown;\n }\n ).index;\n const taskIndex =\n statusIndex >= 0\n ? statusIndex\n : typeof declaredIndex === 'number' && Number.isInteger(declaredIndex)\n ? declaredIndex\n : 0;\n\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = await this.createRuntime(agent).runTask(\n taskStatus,\n taskIndex,\n {\n signal: this.abortSignal ?? currentYamlSignal(agent),\n onStepStart: (info) => {\n if (info.scope === 'case' && info.case.phase === 'steps') {\n taskStatus.currentStep = info.case.stepIndex;\n }\n },\n },\n );\n\n const outcome = state.executionResult.cases[0];\n if (!outcome || outcome.status === 'not-run') {\n throw new Error(`Task \"${taskStatus.name}\" did not run.`);\n }\n if (outcome.status === 'failed') throw legacyYamlOutcomeError(outcome);\n\n this.reportFile = agent.reportFile;\n await this.flushUnstableLogContent();\n }\n\n async run() {\n return runInYamlExecutionContext(() => this.runInContext());\n }\n\n private async runInContext() {\n const startedAt = new Date();\n const runId = uuid();\n const state = getLegacyYamlPlayerState(this);\n state.executionResult = undefined;\n state.executionRecord = undefined;\n this.publicationErrors = [];\n const { android, ios, harmony, computer } = this.script;\n const webEnv = resolveWebTarget(this.script)?.target;\n const androidEnv = android;\n const iosEnv = ios;\n const harmonyEnv = harmony;\n const computerEnv = computer;\n const platform =\n webEnv || androidEnv || iosEnv || harmonyEnv || computerEnv;\n\n this.setPlayerStatus('running');\n\n let agent: Agent | null = null;\n let freeFn: FreeFn[] = [];\n let setupError: unknown;\n let executionError: unknown;\n let cleanupError: unknown;\n let signal: AbortSignal | undefined;\n let session: ReturnType<typeof enterYamlExecution> | undefined;\n try {\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\n platform as T,\n );\n // Register ownership immediately: discovering actions can fail too.\n agent = newAgent;\n session = enterYamlExecution(agent);\n freeFn = [...(newFreeFn || [])];\n await agent._prepareForTestRunner?.();\n signal = this.abortSignal ?? currentYamlSignal(agent);\n signal?.throwIfAborted();\n this.actionSpace = await agent.getActionSpace();\n const originalOnTaskStartTip = agent.onTaskStartTip;\n agent.onTaskStartTip = (tip) => {\n if (this.status === 'running') this.agentStatusTip = tip;\n originalOnTaskStartTip?.(tip);\n };\n freeFn.push({\n name: 'restore-agent-onTaskStartTip',\n fn: () => {\n if (agent) agent.onTaskStartTip = originalOnTaskStartTip;\n },\n });\n } catch (error) {\n // A rejected borrower must not close or publish into another run's Agent.\n if (error instanceof YamlExecutionOwnershipError) throw error;\n setupError = error;\n this.setPlayerStatus('error', error as Error);\n }\n this.interfaceAgent = agent;\n\n const document = collectLegacyYamlDocument(\n this.script,\n this.scriptPath ?? '<inline-yaml>',\n this.actionSpace,\n );\n const runtime =\n agent && !setupError ? this.createRuntime(agent) : undefined;\n try {\n if (runtime && agent) {\n state.executionResult = await runtime.runScript(this.script, {\n document,\n signal,\n createDocumentRunId: () => runId,\n onCaseStart: (collectedCase) => {\n const taskIndex = collectedCase.caseIndex;\n this.setTaskIndex(taskIndex);\n return this.setTaskStatus(taskIndex, 'running');\n },\n onStepStart: (info) => {\n if (info.scope !== 'case' || info.case.phase !== 'steps') return;\n const taskStatus = this.taskStatusList[info.case.caseIndex];\n if (taskStatus) taskStatus.currentStep = info.case.stepIndex;\n },\n onCaseOutcome: async (outcome) => {\n if (outcome.status === 'not-run') return;\n const taskIndex = outcome.caseIndex;\n const taskStatus = this.taskStatusList[taskIndex];\n assert(taskStatus, `missing YAML task at index ${taskIndex}`);\n\n this.reportFile = agent?.reportFile;\n if (outcome.status === 'success') {\n await this.setTaskStatus(taskIndex, 'done');\n await this.flushUnstableLogContent();\n return;\n }\n\n const error = legacyYamlOutcomeError(outcome);\n await this.setTaskStatus(taskIndex, 'error', error);\n },\n });\n const stoppedOnFailure = state.executionResult.cases.some(\n (outcome) =>\n outcome.status === 'failed' &&\n (!this.taskStatusList[outcome.caseIndex]?.continueOnError ||\n isActionReadinessError(legacyYamlOutcomeError(outcome))),\n );\n this.setPlayerStatus(\n stoppedOnFailure || signal?.aborted ? 'error' : 'done',\n );\n }\n } catch (error) {\n executionError = error;\n if (error instanceof WorkflowExecutionFailure)\n state.executionResult = error.result as WorkflowDocumentExecutionResult;\n this.setPlayerStatus('error', error as Error);\n }\n\n this.reportFile = agent?.reportFile;\n this.agentStatusTip = '';\n\n try {\n // A timeout settles the Runner before an uncooperative device action.\n // Never dispose its Agent while that action is still using it.\n if (session?.isRoot && agent) {\n await cleanupResources([agent], () => runFreeFnCleanup(freeFn), {\n owner: this,\n signal,\n onDeferredError: (error) =>\n getDebug('yaml-player', { console: true })(\n `Deferred YAML cleanup failed: ${String(error)}`,\n ),\n });\n } else await runFreeFnCleanup(freeFn);\n } catch (error) {\n cleanupError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? error) as Error,\n );\n }\n\n const endedAt = new Date();\n const cleanupErrors =\n cleanupError instanceof AggregateError\n ? cleanupError.errors\n : cleanupError === undefined\n ? []\n : [cleanupError];\n state.executionRecord = createLegacyYamlExecutionRecord({\n runId,\n attemptIndex: 0,\n script: this.script,\n document,\n startedAt,\n endedAt,\n execution: state.executionResult,\n setupError,\n executionError,\n cleanupErrors,\n aborted: signal?.aborted ?? false,\n publicationErrors: this.publicationErrors,\n outputs: this.result,\n reportFile: agent?.reportFile,\n children: session?.children,\n });\n let reportError: unknown;\n if (!session || session.isRoot) {\n try {\n const published = await publishLegacyYamlReport({\n agent,\n record: state.executionRecord,\n runId,\n script: this.script,\n scriptPath: this.scriptPath,\n fallbackReportFileName: state.fallbackReportFileName,\n });\n this.reportFile = published.reportFile;\n state.executionRecord = published.record;\n } catch (error) {\n reportError = error;\n this.setPlayerStatus(\n 'error',\n (setupError ?? executionError ?? cleanupError ?? error) as Error,\n );\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError: error,\n });\n }\n }\n session?.finish(state.executionRecord);\n // destroy() defers report finalization while YAML owns the Agent. Publish\n // cleanup outcomes and source snapshots before releasing its executions.\n if (agent?.destroyed && !hasActiveYamlExecution(agent)) {\n try {\n await agent.finalizeReport();\n } catch (error) {\n reportError = reportError\n ? new AggregateError(\n [reportError, error],\n 'YAML report publication failed',\n )\n : error;\n this.setPlayerStatus('error', reportError as Error);\n state.executionRecord = Object.freeze({\n ...state.executionRecord,\n status: 'failed',\n reportError,\n });\n }\n }\n if (reportError) {\n const errors = [\n setupError,\n executionError,\n ...cleanupErrors,\n reportError,\n ].filter((error) => error !== undefined);\n throw errors.length === 1\n ? reportError\n : new AggregateError(\n errors,\n 'YAML execution/report finalization failed',\n );\n }\n if (executionError && cleanupError) {\n throw new AggregateError(\n [executionError, ...cleanupErrors],\n 'YAML execution and cleanup failed',\n );\n }\n if (executionError) throw executionError;\n if (cleanupError) throw cleanupError;\n }\n}\n"],"names":["debug","getDebug","ScriptPlayer","key","value","keyToUse","status","error","taskIndex","taskIndexToNotify","taskStatus","index","statusValue","output","resolve","process","outputDir","dirname","mkdir","writeFile","JSON","undefined","content","filePath","path","write","cause","WorkflowPublicationError","agent","createLegacyYamlRuntime","flow","assert","statusIndex","declaredIndex","Number","state","getLegacyYamlPlayerState","currentYamlSignal","info","outcome","Error","legacyYamlOutcomeError","runInYamlExecutionContext","startedAt","Date","runId","uuid","android","ios","harmony","computer","webEnv","resolveWebTarget","androidEnv","iosEnv","harmonyEnv","computerEnv","platform","freeFn","setupError","executionError","cleanupError","signal","session","newAgent","newFreeFn","enterYamlExecution","originalOnTaskStartTip","tip","YamlExecutionOwnershipError","document","collectLegacyYamlDocument","runtime","collectedCase","stoppedOnFailure","isActionReadinessError","WorkflowExecutionFailure","cleanupResources","runFreeFnCleanup","String","endedAt","cleanupErrors","AggregateError","createLegacyYamlExecutionRecord","reportError","published","publishLegacyYamlReport","Object","hasActiveYamlExecution","errors","script","setupAgent","onTaskStatusChange","scriptPath","abortSignal","initializeLegacyYamlPlayerState","outputConfig","resolveYamlOutputConfig","ifInBrowser","ifInWorker","scriptName","basename","join","getMidsceneRunSubDir","task"],"mappings":"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"}